def select_inputs(self): assert self.stage is None, "Incorrect stage" assert self.role == Role.SELLER, "Incorrect role" self.stage = Stage.INIT if self.is_bitcoin_swap(): # Generate a key that will co-sign the BTC multisig self.swap_cosign = SecretKey.random(self.secp) self.public_swap_cosign = self.swap_cosign.to_public_key(self.secp) # Inputs self.input_entries = self.wallet.select_outputs( self.grin_amount + tx_fee(1, 2, MILLI_GRIN_UNIT) + 1) self.inputs = [] for entry in self.input_entries: entry.mark_locked() input = self.wallet.entry_to_input(entry) self.inputs.append(input) self.fee_amount = tx_fee(len(self.input_entries), 2, MILLI_GRIN_UNIT) self.refund_fee_amount = tx_fee(1, 1, MILLI_GRIN_UNIT) self.input_amount = sum(x.value for x in self.input_entries) self.change_amount = self.input_amount - self.grin_amount - self.fee_amount # Change output self.change_child, self.change_entry = self.wallet.create_output( self.change_amount) self.change_entry.mark_locked() self.change_output = self.wallet.entry_to_output(self.change_entry) # Partial multisig output self.partial_child, self.partial_entry = self.wallet.create_output( self.grin_amount) self.partial_entry.mark_locked() self.partial_commit = self.wallet.commit_with_child_key( 0, self.partial_child) # Offset self.offset = BlindingFactor.from_secret_key( SecretKey.random(self.secp)) # Refund output refund_amount = self.grin_amount - self.refund_fee_amount self.refund_child, self.refund_entry = self.wallet.create_output( refund_amount) self.refund_output = self.wallet.entry_to_output(self.refund_entry) # Refund offset self.refund_offset = BlindingFactor.from_secret_key( SecretKey.random(self.secp)) # Nonces self.nonce = SecretKey.random(self.secp) self.public_nonce = self.nonce.to_public_key(self.secp) self.refund_nonce = SecretKey.random(self.secp) self.public_refund_nonce = self.refund_nonce.to_public_key(self.secp) self.wallet.save()
def from_dict(secp: Secp256k1, dct: dict): inputs = [] for input in dct['body']['inputs']: inputs.append(Input.from_dict(secp, input)) outputs = [] for output in dct['body']['outputs']: outputs.append(Output.from_dict(secp, output)) kernels = [] for kernel in dct['body']['kernels']: kernels.append(Kernel.from_dict(secp, kernel)) offset = BlindingFactor.from_bytearray(bytearray(dct['offset'])) return Transaction(inputs, outputs, kernels, offset)
def prepare_swap(self): assert self.role == Role.BUYER and self.stage == Stage.LOCK, "Incorrect stage" self.swap_nonce = SecretKey.random(self.secp) self.public_swap_nonce = self.swap_nonce.to_public_key(self.secp) self.swap_fee_amount = tx_fee(1, 1, MILLI_GRIN_UNIT) self.swap_lock_height = self.lock_height + 1 self.swap_child, self.swap_entry = self.wallet.create_output( self.grin_amount - self.swap_fee_amount) self.wallet.save() self.swap_offset = BlindingFactor.from_secret_key( SecretKey.random(self.secp)) self.swap_output = self.wallet.entry_to_output(self.swap_entry) self.fill_swap_signatures()
def receive(self, dct: dict): if self.stage is None: if self.role == Role.BUYER: dct['time_start'] = int(time()) self.wallet = Wallet.open(self.secp, dct['wallet']) # Add 'foreign_' prefix to some keys dct.update({ "foreign_partial_commit": dct['partial_commit'], "foreign_public_nonce": dct['public_nonce'], "foreign_public_refund_nonce": dct['public_refund_nonce'] }) dct.pop("partial_commit", None) dct.pop("public_nonce", None) dct.pop("public_refund_nonce", None) # Partial multisig output self.partial_child, self.partial_entry = self.wallet.create_output( dct['grin_amount']) self.partial_entry.mark_locked() self.nonce = SecretKey.random(self.secp) self.refund_nonce = SecretKey.random(self.secp) self.secret_lock = SecretKey.random(self.secp) self.public_lock = self.secret_lock.to_public_key(self.secp) if dct['swap_currency'] == "BTC": self.btc_lock_time = int(time() + 24 * 60 * 60) self.btc_refund_key = SecretKey.random(self.secp) self.public_btc_refund_key = self.btc_refund_key.to_public_key( self.secp) self.load(dct) if self.is_bitcoin_swap(): self.btc_lock_address = self.calculate_btc_lock_address() self.wallet.save() else: raise Exception("This stage doesn't expect an input file") elif self.stage == Stage.INIT: self.stage = Stage.SIGN self.foreign_t_1 = PublicKey.from_hex(self.secp, dct['t_1'].encode()) self.foreign_t_2 = PublicKey.from_hex(self.secp, dct['t_2'].encode()) if self.role == Role.SELLER: self.foreign_partial_commit = Commitment.from_hex( self.secp, dct['partial_commit'].encode()) self.foreign_public_nonce = PublicKey.from_hex( self.secp, dct['public_nonce'].encode()) self.foreign_public_refund_nonce = PublicKey.from_hex( self.secp, dct['public_refund_nonce'].encode()) self.public_lock = PublicKey.from_hex( self.secp, dct['public_lock'].encode()) if self.is_bitcoin_swap(): self.btc_lock_time = int(dct['btc_lock_time']) self.public_btc_refund_key = PublicKey.from_hex( self.secp, dct['public_btc_refund_key']) self.btc_lock_address = self.calculate_btc_lock_address() if self.is_ether_swap(): self.eth_address_lock = ethereum_address( self.secp, self.public_lock).decode() self.eth_contract_address = dct['eth_contract_address'] self.commit = self.secp.commit_sum([ self.foreign_partial_commit, self.wallet.commit(self.partial_entry) ], []) self.foreign_partial_signature = Signature.from_hex( dct['partial_signature'].encode()) self.foreign_partial_refund_signature = Signature.from_hex( dct['partial_refund_signature'].encode()) else: self.foreign_tau_x = SecretKey.from_hex( self.secp, dct['tau_x'].encode()) elif self.stage == Stage.SIGN: self.stage = Stage.LOCK if self.role == Role.SELLER: self.range_proof = RangeProof.from_hex( dct['range_proof'].encode()) else: self.tx_height = dct['tx_height'] self.foreign_public_swap_nonce = PublicKey.from_hex( self.secp, dct['public_swap_nonce'].encode()) elif self.stage == Stage.LOCK: self.stage = Stage.SWAP if self.role == Role.SELLER: self.foreign_public_swap_nonce = PublicKey.from_hex( self.secp, dct['public_swap_nonce'].encode()) self.swap_fee_amount = int(dct['swap_fee_amount']) self.swap_lock_height = int(dct['swap_lock_height']) self.swap_output = Output.from_dict(self.secp, dct['swap_output'], True) self.swap_offset = BlindingFactor.from_hex( dct['swap_offset'].encode()) self.foreign_partial_swap_adaptor = Signature.from_hex( dct['partial_swap_adaptor']) else: self.foreign_partial_swap_signature = Signature.from_hex( dct['partial_swap_signature']) elif self.stage == Stage.SWAP: self.stage = Stage.DONE if self.role == Role.SELLER: self.foreign_partial_swap_signature = Signature.from_hex( dct['partial_swap_signature']) else: raise Exception("Invalid stage")
def load(self, dct=None): seller = self.role == Role.SELLER buyer = not seller from_file = dct is None if from_file: f = open(absolute(self.swap_file), "r") dct = json.loads(f.read()) f.close() self.stage = Stage(dct['stage']) if self.wallet is None: self.wallet = Wallet.open(self.secp, dct['wallet']) self.time_start = int(dct['time_start']) self.grin_amount = int(dct['grin_amount']) self.swap_currency = dct['swap_currency'] self.swap_amount = int(dct['swap_amount']) if seller or self.is_ether_swap(): self.swap_receive_address = dct['swap_receive_address'] self.lock_height = int(dct['lock_height']) self.refund_lock_height = int(dct['refund_lock_height']) self.inputs = [ Input.from_dict(self.secp, x, True) for x in dct['inputs'] ] self.fee_amount = int(dct['fee_amount']) self.refund_fee_amount = int(dct['refund_fee_amount']) self.change_output = Output.from_dict(self.secp, dct['change_output'], True) if from_file: self.partial_entry = self.wallet.get_output(dct['partial_entry']) self.partial_child = self.wallet.derive_from_entry( self.partial_entry) self.partial_commit = self.wallet.commit_with_child_key( 0, self.partial_child) self.offset = BlindingFactor.from_hex(dct['offset'].encode()) self.refund_output = Output.from_dict(self.secp, dct['refund_output'], True) self.refund_offset = BlindingFactor.from_hex( dct['refund_offset'].encode()) if from_file: self.nonce = SecretKey.from_hex(self.secp, dct['nonce'].encode()) self.refund_nonce = SecretKey.from_hex( self.secp, dct['refund_nonce'].encode()) self.public_nonce = self.nonce.to_public_key(self.secp) self.public_refund_nonce = self.refund_nonce.to_public_key(self.secp) if seller: if self.is_bitcoin_swap(): self.swap_cosign = SecretKey.from_hex( self.secp, dct['swap_cosign'].encode()) self.input_entries = [ self.wallet.get_output(x) for x in dct['input_entries'] ] self.input_amount = sum(x.value for x in self.input_entries) self.change_amount = self.input_amount - self.grin_amount - self.fee_amount self.change_entry = self.wallet.get_output(dct['change_entry']) self.change_child = self.wallet.derive_from_entry( self.change_entry) self.refund_entry = self.wallet.get_output(dct['refund_entry']) self.refund_child = self.wallet.derive_from_entry( self.refund_entry) else: if from_file: self.secret_lock = SecretKey.from_hex( self.secp, dct['secret_lock'].encode()) if self.is_bitcoin_swap(): self.btc_refund_key = SecretKey.from_hex( self.secp, dct['btc_refund_key'].encode()) if self.is_bitcoin_swap(): self.public_swap_cosign = self.swap_cosign.to_public_key(self.secp) if seller else \ PublicKey.from_hex(self.secp, dct['public_swap_cosign'].encode()) if self.stage >= Stage.SIGN or buyer: self.foreign_partial_commit = Commitment.from_hex( self.secp, dct['foreign_partial_commit'].encode()) self.foreign_public_nonce = PublicKey.from_hex( self.secp, dct['foreign_public_nonce'].encode()) self.foreign_public_refund_nonce = PublicKey.from_hex( self.secp, dct['foreign_public_refund_nonce'].encode()) if from_file: self.public_lock = PublicKey.from_hex( self.secp, dct['public_lock'].encode()) if self.is_bitcoin_swap(): self.public_btc_refund_key = self.btc_refund_key.to_public_key(self.secp) if buyer else \ PublicKey.from_hex(self.secp, dct['public_btc_refund_key'].encode()) if self.is_ether_swap(): self.eth_address_lock = ethereum_address( self.secp, self.public_lock).decode() self.commit = self.secp.commit_sum([self.foreign_partial_commit, self.wallet.commit(self.partial_entry)], []) if not from_file else \ Commitment.from_hex(self.secp, dct['commit'].encode()) if self.stage >= Stage.SIGN or (buyer and from_file): self.public_excess = PublicKey.from_hex( self.secp, dct['public_excess'].encode()) self.public_refund_excess = PublicKey.from_hex( self.secp, dct['public_refund_excess'].encode()) if self.is_bitcoin_swap(): self.btc_lock_time = int(dct['btc_lock_time']) self.btc_lock_address = Address.from_base58check( dct['btc_lock_address'].encode()) if self.is_ether_swap(): self.eth_contract_address = dct['eth_contract_address'] self.partial_signature = Signature.from_hex( dct['partial_signature'].encode()) self.partial_refund_signature = Signature.from_hex( dct['partial_refund_signature'].encode()) self.t_1 = PublicKey.from_hex(self.secp, dct['t_1'].encode()) self.t_2 = PublicKey.from_hex(self.secp, dct['t_2'].encode()) if self.stage >= Stage.SIGN: self.foreign_t_1 = PublicKey.from_hex(self.secp, dct['foreign_t_1'].encode()) self.foreign_t_2 = PublicKey.from_hex(self.secp, dct['foreign_t_2'].encode()) if seller: self.tau_x = SecretKey.from_hex(self.secp, dct['tau_x'].encode()) self.foreign_partial_signature = Signature.from_hex( dct['foreign_partial_signature'].encode()) self.foreign_partial_refund_signature = Signature.from_hex( dct['foreign_partial_refund_signature'].encode()) if self.is_bitcoin_swap(): self.btc_output_points = [ OutputPoint.from_hex(x.encode()) for x in dct['btc_output_points'] ] else: self.foreign_tau_x = SecretKey.from_hex( self.secp, dct['foreign_tau_x'].encode()) if self.stage >= Stage.LOCK or (self.stage == Stage.SIGN and buyer): self.range_proof = RangeProof.from_hex(dct['range_proof'].encode()) if self.stage >= Stage.LOCK: self.tx_height = dct['tx_height'] self.swap_nonce = SecretKey.from_hex(self.secp, dct['swap_nonce'].encode()) self.public_swap_nonce = self.swap_nonce.to_public_key(self.secp) if buyer: self.swap_entry = self.wallet.get_output(dct['swap_entry']) self.swap_child = self.wallet.derive_from_entry( self.swap_entry) self.partial_swap_adaptor = Signature.from_hex( dct['partial_swap_adaptor'].encode()) if (buyer and self.stage >= Stage.LOCK) or (seller and self.stage >= Stage.SWAP): self.foreign_public_swap_nonce = PublicKey.from_hex( self.secp, dct['foreign_public_swap_nonce'].encode()) self.swap_fee_amount = int(dct['swap_fee_amount']) self.swap_lock_height = int(dct['swap_lock_height']) self.swap_output = Output.from_dict(self.secp, dct['swap_output'], True) self.swap_offset = BlindingFactor.from_hex( dct['swap_offset'].encode()) self.public_swap_excess = PublicKey.from_hex( self.secp, dct['public_swap_excess'].encode()) self.partial_swap_signature = Signature.from_hex( dct['partial_swap_signature'].encode()) if seller and self.stage >= Stage.SWAP: self.foreign_partial_swap_adaptor = Signature.from_hex( dct['foreign_partial_swap_adaptor'].encode())
def empty(secp: Secp256k1, features: int, fee: int, lock_height: int): kernel = Kernel(features, fee, lock_height, None, None) return Transaction([], [], [kernel], BlindingFactor.from_secret_key( SecretKey.random(secp)))