Beispiel #1
0
 def test_add_sub(self):
     k1 = CKey(
         x('5586e3531b857c5a3d7af6d512ec84161f4531b66daf2ad72a6f647e4164c8ae'
           ))
     k2 = CKey(
         x('9e77dd4f6693461578e32e60e9c095023e1fc98ae3eaf0c53f645d53a5ead91e'
           ))
     k_sum = CKey.add(k1, k2)
     pub_sum = CPubKey.add(k1.pub, k2.pub)
     self.assertEqual(pub_sum, k_sum.pub)
     if secp256k1_has_pubkey_negate:
         k_diff = CKey.sub(k1, k2)
         pub_diff = CPubKey.sub(k1.pub, k2.pub)
         self.assertEqual(pub_diff, k_diff.pub)
         self.assertEqual(k1, CKey.sub(k_sum, k2))
         self.assertEqual(k2, CKey.sub(k_sum, k1))
         self.assertEqual(k1, CKey.add(k_diff, k2))
         self.assertEqual(k2.negated(), CKey.sub(k_diff, k1))
         self.assertEqual(CKey.add(k2, k2), CKey.sub(k_sum, k_diff))
         self.assertEqual(k1.pub, CPubKey.sub(pub_sum, k2.pub))
         self.assertEqual(k2.pub, CPubKey.sub(pub_sum, k1.pub))
         self.assertEqual(k1.pub, CPubKey.add(pub_diff, k2.pub))
         self.assertEqual(k2.pub.negated(), CPubKey.sub(pub_diff, k1.pub))
         self.assertEqual(CPubKey.add(k2.pub, k2.pub),
                          CPubKey.sub(pub_sum, pub_diff))
         self.assertEqual(
             k1, CKey.combine(k1, k2, k_sum, k2.negated(), k_sum.negated()))
         self.assertEqual(
             k1.pub,
             CPubKey.combine(k1.pub, k2.pub, k_sum.pub, k2.pub.negated(),
                             k_sum.pub.negated()))
         self.assertEqual(CKey.combine(k_sum, k2, k1, k_diff),
                          CKey.combine(k1, k2, k_sum, k_diff))
         self.assertEqual(
             CPubKey.combine(k_sum.pub, k2.pub, k1.pub, k_diff.pub),
             CPubKey.combine(k1.pub, k2.pub, k_sum.pub, k_diff.pub))
         with self.assertRaises(ValueError):
             CKey.sub(k1, k1)
         with self.assertRaises(ValueError):
             CKey.combine(k1, k2, k1.negated(), k2.negated())
         with self.assertRaises(ValueError):
             CPubKey.sub(k1.pub, k1.pub)
         with self.assertRaises(ValueError):
             CPubKey.combine(k1.pub, k2.pub, k1.pub.negated(),
                             k2.pub.negated())
     else:
         logging.basicConfig()
         log = logging.getLogger("Test_CKey")
         log.warning('secp256k1 does not export pubkey negation function. '
                     'You should use newer version of secp256k1 library. '
                     'Tests that involve key substraction are skipped')
Beispiel #2
0
def add_privkeys(priv1, priv2):
    '''Add privkey 1 to privkey 2.
    Input keys must be in binary either compressed or not.
    Returned key will have the same compression state.
    Error if compression state of both input keys is not the same.'''
    y, z = [read_privkey(x) for x in [priv1, priv2]]
    if y[0] != z[0]:
        raise Exception("cannot add privkeys, mixed compression formats")
    else:
        compressed = y[0]
    newpriv1, newpriv2 = (y[1], z[1])
    res = CKey.add(CKey(newpriv1), CKey(newpriv2)).secret_bytes
    if compressed:
        res += b'\x01'
    return res
