예제 #1
0
 def test(self) -> None:
     data = x('5586e3531b857c5a3d7af6d512ec84161f4531b66daf2ad72a6f647e4164c8ae')
     k = CKey(data)
     self.assertEqual(k, data)
     expected_pub = x('0392aef1ad6db10a2da4aa9f9e874fa28d5423eaa29ee83aa9acec01cc812903df')
     self.assertEqual(k.pub, expected_pub)
     expected_uncompressed_pub = x('0492aef1ad6db10a2da4aa9f9e874fa28d5423eaa29ee83aa9acec01cc812903df4c9b555eb62f0bf6dc4406b271365768737733ec8af4024809348ea594eef1f3')
     k = CKey(data, compressed=False)
     self.assertEqual(k.pub, expected_uncompressed_pub)
예제 #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
예제 #3
0
def multiply(s, pub, return_serialized=True):
    '''Input binary compressed pubkey P(33 bytes)
    and scalar s(32 bytes), return s*P.
    The return value is a binary compressed public key,
    or a PublicKey object if return_serialized is False.
    Note that the called function does the type checking
    of the scalar s.
    ('raw' options passed in)
    '''
    try:
        CKey(s)
    except ValueError:
        raise ValueError("Invalid tweak for libsecp256k1 "
                         "multiply: {}".format(bintohex(s)))

    pub_obj = CPubKey(pub)
    if not pub_obj.is_fullyvalid():
        raise ValueError("Invalid pubkey for multiply: {}".format(
            bintohex(pub)))

    privkey_arg = ctypes.c_char_p(s)
    pubkey_buf = pub_obj._to_ctypes_char_array()
    ret = secp_lib.secp256k1_ec_pubkey_tweak_mul(secp256k1_context_verify,
                                                 pubkey_buf, privkey_arg)
    if ret != 1:
        assert ret == 0
        raise ValueError('Multiplication failed')
    if not return_serialized:
        return CPubKey._from_ctypes_char_array(pubkey_buf)
    return bytes(CPubKey._from_ctypes_char_array(pubkey_buf))
예제 #4
0
    def check_unblind(self, unblinded_tx, unblinded_tx_raw, blinded_tx,
                      blinded_tx_raw, bundle, blinding_derivation_key):
        for n, bvout in enumerate(blinded_tx.vout):
            uvout = unblinded_tx.vout[n]

            if not uvout.nValue.is_explicit():
                # skip confidential vouts of partially-blinded txs
                continue

            self.assertEqual(bvout.scriptPubKey, uvout.scriptPubKey)
            if bvout.nAsset.is_explicit():
                self.assertTrue(bvout.nValue.is_explicit())
                self.assertEqual(bvout.nValue.to_amount(),
                                 uvout.nValue.to_amount())
                self.assertEqual(bvout.nAsset.to_asset().data,
                                 uvout.nAsset.to_asset().data)
                self.assertEqual(bvout.nNonce.commitment,
                                 uvout.nNonce.commitment)
            else:
                self.assertFalse(bvout.nValue.is_explicit())

                for fbk, spk_set in bundle['foreign_blinding_keys'].items():
                    if b2x(bvout.scriptPubKey) in spk_set:
                        blinding_key = uvout.scriptPubKey.derive_blinding_key(
                            CKey(lx(fbk)))
                        break
                else:
                    blinding_key = uvout.scriptPubKey.derive_blinding_key(
                        blinding_derivation_key)

                unblind_result = bvout.unblind_confidential_pair(
                    blinding_key=blinding_key,
                    rangeproof=blinded_tx.wit.vtxoutwit[n].rangeproof)

                self.assertFalse(unblind_result.error)
                self.assertEqual(uvout.nValue.to_amount(),
                                 unblind_result.amount)
                self.assertEqual(uvout.nAsset.to_asset().data,
                                 unblind_result.asset.data)
                descr = unblind_result.get_descriptor()

                self.assertIsInstance(descr, BlindingInputDescriptor)
                self.assertEqual(descr.amount, unblind_result.amount)
                self.assertEqual(descr.asset, unblind_result.asset)
                self.assertEqual(descr.blinding_factor,
                                 unblind_result.blinding_factor)
                self.assertEqual(descr.asset_blinding_factor,
                                 unblind_result.asset_blinding_factor)

                ub_info = bundle['unblinded_vout_info'][n]
                if len(ub_info):
                    self.assertEqual(coins_to_satoshi(ub_info['amount']),
                                     unblind_result.amount)
                    self.assertEqual(ub_info['asset'],
                                     unblind_result.asset.to_hex())
                    self.assertEqual(ub_info['blinding_factor'],
                                     unblind_result.blinding_factor.to_hex())
                    self.assertEqual(
                        ub_info['asset_blinding_factor'],
                        unblind_result.asset_blinding_factor.to_hex())
