elements/test/functional/feature_tapscript_opcodes.py
2021-08-26 20:28:01 +00:00

147 lines
No EOL
6.1 KiB
Python
Executable file

#!/usr/bin/env python3
# Copyright (c) 2014-2016 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
#
# Test for taproot sighash algorithm with pegins and issuances
from test_framework.key import compute_xonly_pubkey, generate_privkey, sign_schnorr, tweak_add_privkey, tweak_add_pubkey, verify_schnorr
from test_framework.messages import COIN, COutPoint, CTransaction, CTxIn, CTxInWitness, CTxOut, CTxOutValue, CTxOutWitness, FromHex, sha256, uint256_from_str
from test_framework.test_framework import BitcoinTestFramework
from test_framework.script import CScript, OP_1, OP_EQUAL, OP_SWAP, OP_SHA256FINALIZE, OP_SHA256INITIALIZE, OP_SHA256UPDATE, TaprootSignatureHash, taproot_construct, SIGHASH_DEFAULT, SIGHASH_ALL, SIGHASH_NONE, SIGHASH_SINGLE, SIGHASH_ANYONECANPAY
import os
VALID_SIGHASHES_ECDSA = [
SIGHASH_ALL,
SIGHASH_NONE,
SIGHASH_SINGLE,
SIGHASH_ANYONECANPAY + SIGHASH_ALL,
SIGHASH_ANYONECANPAY + SIGHASH_NONE,
SIGHASH_ANYONECANPAY + SIGHASH_SINGLE
]
VALID_SIGHASHES_TAPROOT = [SIGHASH_DEFAULT] + VALID_SIGHASHES_ECDSA
class TapHashPeginTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 1
self.extra_args = [
["-initialfreecoins=2100000000000000",
"-anyonecanspendaremine=1",
"-blindedaddresses=1",
"-validatepegin=0",
"-con_parent_chain_signblockscript=51",
"-parentscriptprefix=75",
"-parent_bech32_hrp=ert",
"-minrelaytxfee=0",
"-maxtxfee=100.0",
]]
def skip_test_if_missing_module(self):
self.skip_if_no_wallet()
def setup_network(self, split=False):
self.setup_nodes()
def create_taproot_utxo(self, scripts = None):
# modify the transaction to add one output that should spend previous taproot
# Create a taproot prevout
addr = self.nodes[0].getnewaddress()
sec = generate_privkey()
pub = compute_xonly_pubkey(sec)[0]
tap = taproot_construct(pub, scripts)
spk = tap.scriptPubKey
# No need to test blinding in this unit test
unconf_addr = self.nodes[0].getaddressinfo(addr)['unconfidential']
raw_tx = self.nodes[0].createrawtransaction([], [{unconf_addr: 1.2}])
# edit spk directly, no way to get new address.
# would need to implement bech32m in python
tx = FromHex(CTransaction(), raw_tx)
tx.vout[0].scriptPubKey = spk
tx.vout[0].nValue = CTxOutValue(12*10**7)
raw_hex = tx.serialize().hex()
fund_tx = self.nodes[0].fundrawtransaction(raw_hex, False, )["hex"]
fund_tx = FromHex(CTransaction(), fund_tx)
# Createrawtransaction might rearrage txouts
prev_vout = None
for i, out in enumerate(fund_tx.vout):
if spk == out.scriptPubKey:
prev_vout = i
tx = self.nodes[0].blindrawtransaction(fund_tx.serialize().hex())
signed_raw_tx = self.nodes[0].signrawtransactionwithwallet(tx)
_txid = self.nodes[0].sendrawtransaction(signed_raw_tx['hex'])
tx = FromHex(CTransaction(), signed_raw_tx['hex'])
tx.rehash()
self.nodes[0].generate(1)
last_blk = self.nodes[0].getblock(self.nodes[0].getbestblockhash())
assert(tx.hash in last_blk['tx'])
return tx, prev_vout, spk, sec, pub, tap
def tapscript_satisfy_test(self, script, inputs = []):
# Create a taproot utxo
scripts = [("s0", script)]
prev_tx, prev_vout, spk, sec, pub, tap = self.create_taproot_utxo(scripts)
# Spend the pegin and taproot tx together
in_total = prev_tx.vout[prev_vout].nValue.getAmount()
fees = 1000
tx = CTransaction()
tx.vin.append(CTxIn(COutPoint(prev_tx.sha256, prev_vout)))
tx.vout.append(CTxOut(nValue = CTxOutValue(in_total - fees), scriptPubKey = spk)) # send back to self
tx.vout.append(CTxOut(CTxOutValue(fees)))
suffix_annex = []
control_block = bytes([tap.leaves["s0"].version + tap.negflag]) + tap.inner_pubkey + tap.leaves["s0"].merklebranch
wit = inputs + [bytes(tap.leaves["s0"].script), control_block] + suffix_annex
tx.wit.vtxinwit.append(CTxInWitness())
tx.wit.vtxinwit[0].scriptWitness.stack = wit
self.nodes[0].sendrawtransaction(hexstring = tx.serialize().hex())
self.nodes[0].generate(1)
last_blk = self.nodes[0].getblock(self.nodes[0].getbestblockhash())
tx.rehash()
assert(tx.hash in last_blk['tx'])
def run_test(self):
self.nodes[0].generate(101)
self.wait_until(lambda: self.nodes[0].getblockcount() == 101, timeout=5)
# Test whether the above test framework is working
self.log.info("Test simple op_1")
self.tapscript_satisfy_test(CScript([OP_1]))
# Test streaming sha256
# All preimages upto len 80 can be filled in a single standard witness element
# Testing only random even len for speedup
for k in range(0, 81, 2):
preimage = os.urandom(k)
hash = sha256(preimage)
self.tapscript_satisfy_test(CScript([OP_SHA256INITIALIZE, OP_SWAP, OP_SHA256FINALIZE, hash, OP_EQUAL]), inputs = [bytes(0), preimage])
# All preimages greater than 80 require Cating the elements
# But this only works till 520 bytes, streaming opcodes can
# be when dealing with more than 520 bytes
# Also adds a test when the sha256 buffer is full
for pre_len in [64*i for i in range(2, 10)] + [100, 520, 521, 800, 1000, 10000]:
preimage = os.urandom(pre_len)
hash = sha256(preimage)
# Split into chunks of 80
wit = [preimage[i:min(i + 80, len(preimage))] for i in range(0, len(preimage), 80)]
script = CScript([OP_SHA256INITIALIZE] + [OP_SWAP, OP_SHA256UPDATE]*(len(wit)-2) + [OP_SWAP, OP_SHA256FINALIZE, hash, OP_EQUAL])
self.tapscript_satisfy_test(script, wit[::-1])
if __name__ == '__main__':
TapHashPeginTest().main()