Beispiel #3
0
def prepare_elt_spend_reveal_branch(tx, elt_contract, spend_sig, contract_key,
                                    blinding_key):

    key_to_reveal = CKey.add(contract_key, blinding_key)

    rkey = ecdsa.keys.SigningKey.from_string(key_to_reveal.secret_bytes,
                                             curve=ecdsa.SECP256k1)

    k, r = get_known_k_r()
    r = ecdsa.util.number_to_string(r, ecdsa.SECP256k1.order).lstrip(b'\x00')

    reveal_sig = rkey.sign_digest(hashlib.sha256(b'\x01').digest(),
                                  k=k,
                                  sigencode=ecdsa.util.sigencode_der_canonize)

    # For reference: signature serialization code from secp256k1 library
    #
    # sig[0] = 0x30;
    # sig[1] = 4 + lenS + lenR;
    # sig[2] = 0x02;
    # sig[3] = lenR;
    # memcpy(sig+4, rp, lenR);
    # sig[4+lenR] = 0x02;
    # sig[5+lenR] = lenS;
    # memcpy(sig+lenR+6, sp, lenS);

    assert reveal_sig[3] == len(r)
    assert reveal_sig[4:4 + (len(r))] == r

    sig_prefix = reveal_sig[:4]
    sig_suffix = reveal_sig[4 + len(r):]

    # Expected stack after OP_IF branch taken:
    # sig_r sig1_pfx sig1_sfx sig2_pfx sig2_sfx spend_sig
    tx.vin[0].scriptSig = CElementsScript(
        [spend_sig, sig_suffix, sig_prefix, OP_TRUE, elt_contract])