예제 #5
0
    def test_blind_unnblind_sign(self):
        if not secp256k1_has_zkp:
            warn_zkp_unavailable()
            return

        with open(
                os.path.dirname(__file__) + '/data/elements_txs_blinding.json',
                'r') as fd:
            for bundle in json.load(fd):
                blinded_tx_raw = x(bundle['blinded']['hex'])
                blinded_tx = CTransaction.deserialize(blinded_tx_raw)
                self.assertEqual(blinded_tx.serialize(), blinded_tx_raw)
                self.check_serialize_deserialize(blinded_tx, blinded_tx_raw,
                                                 bundle['blinded'])
                unblinded_tx_raw = x(bundle['unblinded']['hex'])
                unblinded_tx = CTransaction.deserialize(unblinded_tx_raw)

                self.assertEqual(unblinded_tx.serialize(), unblinded_tx_raw)
                self.check_serialize_deserialize(unblinded_tx,
                                                 unblinded_tx_raw,
                                                 bundle['unblinded'])
                signed_tx_raw = x(bundle['signed_hex'])
                signed_tx = CTransaction.deserialize(signed_tx_raw)
                self.assertEqual(signed_tx.serialize(), signed_tx_raw)
                blinding_derivation_key = CKey(
                    lx(bundle['blinding_derivation_key']))

                # ensure that str and repr works
                for f in (str, repr):
                    f(unblinded_tx)
                    f(blinded_tx)
                    f(signed_tx)

                if len(blinded_tx.vout) != len(unblinded_tx.vout):
                    assert len(blinded_tx.vout) == len(unblinded_tx.vout) + 1
                    assert blinded_tx.vout[-1].scriptPubKey == b'\x6a',\
                        "expected last output of blinded tx to be OP_RETURN"
                    scriptPubKey = CScript([OP_RETURN])
                    unblinded_tx = unblinded_tx.to_mutable()
                    unblinded_tx.vout.append(
                        CMutableTxOut(
                            nValue=CConfidentialValue(0),
                            nAsset=CConfidentialAsset(
                                unblinded_tx.vout[-1].nAsset.to_asset()),
                            nNonce=CConfidentialNonce(
                                scriptPubKey.derive_blinding_key(
                                    blinding_derivation_key).pub),
                            scriptPubKey=scriptPubKey))
                    unblinded_tx = unblinded_tx.to_immutable()
                    unblinded_tx_raw = unblinded_tx.serialize()

                self.check_blind(unblinded_tx, unblinded_tx_raw, blinded_tx,
                                 blinded_tx_raw, bundle,
                                 blinding_derivation_key)

                self.check_unblind(unblinded_tx, unblinded_tx_raw, blinded_tx,
                                   blinded_tx_raw, bundle,
                                   blinding_derivation_key)

                self.check_sign(blinded_tx, signed_tx, bundle)
