def test_partial_sig():
    secp = Secp256k1(None, FLAG_ALL)
    excess = SecretKey.random(secp)
    public_excess = excess.to_public_key(secp)
    nonce = SecretKey.random(secp)
    public_key_sum = SecretKey.random(secp).to_public_key(secp)
    public_nonce_sum = SecretKey.random(secp).to_public_key(secp)
    fee = randint(1, 999999)
    lock_height = randint(1, 999999)
    sig = aggsig.calculate_partial(secp, excess, nonce, public_key_sum,
                                   public_nonce_sum, fee, lock_height)

    assert aggsig.verify_partial(secp, sig, public_excess, public_key_sum,
                                 public_nonce_sum, fee, lock_height)
    rnd_sig = Signature(bytearray(urandom(64)))
    assert not aggsig.verify_partial(secp, rnd_sig, public_excess,
                                     public_key_sum, public_nonce_sum, fee,
                                     lock_height)
    public_rnd = SecretKey.random(secp).to_public_key(secp)
    assert not aggsig.verify_partial(secp, sig, public_rnd, public_key_sum,
                                     public_nonce_sum, fee, lock_height)
    assert not aggsig.verify_partial(secp, sig, public_excess, public_rnd,
                                     public_nonce_sum, fee, lock_height)
    assert not aggsig.verify_partial(secp, sig, public_excess, public_key_sum,
                                     public_rnd, fee, lock_height)
    assert not aggsig.verify_partial(secp, sig, public_excess, public_key_sum,
                                     public_nonce_sum, 0, lock_height)
    assert not aggsig.verify_partial(secp, sig, public_excess, public_key_sum,
                                     public_nonce_sum, fee, 0)
Exemple #2
0
 def from_dict(secp: Secp256k1, dct: dict):
     return Kernel(
         dct['features']['bits'], dct['fee'], dct['lock_height'],
         None if sum(dct['excess']) == 0 else Commitment.from_bytearray(
             secp, bytearray(dct['excess'])),
         None if sum(dct['excess_sig']) == 0 else Signature.from_bytearray(
             secp, bytearray(dct['excess_sig']), True))
def add_single(secp: Secp256k1, signatures: List[Signature],
               public_nonce_sum: PublicKey) -> Signature:
    signature_out = ffi.new("char [64]")
    signature_ptrs = []
    for signature in signatures:
        signature_ptr = ffi.new("char []", bytes(signature.signature))
        signature_ptrs.append(signature_ptr)

    res = lib.secp256k1_aggsig_add_signatures_single(secp.ctx, signature_out,
                                                     signature_ptrs,
                                                     len(signatures),
                                                     public_nonce_sum.key)
    assert res, "Unable to add signatures"
    return Signature.from_bytearray(secp,
                                    bytearray(ffi.buffer(signature_out, 64)))
def sign_single(secp: Secp256k1, message: Message, secret_key: SecretKey,
                secret_nonce: Optional[SecretKey],
                public_nonce: Optional[PublicKey],
                public_key_sum: Optional[PublicKey],
                public_nonce_sum: Optional[PublicKey],
                extra_secret_key: Optional[SecretKey]) -> Signature:
    signature_out = ffi.new("char [64]")
    res = lib.secp256k1_aggsig_sign_single(
        secp.ctx, signature_out, bytes(message.message), bytes(secret_key.key),
        ffi.NULL if secret_nonce is None else bytes(secret_nonce.key),
        ffi.NULL if extra_secret_key is None else bytes(extra_secret_key.key),
        ffi.NULL if public_nonce is None else public_nonce.key,
        ffi.NULL if public_nonce_sum is None else public_nonce_sum.key,
        ffi.NULL if public_key_sum is None else public_key_sum.key,
        urandom(32))
    assert res, "Unable to sign message"
    return Signature.from_bytearray(secp,
                                    bytearray(ffi.buffer(signature_out, 64)))
