コード例 #1
0
def test_spend_p2wpkh(setup_tx_creation):
    #make 3 p2wpkh outputs from 3 privs
    privs = [struct.pack(b'B', x) * 32 + b'\x01' for x in range(1, 4)]
    pubs = [
        bitcoin.privkey_to_pubkey(binascii.hexlify(priv).decode('ascii'))
        for priv in privs
    ]
    scriptPubKeys = [bitcoin.pubkey_to_p2wpkh_script(pub) for pub in pubs]
    addresses = [bitcoin.pubkey_to_p2wpkh_address(pub) for pub in pubs]
    #pay into it
    wallet_service = make_wallets(1, [[3, 0, 0, 0, 0]], 3)[0]['wallet']
    wallet_service.sync_wallet(fast=True)
    amount = 35000000
    p2wpkh_ins = []
    for addr in addresses:
        ins_full = wallet_service.select_utxos(0, amount)
        txid = make_sign_and_push(ins_full,
                                  wallet_service,
                                  amount,
                                  output_addr=addr)
        assert txid
        p2wpkh_ins.append(txid + ":0")
        #wait for mining
        time.sleep(1)
    #random output address
    output_addr = wallet_service.get_internal_addr(1)
    amount2 = amount * 3 - 50000
    outs = [{'value': amount2, 'address': output_addr}]
    tx = bitcoin.mktx(p2wpkh_ins, outs)
    sigs = []
    for i, priv in enumerate(privs):
        # sign each of 3 inputs
        tx = bitcoin.p2wpkh_sign(tx,
                                 i,
                                 binascii.hexlify(priv),
                                 amount,
                                 native=True)
        # check that verify_tx_input correctly validates;
        # to do this, we need to extract the signature and get the scriptCode
        # of this pubkey
        scriptCode = bitcoin.pubkey_to_p2pkh_script(pubs[i])
        witness = bitcoin.deserialize(tx)['ins'][i]['txinwitness']
        assert len(witness) == 2
        assert witness[1] == pubs[i]
        sig = witness[0]
        assert bitcoin.verify_tx_input(tx,
                                       i,
                                       scriptPubKeys[i],
                                       sig,
                                       pubs[i],
                                       scriptCode=scriptCode,
                                       amount=amount)
    txid = jm_single().bc_interface.pushtx(tx)
    assert txid
コード例 #2
0
def test_create_and_sign_psbt_with_legacy(setup_psbt_wallet):
    """ The purpose of this test is to check that we can create and
    then partially sign a PSBT where we own one input and the other input
    is of legacy p2pkh type.
    """
    wallet_service = make_wallets(1, [[1, 0, 0, 0, 0]], 1)[0]['wallet']
    wallet_service.sync_wallet(fast=True)
    utxos = wallet_service.select_utxos(0, bitcoin.coins_to_satoshi(0.5))
    assert len(utxos) == 1
    # create a legacy address and make a payment into it
    legacy_addr = bitcoin.CCoinAddress.from_scriptPubKey(
        bitcoin.pubkey_to_p2pkh_script(bitcoin.privkey_to_pubkey(b"\x01" *
                                                                 33)))
    tx = direct_send(wallet_service,
                     bitcoin.coins_to_satoshi(0.3),
                     0,
                     str(legacy_addr),
                     accept_callback=dummy_accept_callback,
                     info_callback=dummy_info_callback,
                     return_transaction=True)
    assert tx
    # this time we will have one utxo worth <~ 0.7
    my_utxos = wallet_service.select_utxos(0, bitcoin.coins_to_satoshi(0.5))
    assert len(my_utxos) == 1
    # find the outpoint for the legacy address we're spending
    n = -1
    for i, t in enumerate(tx.vout):
        if bitcoin.CCoinAddress.from_scriptPubKey(
                t.scriptPubKey) == legacy_addr:
            n = i
    assert n > -1
    utxos = copy.deepcopy(my_utxos)
    utxos[(tx.GetTxid()[::-1], n)] = {
        "script": legacy_addr.to_scriptPubKey(),
        "value": bitcoin.coins_to_satoshi(0.3)
    }
    outs = [{
        "value": bitcoin.coins_to_satoshi(0.998),
        "address": wallet_service.get_addr(0, 0, 0)
    }]
    tx2 = bitcoin.mktx(list(utxos.keys()), outs)
    spent_outs = wallet_service.witness_utxos_to_psbt_utxos(my_utxos)
    spent_outs.append(tx)
    new_psbt = wallet_service.create_psbt_from_tx(tx2,
                                                  spent_outs,
                                                  force_witness_utxo=False)
    signed_psbt_and_signresult, err = wallet_service.sign_psbt(
        new_psbt.serialize(), with_sign_result=True)
    assert err is None
    signresult, signed_psbt = signed_psbt_and_signresult
    assert signresult.num_inputs_signed == 1
    assert signresult.num_inputs_final == 1
    assert not signresult.is_final