예제 #6
0
def ecdsa_raw_sign(msg, priv, rawmsg=False):
    '''Take the binary message msg and sign it with the private key
    priv.
    If rawmsg is True, no sha256 hash is applied to msg before signing.
    In this case, msg must be a precalculated hash (256 bit).
    If rawmsg is False, the secp256k1 lib will hash the message as part
    of the ECDSA-SHA256 signing algo.
    Return value: the calculated signature.'''
    if rawmsg and len(msg) != 32:
        raise Exception("Invalid hash input to ECDSA raw sign.")
    compressed, p = read_privkey(priv)
    newpriv = CKey(p, compressed=compressed)
    if rawmsg:
        sig = newpriv.sign(msg, _ecdsa_sig_grind_low_r=False)
    else:
        sig = newpriv.sign(Hash(msg), _ecdsa_sig_grind_low_r=False)
    return sig
    def test_split_blinding_multi_sign(self) -> None:
        if not secp256k1_has_zkp:
            warn_zkp_unavailable()
            return

        with open(
                os.path.dirname(__file__) +
                '/data/elements_txs_split_blinding.json', 'r') as fd:
            split_blind_txdata = json.load(fd)
            # we need to supply asset commitments from all inputs of the final
            # tranaction to the blinding function, even if we are blinding a tx
            # template that does not contain these inputs
            asset_commitments = [
                x(utxo['assetcommitment'])
                for utxo in split_blind_txdata['tx2']['vin_utxo']
            ]

            for txlabel in ('tx1', 'tx2'):
                bundle = split_blind_txdata[txlabel]
                blinded_tx_raw = x(bundle['blinded']['hex'])
                blinded_tx = CElementsTransaction.deserialize(blinded_tx_raw)
                self.assertEqual(blinded_tx.serialize(), blinded_tx_raw)
                self.check_serialize_deserialize(blinded_tx, blinded_tx_raw,
                                                 bundle['blinded'])
                unblinded_tx_raw = x(bundle['unblinded']['hex'])
                unblinded_tx = CElementsTransaction.deserialize(
                    unblinded_tx_raw)

                self.assertEqual(unblinded_tx.serialize(), unblinded_tx_raw)
                self.check_serialize_deserialize(unblinded_tx,
                                                 unblinded_tx_raw,
                                                 bundle['unblinded'])
                signed_tx: Optional[CElementsTransaction]
                if 'signed_hex' in bundle:
                    signed_tx_raw = x(bundle['signed_hex'])
                    signed_tx = CElementsTransaction.deserialize(signed_tx_raw)
                    self.assertEqual(signed_tx.serialize(), signed_tx_raw)
                else:
                    signed_tx = None

                blinding_derivation_key = CKey(
                    lx(bundle['blinding_derivation_key']))

                self.check_blind(unblinded_tx,
                                 unblinded_tx_raw,
                                 blinded_tx,
                                 blinded_tx_raw,
                                 bundle,
                                 blinding_derivation_key,
                                 asset_commitments=asset_commitments)

                self.check_unblind(unblinded_tx, unblinded_tx_raw, blinded_tx,
                                   blinded_tx_raw, bundle,
                                   blinding_derivation_key)

                if signed_tx is not None:
                    self.check_sign(blinded_tx, signed_tx, bundle)
예제 #8
0
def getG(compressed=True):
    """Returns the public key binary
    representation of secp256k1 G;
    note that CPubKey is of type bytes.
    """
    priv = b"\x00" * 31 + b"\x01"
    k = CKey(priv, compressed=compressed)
    G = k.pub
    return G
예제 #9
0
def privkey_to_pubkey(priv):
    '''Take 32/33 byte raw private key as input.
    If 32 bytes, return as uncompressed raw public key.
    If 33 bytes and the final byte is 01, return
    compresse public key. Else throws Exception.'''
    compressed, priv = read_privkey(priv)
    # CKey checks for validity of key value;
    # any invalidity throws ValueError.
    newpriv = CKey(priv, compressed=compressed)
    return newpriv.pub