def test_sig():
    # Test Grin-like signature scheme
    secp = Secp256k1(None, FLAG_ALL)
    nonce_a = SecretKey.random(secp)
    public_nonce_a = nonce_a.to_public_key(secp)
    nonce_b = SecretKey.random(secp)
    public_nonce_b = nonce_b.to_public_key(secp)
    public_nonce_sum = PublicKey.from_combination(
        secp, [public_nonce_a, public_nonce_b])
    excess_a = SecretKey.random(secp)
    public_excess_a = excess_a.to_public_key(secp)
    excess_b = SecretKey.random(secp)
    public_excess_b = excess_b.to_public_key(secp)
    public_excess_sum = PublicKey.from_combination(
        secp, [public_excess_a, public_excess_b])
    fee = randint(1, 999999)
    lock_height = randint(1, 999999)

    # Partial signature for A
    sig_a = aggsig.calculate_partial(secp, excess_a, nonce_a,
                                     public_excess_sum, public_nonce_sum, fee,
                                     lock_height)
    assert aggsig.verify_partial(secp, sig_a, public_excess_a,
                                 public_excess_sum, public_nonce_sum, fee,
                                 lock_height)

    # Partial signature for B
    sig_b = aggsig.calculate_partial(secp, excess_b, nonce_b,
                                     public_excess_sum, public_nonce_sum, fee,
                                     lock_height)
    assert aggsig.verify_partial(secp, sig_b, public_excess_b,
                                 public_excess_sum, public_nonce_sum, fee,
                                 lock_height)

    # Total signature
    sig = aggsig.add_partials(secp, [sig_a, sig_b], public_nonce_sum)
    assert aggsig.verify(secp, sig, public_excess_sum, fee, lock_height)
    rnd_sig = Signature(bytearray(urandom(64)))
    assert not aggsig.verify(secp, rnd_sig, public_excess_sum, fee,
                             lock_height)
    public_rnd = SecretKey.random(secp).to_public_key(secp)
    assert not aggsig.verify(secp, sig, public_rnd, fee, lock_height)
    assert not aggsig.verify(secp, sig, public_excess_sum, 0, lock_height)
    assert not aggsig.verify(secp, sig, public_excess_sum, fee, 0)