コード例 #3
0
 def pubkey_to_script_code(cls, pubkey):
     """ As per BIP143, the scriptCode for the p2wpkh
     case is "76a914+hash160(pub)+"88ac" as per the
     scriptPubKey of the p2pkh case.
     """
     return btc.pubkey_to_p2pkh_script(pubkey, require_compressed=True)
コード例 #4
0
 def pubkey_to_script(cls, pubkey):
     # this call does not enforce compressed:
     return btc.pubkey_to_p2pkh_script(pubkey)
コード例 #5
0
def main():

    # sets up grpc connection to lnd
    channel = get_secure_channel()

    # note that the 'admin' macaroon already has the required
    # permissions for the walletkit request, so we don't need
    # that third macaroon.
    macaroon, signer_macaroon = get_macaroons(["admin", "signer"])

    # the main stub allows access to the default rpc commands:
    stub = lnrpc.LightningStub(channel)
    # the signer stub allows us to access the rpc for signing
    # transactions on our coins:
    stub_signer = signrpc.SignerStub(channel)
    # we also need a stub for the walletkit rpc to extract
    # public keys for addresses holding coins:
    stub_walletkit = walletrpc.WalletKitStub(channel)

    # Here we start the process to sign a custom tx.
    # 1. List unspent coins, get most recent ones (just an example).
    # 2. Get the pubkeys of those addresses.
    # 3. Get the next unused address in the wallet as destination.
    # 4. Build a transaction, (in future: optionally taking extra
    #    inputs and outputs from elsewhere).
    # 5. Use signOutputRaw rpc to sign the new transaction.
    # 6. Use the walletkit PublishTransaction to publish.

    # Just an example of retrieving basic info, not necessary:
    # Retrieve and display the wallet balance
    response = stub.WalletBalance(ln.WalletBalanceRequest(),
                                  metadata=[('macaroon', macaroon)])
    print("Current on-chain wallet balance: ", response.total_balance)

    inputs = get_our_coins(stub, macaroon) + get_other_coins()

    for inp in inputs:
        # Attach auxiliary data needed to the inputs, for signing.

        # Get the public key of an address
        inp["pubkey"] = stub_walletkit.KeyForAddress(
            walletkit.KeyForAddressRequest(addr_in=inp["utxo"].address),
            metadata=[('macaroon', macaroon)]).raw_key_bytes

        # this data (known as scriptCode in BIP143 parlance)
        # is the pubkeyhash script for this p2wpkh, as is needed
        # to construct the signature hash.
        # **NOTE** This code currently works with bech32 only.
        # TODO update to allow p2sh-p2wpkh in wallet coins, also.
        inp["script"] = btc.pubkey_to_p2pkh_script(inp["pubkey"])

    # We need an output address for the transaction, this is taken from the
    # standard wallet 'new address' request (type 0 is bech32 p2wpkh):
    request = ln.NewAddressRequest(type=0, )
    response = stub.NewAddress(request, metadata=[('macaroon', macaroon)])
    output_address = response.address
    print("Generated new address: ", output_address)

    # Build the raw unsigned transaction
    tx_ins = []
    output_amt = 0
    for inp in inputs:
        tx_ins.append(inp["utxo"].outpoint.txid_str + ":" +
                      str(inp["utxo"].outpoint.output_index))
        output_amt += inp["utxo"].amount_sat

    fee_est = estimate_tx_fee(2, 1, "p2wpkh", 6, stub, macaroon)

    output = {"address": output_address, "value": output_amt - fee_est}
    tx_unsigned = btc.mktx(tx_ins, [output], version=2)
    print(btc.deserialize(tx_unsigned))

    # use SignOutputRaw to sign each input (currently, they are all ours).
    raw_sigs = {}
    for i, inp in enumerate(inputs):
        # KeyDescriptors must contain at least one of the pubkey and the HD path,
        # here we use the latter:
        kd = signer.KeyDescriptor(raw_key_bytes=inp["pubkey"])
        # specify the utxo information for this input into a TxOut:
        sdout = signer.TxOut(value=inp["utxo"].amount_sat,
                             pk_script=unhexlify(inp["utxo"].pk_script))
        # we must pass a list of SignDescriptors; we could batch all into
        # one grpc call if we preferred. The witnessscript field is
        # constructed above as the "script" field in the input dict.
        sds = [
            signer.SignDescriptor(key_desc=kd,
                                  input_index=i,
                                  output=sdout,
                                  witness_script=inp["script"],
                                  sighash=1)
        ]
        req = signer.SignReq(raw_tx_bytes=unhexlify(tx_unsigned),
                             sign_descs=sds)
        # here we make the actual signing request to lnd over grpc:
        response = stub_signer.SignOutputRaw(req,
                                             metadata=[('macaroon',
                                                        signer_macaroon)])
        # note that btcwallet's sign function does not return the sighash byte,
        # it must be added manually:
        raw_sigs[i] = response.raw_sigs[0] + sighash_all_bytes

    # insert the signatures into the relevant inputs in the deserialized tx
    tx_unsigned_deser = btc.deserialize(tx_unsigned)
    for i in range(len(inputs)):
        tx_unsigned_deser["ins"][i]["txinwitness"] = [
            btc.safe_hexlify(raw_sigs[i]),
            btc.safe_hexlify(inputs[i]["pubkey"])
        ]
    print("Signed transaction: \n", tx_unsigned_deser)
    hextx = btc.serialize(tx_unsigned_deser)
    print("Serialized: ", hextx)
    print("You can broadcast this externally e.g. via Bitcoin Core")
