Пример #1
0
    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()
Пример #2
0
 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)
Пример #3
0
    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()
Пример #4
0
    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")
Пример #5
0
    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())
Пример #6
0
 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)))