Beispiel #4
0
def alice(say, recv, send, die, btc_rpc, elt_rpc):
    """A function that implements the logic
    of the Elements-side participant
    of confidential cross-chain atomic swap"""

    global last_wish_func

    # Default chain for Alice will be Elements
    # To handle bitcoin-related objects, either
    # `with ChainParams(bitcoin_chain_name):` have to be used, or
    # concrete classes, like CBitcoinAddress, CBitcoinTransaction, etc.
    select_chain_params(elements_chain_name)

    # Let's create the shared blinding key
    blinding_key = CKey.from_secret_bytes(os.urandom(32))
    # And the key for btc spend
    alice_btc_key = CKey.from_secret_bytes(os.urandom(32))
    # And the key for the 'timeout' branch of the contract
    alice_elt_exit_key = CKey.from_secret_bytes(os.urandom(32))

    say('Sending pubkeys to Bob')
    send('pubkeys', (alice_btc_key.pub, alice_elt_exit_key.pub))

    say('Sending the blinding key to Bob')
    send('blinding_key', blinding_key.secret_bytes)

    (contract_pubkey_raw, bob_elt_pubkey_raw,
     bob_btc_exit_pub_raw) = recv('pubkeys')

    say("Pubkey of the key to be revealed: {}".format(
        b2x(contract_pubkey_raw)))
    say("Bob's Elements-side pubkey: {}".format(b2x(bob_elt_pubkey_raw)))

    contract_pubkey = CPubKey(contract_pubkey_raw)

    key_to_reveal_pub = CPubKey.add(contract_pubkey, blinding_key.pub)

    elt_contract = make_elt_cntract(key_to_reveal_pub, bob_elt_pubkey_raw,
                                    alice_elt_exit_key.pub)

    elt_contract_addr = P2SHCoinAddress.from_redeemScript(elt_contract)

    confidential_contract_addr = P2SHCoinConfidentialAddress.from_unconfidential(
        elt_contract_addr, blinding_key.pub)
    assert isinstance(confidential_contract_addr, CElementsConfidentialAddress)

    say("Created Elemets-side swap contract, size: {}".format(
        len(elt_contract)))
    say("Contract address:\n\tconfidential: {}\n\tunconfidential: {}".format(
        confidential_contract_addr, elt_contract_addr))

    btc_txid = recv('btc_txid')

    combined_btc_spend_pubkey = CPubKey.add(contract_pubkey, alice_btc_key.pub)
    btc_contract = make_btc_contract(combined_btc_spend_pubkey,
                                     bob_btc_exit_pub_raw)

    tx_json = btc_rpc.getrawtransaction(btc_txid, 1)

    if tx_json['confirmations'] < 6:
        die('Transaction does not have enough confirmations')

    # We use ChainParams, and not P2WSHBitcoinAddress here,
    # because bitcoin_chain_name might be 'bitcoin/regtest', for example,
    # and then the address would need to be P2WSHBitcoinRegtestAddress.
    # with ChainParams we leverage the 'frontend class' magic, P2WSHCoinAddress
    # will give us appropriate instance.
    with ChainParams(bitcoin_chain_name):
        btc_contract_addr = P2WSHCoinAddress.from_redeemScript(btc_contract)
        say('Looking for this address in transaction {} in Bitcoin'.format(
            btc_txid))

    # CTransaction subclasses do not change between mainnet/testnet/regtest,
    # so we can directly use CBitcoinTransaction.
    # That might not be true for other chains, though.
    # You might also want to use CTransaction within `with ChainParams(...):`
    btc_tx = CBitcoinTransaction.deserialize(x(tx_json['hex']))

    for n, vout in enumerate(btc_tx.vout):
        if vout.scriptPubKey == btc_contract_addr.to_scriptPubKey():
            say("Found the address at output {}".format(n))
            btc_vout_n = n
            break
    else:
        die('Did not find contract address in transaction')

    if vout.nValue != coins_to_satoshi(pre_agreed_amount):
        die('the amount {} found at the output in the offered transaction '
            'does not match the expected amount {}'.format(
                satoshi_to_coins(vout.nValue), pre_agreed_amount))

    say('Bitcoin amount match expected values')

    say('Sending {} to {}'.format(pre_agreed_amount,
                                  confidential_contract_addr))
    contract_txid = elt_rpc.sendtoaddress(str(confidential_contract_addr),
                                          pre_agreed_amount)

    def alice_last_wish_func():
        try_reclaim_elt(say, elt_rpc, contract_txid, elt_contract,
                        alice_elt_exit_key, blinding_key, die)

    last_wish_func = alice_last_wish_func

    wait_confirm(say, 'Elements', contract_txid, die, elt_rpc, num_confirms=2)

    send('elt_txid', contract_txid)

    sr_txid = wait_spend_reveal_transaction(say, contract_txid, die, elt_rpc)

    say('Got txid for spend-reveal transaction from Bob ({})'.format(sr_txid))

    tx_json = elt_rpc.getrawtransaction(sr_txid, 1)

    wait_confirm(say, 'Elements', sr_txid, die, elt_rpc, num_confirms=2)

    sr_tx = CTransaction.deserialize(x(tx_json['hex']))

    for n, vin in enumerate(sr_tx.vin):
        if vin.prevout.hash == lx(contract_txid)\
                and vin.scriptSig[-(len(elt_contract)):] == elt_contract:
            say('Transaction input {} seems to contain a script '
                'we can recover the key from'.format(n))
            reveal_script_iter = iter(vin.scriptSig)
            break
    else:
        die('Spend-reveal transaction does not have input that spends '
            'the contract output')

    next(reveal_script_iter)  # skip Bob's spend signature

    try:
        # 2 skipped bytes are tag and len
        sig_s = ecdsa.util.string_to_number(next(reveal_script_iter)[2:])
    except (ValueError, StopIteration):
        die('Reveal script is invalid')

    k, r = get_known_k_r()
    order = ecdsa.SECP256k1.order
    mhash = ecdsa.util.string_to_number(hashlib.sha256(b'\x01').digest())
    r_inverse = ecdsa.numbertheory.inverse_mod(r, order)

    for s in (-sig_s, sig_s):
        secret_exponent = (((s * k - mhash) % order) * r_inverse) % order

        recovered_key = CKey.from_secret_bytes(
            ecdsa.util.number_to_string(secret_exponent, order))

        if recovered_key.pub == key_to_reveal_pub:
            break
    else:
        die('Key recovery failed. Should not happen - the sig was already '
            'verified when transaction was accepted into mempool. '
            'Must be a bug.')

    say('recovered key pubkey: {}'.format(b2x(recovered_key.pub)))
    contract_key = CKey.sub(recovered_key, blinding_key)
    say('recovered unblined key pubkey: {}'.format(b2x(contract_key.pub)))
    combined_btc_spend_key = CKey.add(contract_key, alice_btc_key)

    say('Successfully recovered the key. Can now spend Bitcoin from {}'.format(
        btc_contract_addr))

    with ChainParams(bitcoin_chain_name):
        dst_addr = CCoinAddress(btc_rpc.getnewaddress())
        btc_claim_tx = create_btc_spend_tx(dst_addr,
                                           btc_txid,
                                           btc_vout_n,
                                           btc_contract,
                                           spend_key=combined_btc_spend_key)

    say('Sending my Bitcoin-claim transaction')
    btc_claim_txid = btc_rpc.sendrawtransaction(b2x(btc_claim_tx.serialize()))

    wait_confirm(say, 'Bitcoin', btc_claim_txid, die, btc_rpc, num_confirms=3)

    say('Got my Bitcoin. Swap successful!')