예제 #10
0
    def test_parse_standard_multisig_redeem_script(self):
        def T(script):
            return parse_standard_multisig_redeem_script(script)

        def T2(script, result):
            info = T(script)
            self.assertEqual(info.total, result.total)
            self.assertEqual(info.required, result.required)
            self.assertEqual(info.pubkeys, result.pubkeys)

        # NOTE: p2sh_multisig_parse_script does not check validity of pubkeys
        pubkeys = [CKey.from_secret_bytes(os.urandom(32)).pub
                   for _ in range(15)]

        T2(CScript([1, pubkeys[0], pubkeys[1], 2, OP_CHECKMULTISIG]),
           StandardMultisigScriptInfo(total=2, required=1,
                                      pubkeys=pubkeys[:2]))
        T2(CScript([11] + pubkeys[:12] + [12, OP_CHECKMULTISIG]),
           StandardMultisigScriptInfo(total=12, required=11,
                                      pubkeys=pubkeys[:12]))
        T2(CScript([15] + pubkeys + [15, OP_CHECKMULTISIG]),
           StandardMultisigScriptInfo(total=15, required=15, pubkeys=pubkeys))

        with self.assertRaises(ValueError):
            # invalid script - extra opcode
            T(CScript([1, pubkeys[0], pubkeys[1], 2, OP_CHECKMULTISIG, OP_DROP]))
        with self.assertRaises(ValueError):
            # invalid pubkey - extra data
            T(CScript([1, pubkeys[0]+b'abc', pubkeys[1], 2, OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            # invalid pubkey - truncated
            T(CScript([1, pubkeys[0], pubkeys[1][:-1], 2, OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            T(CScript([]))
        with self.assertRaises(ValueError):
            T(CScript([b'\x01', pubkeys[0], pubkeys[1], 2, OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            T(CScript([16, pubkeys[0], pubkeys[1], 2, OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            T(CScript([11] + pubkeys[:12] + [b'\x0c', OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            T(CScript([11] + pubkeys + [14, OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            T(CScript([11] + pubkeys + pubkeys + [15, OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            T(CScript([1, pubkeys[0], pubkeys[1], 1, OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            T(CScript([11] + pubkeys[:12] + [11, OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            T(CScript([11] + pubkeys[:12] + [11, OP_CHECKSIG]))
        with self.assertRaises(ValueError):
            T(CScript([11] + pubkeys[:12]))  # short script 1
        with self.assertRaises(ValueError):
            T(CScript([11] + pubkeys[:12] + [11]))  # short script 2
예제 #11
0
def snicker_pubkey_tweak(pub, tweak):
    """ use secp256k1 library to perform tweak.
    Both `pub` and `tweak` are expected as byte strings
    (33 and 32 bytes respectively).
    Return value is also a 33 byte string serialization
    of the resulting pubkey (compressed).
    """
    base_pub = CPubKey(pub)
    # convert the tweak to a new pubkey
    tweak_pub = CKey(tweak, compressed=True).pub
    return add_pubkeys([base_pub, tweak_pub])
예제 #12
0
    def test_parse_standard_multisig_redeem_script(self):
        def T(script, result):
            self.assertEqual(parse_standard_multisig_redeem_script(script),
                             result)

        # NOTE: p2sh_multisig_parse_script does not check validity of pubkeys
        pubkeys = [
            CKey.from_secret_bytes(os.urandom(32)).pub for _ in range(15)
        ]

        T(CScript([1, pubkeys[0], pubkeys[1], 2, OP_CHECKMULTISIG]), {
            'total': 2,
            'required': 1,
            'pubkeys': pubkeys[:2]
        })
        T(CScript([11] + pubkeys[:12] + [12, OP_CHECKMULTISIG]), {
            'total': 12,
            'required': 11,
            'pubkeys': pubkeys[:12]
        })
        T(CScript([15] + pubkeys + [15, OP_CHECKMULTISIG]), {
            'total': 15,
            'required': 15,
            'pubkeys': pubkeys
        })

        with self.assertRaises(ValueError):
            T(CScript([]), {})
        with self.assertRaises(ValueError):
            T(CScript([b'\x01', pubkeys[0], pubkeys[1], 2, OP_CHECKMULTISIG]),
              {})
        with self.assertRaises(ValueError):
            T(CScript([16, pubkeys[0], pubkeys[1], 2, OP_CHECKMULTISIG]), {})
        with self.assertRaises(ValueError):
            T(CScript([11] + pubkeys[:12] + [b'\x0c', OP_CHECKMULTISIG]), {})
        with self.assertRaises(ValueError):
            T(CScript([11] + pubkeys + [14, OP_CHECKMULTISIG]), {})
        with self.assertRaises(ValueError):
            T(CScript([11] + pubkeys + pubkeys + [15, OP_CHECKMULTISIG]), {})
        with self.assertRaises(ValueError):
            T(CScript([1, pubkeys[0], pubkeys[1], 1, OP_CHECKMULTISIG]), {})
        with self.assertRaises(ValueError):
            T(CScript([11] + pubkeys[:12] + [11, OP_CHECKMULTISIG]), {})
        with self.assertRaises(ValueError):
            T(CScript([11] + pubkeys[:12] + [11, OP_CHECKSIG]), {})
        with self.assertRaises(ValueError):
            T(
                CScript([11] + pubkeys[:12]),  # short script 1
                {})
        with self.assertRaises(ValueError):
            T(
                CScript([11] + pubkeys[:12] + [11]),  # short script 2
                {})
예제 #13
0
def ecdh(privkey, pubkey):
    """ Take a privkey in raw byte serialization,
    and a pubkey serialized in compressed, binary format (33 bytes),
    and output the shared secret as a 32 byte hash digest output.
    The exact calculation is:
    shared_secret = SHA256(compressed_serialization_of_pubkey(privkey * pubkey))
    .. where * is elliptic curve scalar multiplication.
    See https://github.com/bitcoin/bitcoin/blob/master/src/secp256k1/src/modules/ecdh/main_impl.h
    for implementation details.
    """
    _, priv = read_privkey(privkey)
    return CKey(priv).ECDH(CPubKey(pubkey))
예제 #14
0
    def test_sighash(self) -> None:
        spent_amount = 1100
        pub = CKey.from_secret_bytes(os.urandom(32)).pub
        spk_legacy = P2PKHCoinAddress.from_pubkey(pub).to_scriptPubKey()
        spk_segwit = P2WPKHCoinAddress.from_pubkey(pub).to_scriptPubKey()

        tx = CTransaction([
            CTxIn(
                COutPoint(b'\x00' * 32, 0), CScript([]), nSequence=0xFFFFFFFF)
        ], [CTxOut(1000, spk_legacy)],
                          nLockTime=0,
                          nVersion=1)

        # no exceptions should be raised with these two calls
        spk_legacy.sighash(tx,
                           0,
                           SIGHASH_ALL,
                           amount=spent_amount,
                           sigversion=SIGVERSION_WITNESS_V0)
        spk_segwit.sighash(tx,
                           0,
                           SIGHASH_ALL,
                           amount=spent_amount,
                           sigversion=SIGVERSION_WITNESS_V0)

        with self.assertRaises(ValueError):
            # unknown sigversion
            spk_segwit.sighash(tx,
                               0,
                               SIGHASH_ALL,
                               amount=spent_amount,
                               sigversion=SIGVERSION_WITNESS_V0 +
                               1)  # type: ignore

        assert spk_segwit.is_witness_scriptpubkey()
        with self.assertRaises(ValueError):
            # incorect sigversion for segwit
            spk_segwit.sighash(tx,
                               0,
                               SIGHASH_ALL,
                               amount=spent_amount,
                               sigversion=SIGVERSION_BASE)

        with self.assertRaises(ValueError):
            # inIdx > len(tx.vin) - non-raw sighash function should raise
            # ValueError (raw_sighash can return HASH_ONE)
            spk_legacy.sighash(tx,
                               10,
                               SIGHASH_ALL,
                               amount=spent_amount,
                               sigversion=SIGVERSION_BASE)
예제 #15
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])
예제 #16
0
        def T(required: int,
              total: int,
              alt_total: Optional[int] = None) -> None:
            amount = 10000
            keys = [
                CKey.from_secret_bytes(os.urandom(32)) for _ in range(total)
            ]
            pubkeys = [k.pub for k in keys]

            if alt_total is not None:
                total = alt_total  # for assertRaises checks

            redeem_script = standard_multisig_redeem_script(total=total,
                                                            required=required,
                                                            pubkeys=pubkeys)

            # Test with P2SH

            scriptPubKey = redeem_script.to_p2sh_scriptPubKey()

            (_, tx) = self.create_test_txs(CScript(), scriptPubKey,
                                           CScriptWitness([]), amount)

            tx = tx.to_mutable()

            sighash = redeem_script.sighash(tx,
                                            0,
                                            SIGHASH_ALL,
                                            amount=amount,
                                            sigversion=SIGVERSION_BASE)

            sigs = [
                k.sign(sighash) + bytes([SIGHASH_ALL]) for k in keys[:required]
            ]

            tx.vin[0].scriptSig = CScript(
                standard_multisig_witness_stack(sigs, redeem_script))

            VerifyScript(tx.vin[0].scriptSig, scriptPubKey, tx, 0,
                         (SCRIPT_VERIFY_P2SH, ))

            # Test with P2WSH

            scriptPubKey = redeem_script.to_p2wsh_scriptPubKey()

            (_, tx) = self.create_test_txs(CScript(), scriptPubKey,
                                           CScriptWitness([]), amount)

            tx = tx.to_mutable()

            sighash = redeem_script.sighash(tx,
                                            0,
                                            SIGHASH_ALL,
                                            amount=amount,
                                            sigversion=SIGVERSION_WITNESS_V0)

            sigs = [
                k.sign(sighash) + bytes([SIGHASH_ALL]) for k in keys[:required]
            ]

            witness_stack = standard_multisig_witness_stack(
                sigs, redeem_script)
            tx.vin[0].scriptSig = CScript([])
            tx.wit.vtxinwit[0] = CTxInWitness(
                CScriptWitness(witness_stack)).to_mutable()

            VerifyScript(tx.vin[0].scriptSig,
                         scriptPubKey,
                         tx,
                         0,
                         flags=(SCRIPT_VERIFY_WITNESS, SCRIPT_VERIFY_P2SH),
                         amount=amount,
                         witness=tx.wit.vtxinwit[0].scriptWitness)

            # Test with P2SH_P2WSH

            scriptPubKey = redeem_script.to_p2wsh_scriptPubKey()

            (_, tx) = self.create_test_txs(CScript(), scriptPubKey,
                                           CScriptWitness([]), amount)

            tx = tx.to_mutable()

            sighash = redeem_script.sighash(tx,
                                            0,
                                            SIGHASH_ALL,
                                            amount=amount,
                                            sigversion=SIGVERSION_WITNESS_V0)

            sigs = [
                k.sign(sighash) + bytes([SIGHASH_ALL]) for k in keys[:required]
            ]

            witness_stack = standard_multisig_witness_stack(
                sigs, redeem_script)
            tx.vin[0].scriptSig = CScript([scriptPubKey])
            tx.wit.vtxinwit[0] = CTxInWitness(
                CScriptWitness(witness_stack)).to_mutable()

            VerifyScript(tx.vin[0].scriptSig,
                         scriptPubKey.to_p2sh_scriptPubKey(),
                         tx,
                         0,
                         flags=(SCRIPT_VERIFY_WITNESS, SCRIPT_VERIFY_P2SH),
                         amount=amount,
                         witness=tx.wit.vtxinwit[0].scriptWitness)
예제 #17
0
 def test_add_sub(self) -> None:
     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')
예제 #18
0
def bob(say, recv, send, die, btc_rpc, elt_rpc):
    """A function that implements the logic
    of the Bitcoin-side participant
    of confidential cross-chain atomic swap"""

    global last_wish_func

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

    say('Waiting for blinding key from Alice')
    alice_btc_pub_raw, alice_elt_exit_pub_raw = recv('pubkeys')

    blinding_key = CKey.from_secret_bytes(recv('blinding_key'))
    say("Pubkey for blinding key: {}".format(b2x(blinding_key.pub)))

    # Let's create the key that would lock the coins on Bitcoin side
    contract_key = CKey.from_secret_bytes(os.urandom(32))
    # And the key for Elements side
    bob_elt_spend_key = CKey.from_secret_bytes(os.urandom(32))
    # And the key for 'timeout' case on btc side
    bob_btc_exit_key = CKey.from_secret_bytes(os.urandom(32))

    key_to_reveal_pub = CPubKey.add(contract_key.pub, blinding_key.pub)
    say("The pubkey of the combined key to be revealed: {}".format(
        b2x(key_to_reveal_pub)))

    say('Sending my pubkeys to Alice')
    send('pubkeys',
         (contract_key.pub, bob_elt_spend_key.pub, bob_btc_exit_key.pub))

    combined_btc_spend_pubkey = CPubKey.add(contract_key.pub,
                                            CPubKey(alice_btc_pub_raw))

    say('combined_btc_spend_pubkey: {}'.format(b2x(combined_btc_spend_pubkey)))
    btc_contract = make_btc_contract(combined_btc_spend_pubkey,
                                     bob_btc_exit_key.pub)

    btc_contract_addr = P2WSHCoinAddress.from_redeemScript(btc_contract)

    say("Created Bitcoin-side swap contract, size: {}".format(
        len(btc_contract)))
    say("Contract address: {}".format(btc_contract_addr))

    say('Sending {} to {}'.format(pre_agreed_amount, btc_contract_addr))
    btc_txid = btc_rpc.sendtoaddress(str(btc_contract_addr), pre_agreed_amount)

    def bob_last_wish_func():
        try_reclaim_btc(say, btc_rpc, btc_txid, btc_contract, bob_btc_exit_key,
                        die)

    last_wish_func = bob_last_wish_func

    wait_confirm(say, 'Bitcoin', btc_txid, die, btc_rpc, num_confirms=6)

    send('btc_txid', btc_txid)
    elt_txid = recv('elt_txid')

    elt_contract = make_elt_cntract(key_to_reveal_pub, bob_elt_spend_key.pub,
                                    alice_elt_exit_pub_raw)

    with ChainParams(elements_chain_name):
        elt_contract_addr = P2SHCoinAddress.from_redeemScript(elt_contract)

    say('Got Elements contract address from Alice: {}'.format(
        elt_contract_addr))
    say('Looking for this address in transaction {} in Elements'.format(
        elt_txid))

    tx_json = elt_rpc.getrawtransaction(elt_txid, 1)

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

    elt_commit_tx = CElementsTransaction.deserialize(x(tx_json['hex']))

    vout_n, unblind_result = find_and_unblind_vout(say, elt_commit_tx,
                                                   elt_contract_addr,
                                                   blinding_key, die)

    if unblind_result.amount != 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(unblind_result.amount), pre_agreed_amount))

    say('The asset and amount match expected values. lets spend it.')

    with ChainParams(elements_chain_name):
        dst_addr = CCoinAddress(elt_rpc.getnewaddress())
        assert isinstance(dst_addr, CCoinConfidentialAddress)

        say('I will claim my Elements-BTC to {}'.format(dst_addr))

        elt_claim_tx = create_elt_spend_tx(
            dst_addr,
            elt_txid,
            vout_n,
            elt_contract,
            die,
            spend_key=bob_elt_spend_key,
            contract_key=contract_key,
            blinding_key=blinding_key,
            blinding_factor=unblind_result.blinding_factor,
            asset_blinding_factor=unblind_result.asset_blinding_factor)

        # Cannot use VerifyScript for now,
        # because it does not support CHECKSIGFROMSTACK yet
        #
        # VerifyScript(tx.vin[0].scriptSig,
        #              elt_contract_addr.to_scriptPubKey(),
        #              tx, 0, amount=amount)

    say('Sending my spend-reveal transaction')
    sr_txid = elt_rpc.sendrawtransaction(b2x(elt_claim_tx.serialize()))

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

    say('Got my Elements-BTC. Swap successful (at least for me :-)')
예제 #19
0
    def test_invalid_key(self) -> None:
        with self.assertRaises(ValueError):
            CKey(b'\x00'*32)

        with self.assertRaises(ValueError):
            CKey(b'\xff'*32)
예제 #20
0
def create_elt_spend_tx(dst_addr,
                        txid,
                        vout_n,
                        elt_contract,
                        die,
                        spend_key=None,
                        contract_key=None,
                        blinding_key=None,
                        blinding_factor=None,
                        asset_blinding_factor=None,
                        branch_condition=True):

    fee_satoshi = coins_to_satoshi(fixed_fee_amount)
    out_amount = coins_to_satoshi(pre_agreed_amount) - fee_satoshi

    # Single blinded output is not allowed, so we add
    # dummy OP_RETURN output, and we need dummy pubkey for it
    dummy_key = CKey.from_secret_bytes(os.urandom(32))

    tx = CMutableTransaction(
        vin=[CTxIn(prevout=COutPoint(hash=lx(txid), n=vout_n))],
        vout=[
            CTxOut(nValue=CConfidentialValue(out_amount),
                   nAsset=CConfidentialAsset(bitcoin_asset),
                   scriptPubKey=dst_addr.to_scriptPubKey(),
                   nNonce=CConfidentialNonce(dst_addr.blinding_pubkey)),
            CTxOut(nValue=CConfidentialValue(0),
                   nAsset=CConfidentialAsset(bitcoin_asset),
                   nNonce=CConfidentialNonce(dummy_key.pub),
                   scriptPubKey=CElementsScript([OP_RETURN])),
            CTxOut(nValue=CConfidentialValue(fee_satoshi),
                   nAsset=CConfidentialAsset(bitcoin_asset))
        ])

    output_pubkeys = [dst_addr.blinding_pubkey, dummy_key.pub]

    in_amount = coins_to_satoshi(pre_agreed_amount)

    input_descriptors = [
        BlindingInputDescriptor(asset=bitcoin_asset,
                                amount=in_amount,
                                blinding_factor=blinding_factor,
                                asset_blinding_factor=asset_blinding_factor)
    ]

    blind_result = tx.blind(input_descriptors=input_descriptors,
                            output_pubkeys=output_pubkeys)

    # The blinding must succeed!
    if blind_result.error:
        die('blind failed: {}'.format(blind_result.error))

    if branch_condition is False:
        # Must set nSequence before we calculate signature hash,
        # because it is included in it
        tx.vin[0].nSequence = elements_contract_timeout

    # We used P2SHCoinAddress to create the address that
    # we sent Elements-BTC to, so we know that we need
    # to use SIGVERSION_BASE

    sighash = elt_contract.sighash(tx,
                                   0,
                                   SIGHASH_ALL,
                                   amount=CConfidentialValue(in_amount),
                                   sigversion=SIGVERSION_BASE)

    spend_sig = spend_key.sign(sighash) + bytes([SIGHASH_ALL])

    if branch_condition is True:
        prepare_elt_spend_reveal_branch(tx, elt_contract, spend_sig,
                                        contract_key, blinding_key)
    else:
        tx.vin[0].scriptSig = CElementsScript(
            [spend_sig, OP_FALSE, elt_contract])

    return tx
예제 #21
0
    def test_parse_standard_multisig_redeem_script(self) -> None:
        def T(script: CScript) -> StandardMultisigScriptInfo:
            return parse_standard_multisig_redeem_script(script)

        def T2(script: CScript, result: StandardMultisigScriptInfo) -> None:
            info = T(script)
            self.assertEqual(info.total, result.total)
            self.assertEqual(info.required, result.required)
            self.assertEqual(info.pubkeys, result.pubkeys)

        # NOTE: p2sh_multisig_parse_script does not check validity of pubkeys
        pubkeys = [
            CKey.from_secret_bytes(os.urandom(32)).pub for _ in range(15)
        ]

        T2(
            CScript([1, pubkeys[0], pubkeys[1], 2, OP_CHECKMULTISIG]),
            StandardMultisigScriptInfo(total=2,
                                       required=1,
                                       pubkeys=pubkeys[:2]))

        # "list-any" - to workaround to
        # 'Unsupported operand types for + (List[int] and List[CPubKey])
        la: List[Any] = []

        T2(
            CScript(la + [11] + pubkeys[:12] + [12, OP_CHECKMULTISIG]),
            StandardMultisigScriptInfo(total=12,
                                       required=11,
                                       pubkeys=pubkeys[:12]))
        T2(CScript(la + [15] + pubkeys + [15, OP_CHECKMULTISIG]),
           StandardMultisigScriptInfo(total=15, required=15, pubkeys=pubkeys))

        with self.assertRaises(ValueError):
            # invalid script - extra opcode
            T(
                CScript(
                    [1, pubkeys[0], pubkeys[1], 2, OP_CHECKMULTISIG, OP_DROP]))
        with self.assertRaises(ValueError):
            # invalid pubkey - extra data
            T(
                CScript(
                    [1, pubkeys[0] + b'abc', pubkeys[1], 2, OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            # invalid pubkey - truncated
            T(CScript([1, pubkeys[0], pubkeys[1][:-1], 2, OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            T(CScript([]))
        with self.assertRaises(ValueError):
            T(CScript([b'\x01', pubkeys[0], pubkeys[1], 2, OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            T(CScript([16, pubkeys[0], pubkeys[1], 2, OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            T(CScript(la + [11] + pubkeys[:12] + [b'\x0c', OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            T(CScript(la + [11] + pubkeys + [14, OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            T(CScript(la + [11] + pubkeys + pubkeys + [15, OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            T(CScript([1, pubkeys[0], pubkeys[1], 1, OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            T(CScript(la + [11] + pubkeys[:12] + [11, OP_CHECKMULTISIG]))
        with self.assertRaises(ValueError):
            T(CScript(la + [11] + pubkeys[:12] + [11, OP_CHECKSIG]))
        with self.assertRaises(ValueError):
            T(CScript(la + [11] + pubkeys[:12]))  # short script 1
        with self.assertRaises(ValueError):
            T(CScript(la + [11] + pubkeys[:12] + [11]))  # short script 2
예제 #22
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!')
예제 #23
0
def podle_PrivateKey(priv):
    """Returns a PrivateKey object from a binary string
    """
    return CKey(priv)