Exemple #6
0
def send(node_url: str):
    global secp, wallet, proof_builder

    now = int(time())

    send_amount = GRIN_UNIT
    lock_height = 1
    refund_lock_height = lock_height + 1440  # ~24 hours
    dest_url = "http://127.0.0.1:18185"
    fluff = True

    secp = Secp256k1(None, FLAG_ALL)
    wallet = Wallet.open(secp, "wallet_a")

    print("Preparing to create multisig with {}".format(dest_url))

    input_entries = wallet.select_outputs(send_amount +
                                          tx_fee(1, 2, MILLI_GRIN_UNIT))
    fee_amount = tx_fee(len(input_entries), 2, MILLI_GRIN_UNIT)
    input_amount = sum(x.value for x in input_entries)
    change_amount = input_amount - send_amount - fee_amount
    refund_fee_amount = tx_fee(1, 1, MILLI_GRIN_UNIT)

    print("Selected {} inputs".format(len(input_entries)))

    tx = Transaction.empty(secp, 0, fee_amount, lock_height)
    refund_tx = Transaction.empty(secp, 0, refund_fee_amount,
                                  refund_lock_height)

    blind_sum = BlindSum()

    # Inputs
    inputs = []
    for entry in input_entries:
        entry.mark_locked()
        blind_sum.sub_child_key(wallet.derive_from_entry(entry))
        input = wallet.entry_to_input(entry)
        tx.add_input(secp, input)
        inputs.append(input)

    # Change output
    change_child, change_entry = wallet.create_output(change_amount)
    blind_sum.add_child_key(change_child)
    change_output = wallet.entry_to_output(change_entry)
    tx.add_output(secp, change_output)

    # Multisig output
    partial_child, partial_entry = wallet.create_output(send_amount)
    partial_entry.mark_locked()
    blind_sum.add_child_key(partial_child)
    public_partial_commit = wallet.commit_with_child_key(0, partial_child)

    # Refund output
    refund_amount = send_amount - refund_fee_amount
    refund_child, refund_entry = wallet.create_output(refund_amount)
    refund_output = wallet.entry_to_output(refund_entry)
    refund_tx.add_output(secp, refund_output)

    # Offset
    blind_sum.sub_blinding_factor(tx.offset)

    # Excess
    excess = wallet.chain.blind_sum(blind_sum).to_secret_key(secp)
    public_excess = excess.to_public_key(secp)

    # Nonce
    nonce = SecretKey.random(secp)
    public_nonce = nonce.to_public_key(secp)

    # Refund nonce
    refund_nonce = SecretKey.random(secp)
    refund_public_nonce = refund_nonce.to_public_key(secp)

    dct = {
        "amount": send_amount,
        "fee": fee_amount,
        "refund_fee": refund_fee_amount,
        "lock_height": lock_height,
        "refund_lock_height": refund_lock_height,
        "public_partial_commit": public_partial_commit.to_hex(secp).decode(),
        "public_nonce": public_nonce.to_hex(secp).decode(),
        "refund_public_nonce": refund_public_nonce.to_hex(secp).decode()
    }

    f = open("logs/{}_multisig_1.json".format(now), "w")
    f.write(json.dumps(dct, indent=2))
    f.close()

    print("Sending to receiver..")

    req = urlopen(dest_url, json.dumps(dct).encode(), 60)
    dct2 = json.loads(req.read().decode())

    f = open("logs/{}_multisig_2.json".format(now), "w")
    f.write(json.dumps(dct2, indent=2))
    f.close()

    print("Received response, processing..")

    public_partial_commit_recv = Commitment.from_hex(
        secp, dct2['public_partial_commit'].encode())
    public_partial_recv = public_partial_commit_recv.to_public_key(secp)
    public_nonce_recv = PublicKey.from_hex(secp, dct2['public_nonce'].encode())
    public_excess_recv = public_partial_commit_recv.to_public_key(secp)
    partial_signature_recv = Signature.from_hex(
        dct2['partial_signature'].encode())
    refund_public_nonce_recv = PublicKey.from_hex(
        secp, dct2['refund_public_nonce'].encode())
    refund_public_excess_recv = PublicKey.from_hex(
        secp, dct2['refund_public_excess'].encode())
    refund_partial_signature_recv = Signature.from_hex(
        dct2['refund_partial_signature'].encode())

    # Commitment
    commit = secp.commit_sum(
        [public_partial_commit_recv,
         wallet.commit(partial_entry)], [])
    print("Total commit: {}".format(commit))

    # Nonce sums
    public_nonce_sum = PublicKey.from_combination(
        secp, [public_nonce_recv, public_nonce])
    refund_public_nonce_sum = PublicKey.from_combination(
        secp, [refund_public_nonce_recv, refund_public_nonce])

    # Step 2 of bulletproof
    proof_builder = MultiPartyBulletProof(secp, partial_child,
                                          public_partial_recv, send_amount,
                                          commit)
    t_1_recv = PublicKey.from_hex(secp, dct2['t_1'].encode())
    t_2_recv = PublicKey.from_hex(secp, dct2['t_2'].encode())
    t_1, t_2 = proof_builder.step_1()
    proof_builder.fill_step_1(t_1_recv, t_2_recv)
    tau_x = proof_builder.step_2()

    dct3 = {
        "t_1": t_1.to_hex(secp).decode(),
        "t_2": t_2.to_hex(secp).decode(),
        "tau_x": tau_x.to_hex().decode()
    }

    f = open("logs/{}_multisig_3.json".format(now), "w")
    f.write(json.dumps(dct3, indent=2))
    f.close()

    print("Sending bulletproof component..")

    req2 = urlopen(dest_url, json.dumps(dct3).encode(), 60)
    dct4 = json.loads(req2.read().decode())

    print("Received response")

    f = open("logs/{}_multisig_4.json".format(now), "w")
    f.write(json.dumps(dct4, indent=2))
    f.close()

    # Bulletproof
    proof = RangeProof.from_bytearray(
        bytearray(unhexlify(dct4['proof'].encode())))
    output = Output(OutputFeatures.DEFAULT_OUTPUT, commit, proof)
    assert output.verify(secp), "Invalid bulletproof"
    tx.add_output(secp, output)
    print("Created bulletproof")

    # First we finalize the refund tx, and check its validity
    refund_input = Input(OutputFeatures.DEFAULT_OUTPUT, commit)
    refund_tx.add_input(secp, refund_input)

    # Refund excess
    refund_blind_sum = BlindSum()
    refund_blind_sum.sub_child_key(partial_child)
    refund_blind_sum.add_child_key(refund_child)
    refund_blind_sum.sub_blinding_factor(refund_tx.offset)
    refund_excess = wallet.chain.blind_sum(refund_blind_sum).to_secret_key(
        secp)
    refund_public_excess = refund_excess.to_public_key(secp)

    # Refund partial signature
    refund_partial_signature = aggsig.calculate_partial(
        secp, refund_excess, refund_nonce, refund_public_nonce_sum,
        refund_fee_amount, refund_lock_height)

    # Refund final signature
    refund_public_excess_sum = PublicKey.from_combination(
        secp, [refund_public_excess_recv, refund_public_excess])
    refund_signature = aggsig.add_partials(
        secp, [refund_partial_signature_recv, refund_partial_signature],
        refund_public_nonce_sum)
    assert aggsig.verify(secp, refund_signature, refund_public_excess_sum, refund_fee_amount, refund_lock_height), \
        "Unable to verify refund signature"
    refund_kernel = refund_tx.kernels[0]
    refund_kernel.excess = refund_tx.sum_commitments(secp)
    refund_kernel.excess_signature = refund_signature
    assert refund_tx.verify_kernels(secp), "Unable to verify refund kernel"

    print("Refund tx is valid")

    f = open("logs/{}_refund.json".format(now), "w")
    f.write(json.dumps(refund_tx.to_dict(secp), indent=2))
    f.close()

    refund_tx_wrapper = {"tx_hex": refund_tx.to_hex(secp).decode()}

    f = open("logs/{}_refund_hex.json".format(now), "w")
    f.write(json.dumps(refund_tx_wrapper, indent=2))
    f.close()

    print("Finalizing multisig tx..")

    # Partial signature
    partial_signature = aggsig.calculate_partial(secp, excess, nonce,
                                                 public_nonce_sum, fee_amount,
                                                 lock_height)

    # Final signature
    public_excess_sum = PublicKey.from_combination(
        secp, [public_excess_recv, public_excess])
    signature = aggsig.add_partials(
        secp, [partial_signature_recv, partial_signature], public_nonce_sum)
    assert aggsig.verify(secp, signature, public_excess_sum, fee_amount,
                         lock_height), "Unable to verify signature"
    kernel = tx.kernels[0]
    kernel.excess = tx.sum_commitments(secp)
    kernel.excess_signature = signature
    assert tx.verify_kernels(secp), "Unable to verify kernel"

    f = open("logs/{}_tx.json".format(now), "w")
    f.write(json.dumps(tx.to_dict(secp), indent=2))
    f.close()

    tx_wrapper = {"tx_hex": tx.to_hex(secp).decode()}

    f = open("logs/{}_tx_hex.json".format(now), "w")
    f.write(json.dumps(tx_wrapper, indent=2))
    f.close()

    print("Submitting to node..")

    urlopen("{}/v1/pool/push".format(node_url) + ("?fluff" if fluff else ""),
            json.dumps(tx_wrapper).encode(), 600)

    wallet.save()

    print("Transaction complete!")
Exemple #7
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")
Exemple #8
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())