コード例 #6
0
 def pubkey_to_script(cls, pubkey):
     return btc.pubkey_to_p2pkh_script(pubkey)
コード例 #7
0
def test_is_snicker_tx(our_input_val, their_input_val, network_fee,
                       script_type, net_transfer):
    our_input = (bytes([1]) * 32, 0)
    their_input = (bytes([2]) * 32, 1)
    assert our_input_val - their_input_val - network_fee > 0
    total_input_amount = our_input_val + their_input_val
    total_output_amount = total_input_amount - network_fee
    receiver_output_amount = their_input_val + net_transfer
    proposer_output_amount = total_output_amount - receiver_output_amount

    # all keys are just made up; only the script type will be checked
    privs = [bytes([i]) * 32 + bytes([1]) for i in range(1, 4)]
    pubs = [btc.privkey_to_pubkey(x) for x in privs]

    if script_type == "p2wpkh":
        spks = [btc.pubkey_to_p2wpkh_script(x) for x in pubs]
    elif script_type == "p2sh-p2wpkh":
        spks = [btc.pubkey_to_p2sh_p2wpkh_script(x) for x in pubs]
    else:
        assert False
    tweaked_addr, our_addr, change_addr = [
        str(btc.CCoinAddress.from_scriptPubKey(x)) for x in spks
    ]
    # now we must construct the three outputs with correct output amounts.
    outputs = [{"address": tweaked_addr, "value": receiver_output_amount}]
    outputs.append({"address": our_addr, "value": receiver_output_amount})
    outputs.append({
        "address": change_addr,
        "value": total_output_amount - 2 * receiver_output_amount
    })
    assert all([x["value"] > 0 for x in outputs])

    # make_shuffled_tx mutates ordering (yuck), work with copies only:
    outputs1 = copy.deepcopy(outputs)
    # version and locktime as currently specified in the BIP
    # for 0/1 version SNICKER. (Note the locktime is partly because
    # of expected delays).
    tx = btc.make_shuffled_tx([our_input, their_input],
                              outputs1,
                              version=2,
                              locktime=0)
    assert btc.is_snicker_tx(tx)

    # construct variants which will be invalid.

    # mixed script types in outputs
    wrong_tweaked_spk = btc.pubkey_to_p2pkh_script(pubs[1])
    wrong_tweaked_addr = str(
        btc.CCoinAddress.from_scriptPubKey(wrong_tweaked_spk))
    outputs2 = copy.deepcopy(outputs)
    outputs2[0] = {
        "address": wrong_tweaked_addr,
        "value": receiver_output_amount
    }
    tx2 = btc.make_shuffled_tx([our_input, their_input],
                               outputs2,
                               version=2,
                               locktime=0)
    assert not btc.is_snicker_tx(tx2)

    # nonequal output amounts
    outputs3 = copy.deepcopy(outputs)
    outputs3[1] = {"address": our_addr, "value": receiver_output_amount - 1}
    tx3 = btc.make_shuffled_tx([our_input, their_input],
                               outputs3,
                               version=2,
                               locktime=0)
    assert not btc.is_snicker_tx(tx3)

    # too few outputs
    outputs4 = copy.deepcopy(outputs)
    outputs4 = outputs4[:2]
    tx4 = btc.make_shuffled_tx([our_input, their_input],
                               outputs4,
                               version=2,
                               locktime=0)
    assert not btc.is_snicker_tx(tx4)

    # too many outputs
    outputs5 = copy.deepcopy(outputs)
    outputs5.append({"address": change_addr, "value": 200000})
    tx5 = btc.make_shuffled_tx([our_input, their_input],
                               outputs5,
                               version=2,
                               locktime=0)
    assert not btc.is_snicker_tx(tx5)

    # wrong nVersion
    tx6 = btc.make_shuffled_tx([our_input, their_input],
                               outputs,
                               version=1,
                               locktime=0)
    assert not btc.is_snicker_tx(tx6)

    # wrong nLockTime
    tx7 = btc.make_shuffled_tx([our_input, their_input],
                               outputs,
                               version=2,
                               locktime=1)
    assert not btc.is_snicker_tx(tx7)