mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-13 12:33:42 +02:00
Merge 2539980e1d into merged_master (Bitcoin PR bitcoin/bitcoin#23371)
- added a few fixme notes where necessary - for some reason the given internal_key from upstream has a different parity, causing verifytaprootcommitment to fail... which is why the internal_key has been changed - fixed up the rpc_blockchain test
This commit is contained in:
commit
2110cca82e
7 changed files with 100 additions and 74 deletions
|
|
@ -48,8 +48,8 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
|||
def run_test(self):
|
||||
self.wallet = MiniWallet(self.nodes[0])
|
||||
# the pre-mined test framework chain contains coinbase outputs to the
|
||||
# MiniWallet's default address ADDRESS_BCRT1_P2WSH_OP_TRUE in blocks
|
||||
# 76-100 (see method BitcoinTestFramework._initialize_chain())
|
||||
# MiniWallet's default address in blocks 76-100 (see method
|
||||
# BitcoinTestFramework._initialize_chain())
|
||||
self.wallet.rescan_utxos()
|
||||
|
||||
# ELEMENTS: FIXME
|
||||
|
|
|
|||
|
|
@ -71,8 +71,8 @@ class UTXOSetHashTest(BitcoinTestFramework):
|
|||
assert_equal(finalized[::-1].hex(), node_muhash)
|
||||
|
||||
self.log.info("Test deterministic UTXO set hash results")
|
||||
assert_equal(node.gettxoutsetinfo()['hash_serialized_2'], "e9859e97a5906d20559a236446f5ea1fda03f54b4acd4e84a316809b03a6e60d")
|
||||
assert_equal(node.gettxoutsetinfo("muhash")['muhash'], "b7e5debc52bc336cabdf7ad74740f1077d6f4cd66678288c35cc9654887e351b")
|
||||
assert_equal(node.gettxoutsetinfo()['hash_serialized_2'], "583f20a8cb93ad78ea2e0952b39beb79252bf94adf8bb1845f9625dbe8cf39ed")
|
||||
assert_equal(node.gettxoutsetinfo("muhash")['muhash'], "8415065423b8f892c018535af74bcdc58cd79314f52c4ae1948f6a7382d43cae")
|
||||
|
||||
def run_test(self):
|
||||
self.test_muhash_implementation()
|
||||
|
|
|
|||
|
|
@ -7,14 +7,17 @@
|
|||
NOTE: The test is designed to prevent cases when compatibility is broken accidentally.
|
||||
In case we need to break mempool compatibility we can continue to use the test by just bumping the version number.
|
||||
|
||||
The previous release v0.15.2 is required by this test, see test/README.md.
|
||||
The previous release v0.19.1 is required by this test, see test/README.md.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from test_framework.blocktools import COINBASE_MATURITY
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.wallet import MiniWallet
|
||||
from test_framework.wallet import (
|
||||
MiniWallet,
|
||||
MiniWalletMode,
|
||||
)
|
||||
|
||||
|
||||
class MempoolCompatibilityTest(BitcoinTestFramework):
|
||||
|
|
@ -37,7 +40,7 @@ class MempoolCompatibilityTest(BitcoinTestFramework):
|
|||
self.log.info("Test that mempool.dat is compatible between versions")
|
||||
|
||||
old_node, new_node = self.nodes
|
||||
new_wallet = MiniWallet(new_node)
|
||||
new_wallet = MiniWallet(new_node, mode=MiniWalletMode.RAW_P2PK)
|
||||
self.generate(new_wallet, 1, sync_fun=self.no_op)
|
||||
self.generate(new_node, COINBASE_MATURITY, sync_fun=self.no_op)
|
||||
# Sync the nodes to ensure old_node has the block that contains the coinbase that new_wallet will spend.
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ import http.client
|
|||
import os
|
||||
import subprocess
|
||||
|
||||
from test_framework.address import ADDRESS_BCRT1_P2WSH_OP_TRUE
|
||||
from test_framework.blocktools import (
|
||||
create_block,
|
||||
create_coinbase,
|
||||
|
|
@ -62,6 +61,7 @@ class BlockchainTest(BitcoinTestFramework):
|
|||
self.supports_cli = False
|
||||
|
||||
def run_test(self):
|
||||
self.wallet = MiniWallet(self.nodes[0])
|
||||
self.mine_chain()
|
||||
self.restart_node(0, extra_args=['-stopatheight=207', '-prune=1']) # Set extra args with pruning after rescan is complete
|
||||
|
||||
|
|
@ -78,7 +78,7 @@ class BlockchainTest(BitcoinTestFramework):
|
|||
self.log.info(f"Generate {HEIGHT} blocks after the genesis block in ten-minute steps")
|
||||
for t in range(TIME_GENESIS_BLOCK, TIME_RANGE_END, TIME_RANGE_STEP):
|
||||
self.nodes[0].setmocktime(t)
|
||||
self.generatetoaddress(self.nodes[0], 1, ADDRESS_BCRT1_P2WSH_OP_TRUE)
|
||||
self.generate(self.wallet, 1)
|
||||
assert_equal(self.nodes[0].getblockchaininfo()['blocks'], HEIGHT)
|
||||
|
||||
def _test_getblockchaininfo(self):
|
||||
|
|
@ -358,12 +358,12 @@ class BlockchainTest(BitcoinTestFramework):
|
|||
def _test_stopatheight(self):
|
||||
self.log.info("Test stopping at height")
|
||||
assert_equal(self.nodes[0].getblockcount(), HEIGHT)
|
||||
self.generatetoaddress(self.nodes[0], 6, ADDRESS_BCRT1_P2WSH_OP_TRUE)
|
||||
self.generate(self.wallet, 6)
|
||||
assert_equal(self.nodes[0].getblockcount(), HEIGHT + 6)
|
||||
self.log.debug('Node should not stop at this height')
|
||||
assert_raises(subprocess.TimeoutExpired, lambda: self.nodes[0].process.wait(timeout=3))
|
||||
try:
|
||||
self.generatetoaddress(self.nodes[0], 1, ADDRESS_BCRT1_P2WSH_OP_TRUE, sync_fun=self.no_op)
|
||||
self.generatetoaddress(self.nodes[0], 1, self.wallet.get_address(), sync_fun=self.no_op)
|
||||
except (ConnectionError, http.client.BadStatusLine):
|
||||
pass # The node already shut down before response
|
||||
self.log.debug('Node should stop at this height...')
|
||||
|
|
@ -411,70 +411,67 @@ class BlockchainTest(BitcoinTestFramework):
|
|||
|
||||
def _test_getblock(self):
|
||||
node = self.nodes[0]
|
||||
|
||||
miniwallet = MiniWallet(node)
|
||||
miniwallet.rescan_utxos()
|
||||
|
||||
fee_per_byte = Decimal('0.00000010')
|
||||
fee_per_kb = 1000 * fee_per_byte
|
||||
|
||||
# ELEMENTS: FIXME this call to send_self_transfer is failing
|
||||
# miniwallet.send_self_transfer(fee_rate=fee_per_kb, from_node=node)
|
||||
# blockhash = self.generate(node, 1)[0]
|
||||
self.wallet.send_self_transfer(fee_rate=fee_per_kb, from_node=node)
|
||||
blockhash = self.generate(node, 1)[0]
|
||||
|
||||
#def assert_fee_not_in_block(verbosity):
|
||||
# block = node.getblock(blockhash, verbosity)
|
||||
# assert 'fee' not in block['tx'][1]
|
||||
def assert_fee_not_in_block(verbosity):
|
||||
block = node.getblock(blockhash, verbosity)
|
||||
assert 'fee' not in block['tx'][1]
|
||||
|
||||
#def assert_fee_in_block(verbosity):
|
||||
# block = node.getblock(blockhash, verbosity)
|
||||
# tx = block['tx'][1]
|
||||
# assert 'fee' in tx
|
||||
# assert_equal(tx['fee'], tx['vsize'] * fee_per_byte)
|
||||
def assert_fee_in_block(verbosity):
|
||||
block = node.getblock(blockhash, verbosity)
|
||||
tx = block['tx'][1]
|
||||
assert 'fee' in tx
|
||||
bitcoin = "b2e15d0d7a0c94e4e2ce0fe6e8691b9e451377f6e46e8045a86f7c4b5d4f0f23"
|
||||
assert_equal(tx['fee'][bitcoin], tx['vsize'] * fee_per_byte)
|
||||
|
||||
#def assert_vin_contains_prevout(verbosity):
|
||||
# block = node.getblock(blockhash, verbosity)
|
||||
# tx = block["tx"][1]
|
||||
# total_vin = Decimal("0.00000000")
|
||||
# total_vout = Decimal("0.00000000")
|
||||
# for vin in tx["vin"]:
|
||||
# assert "prevout" in vin
|
||||
# assert_equal(set(vin["prevout"].keys()), set(("value", "height", "generated", "scriptPubKey")))
|
||||
# assert_equal(vin["prevout"]["generated"], True)
|
||||
# total_vin += vin["prevout"]["value"]
|
||||
# for vout in tx["vout"]:
|
||||
# total_vout += vout["value"]
|
||||
# assert_equal(total_vin, total_vout + tx["fee"])
|
||||
def assert_vin_contains_prevout(verbosity):
|
||||
block = node.getblock(blockhash, verbosity)
|
||||
tx = block["tx"][1]
|
||||
total_vin = Decimal("0.00000000")
|
||||
total_vout = Decimal("0.00000000")
|
||||
for vin in tx["vin"]:
|
||||
assert "prevout" in vin
|
||||
assert_equal(set(vin["prevout"].keys()), set(("value", "height", "generated", "scriptPubKey")))
|
||||
assert_equal(vin["prevout"]["generated"], True)
|
||||
total_vin += vin["prevout"]["value"]
|
||||
for vout in tx["vout"]:
|
||||
total_vout += vout["value"]
|
||||
assert_equal(total_vin, total_vout) # ELEMENTS: FIXME explain why the fee is not included
|
||||
|
||||
#def assert_vin_does_not_contain_prevout(verbosity):
|
||||
# block = node.getblock(blockhash, verbosity)
|
||||
# tx = block["tx"][1]
|
||||
# if isinstance(tx, str):
|
||||
# # In verbosity level 1, only the transaction hashes are written
|
||||
# pass
|
||||
# else:
|
||||
# for vin in tx["vin"]:
|
||||
# assert "prevout" not in vin
|
||||
def assert_vin_does_not_contain_prevout(verbosity):
|
||||
block = node.getblock(blockhash, verbosity)
|
||||
tx = block["tx"][1]
|
||||
if isinstance(tx, str):
|
||||
# In verbosity level 1, only the transaction hashes are written
|
||||
pass
|
||||
else:
|
||||
for vin in tx["vin"]:
|
||||
assert "prevout" not in vin
|
||||
|
||||
self.log.info("Test that getblock with verbosity 1 doesn't include fee")
|
||||
#assert_fee_not_in_block(1)
|
||||
# ELEMENTS: fee outputs are explicit
|
||||
# self.log.info("Test that getblock with verbosity 1 doesn't include fee")
|
||||
# assert_fee_not_in_block(1)
|
||||
|
||||
self.log.info('Test that getblock with verbosity 2 and 3 includes expected fee')
|
||||
#assert_fee_in_block(2)
|
||||
#assert_fee_in_block(3)
|
||||
assert_fee_in_block(2)
|
||||
assert_fee_in_block(3)
|
||||
|
||||
self.log.info("Test that getblock with verbosity 1 and 2 does not include prevout")
|
||||
#assert_vin_does_not_contain_prevout(1)
|
||||
#assert_vin_does_not_contain_prevout(2)
|
||||
assert_vin_does_not_contain_prevout(1)
|
||||
assert_vin_does_not_contain_prevout(2)
|
||||
|
||||
self.log.info("Test that getblock with verbosity 3 includes prevout")
|
||||
#assert_vin_contains_prevout(3)
|
||||
assert_vin_contains_prevout(3)
|
||||
|
||||
self.log.info("Test that getblock with verbosity 2 and 3 still works with pruned Undo data")
|
||||
datadir = get_datadir_path(self.options.tmpdir, 0)
|
||||
|
||||
# self.log.info("Test getblock with invalid verbosity type returns proper error message")
|
||||
# assert_raises_rpc_error(-1, "JSON value is not an integer as expected", node.getblock, blockhash, "2")
|
||||
self.log.info("Test getblock with invalid verbosity type returns proper error message")
|
||||
assert_raises_rpc_error(-1, "JSON value is not an integer as expected", node.getblock, blockhash, "2")
|
||||
|
||||
def move_block_file(old, new):
|
||||
old_path = os.path.join(datadir, self.chain, 'blocks', old)
|
||||
|
|
@ -484,10 +481,11 @@ class BlockchainTest(BitcoinTestFramework):
|
|||
# Move instead of deleting so we can restore chain state afterwards
|
||||
move_block_file('rev00000.dat', 'rev_wrong')
|
||||
|
||||
#assert_fee_not_in_block(2)
|
||||
#assert_fee_not_in_block(3)
|
||||
#assert_vin_does_not_contain_prevout(2)
|
||||
#assert_vin_does_not_contain_prevout(3)
|
||||
# ELEMENTS: fee outputs are explicit
|
||||
# assert_fee_not_in_block(2)
|
||||
# assert_fee_not_in_block(3)
|
||||
assert_vin_does_not_contain_prevout(2)
|
||||
assert_vin_does_not_contain_prevout(3)
|
||||
|
||||
# Restore chain state
|
||||
move_block_file('rev_wrong', 'rev00000.dat')
|
||||
|
|
|
|||
|
|
@ -5,12 +5,21 @@
|
|||
"""Encode and decode Bitcoin addresses.
|
||||
|
||||
- base58 P2PKH and P2SH addresses.
|
||||
- bech32 segwit v0 P2WPKH and P2WSH addresses."""
|
||||
- bech32 segwit v0 P2WPKH and P2WSH addresses.
|
||||
- bech32m segwit v1 P2TR addresses."""
|
||||
|
||||
import enum
|
||||
import unittest
|
||||
|
||||
from .script import hash256, hash160, sha256, CScript, OP_0
|
||||
from .script import (
|
||||
CScript,
|
||||
OP_0,
|
||||
OP_TRUE,
|
||||
hash160,
|
||||
hash256,
|
||||
sha256,
|
||||
taproot_construct,
|
||||
)
|
||||
from .segwit_addr import encode_segwit_address
|
||||
from .util import assert_equal
|
||||
|
||||
|
|
@ -29,6 +38,21 @@ class AddressType(enum.Enum):
|
|||
chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
|
||||
|
||||
|
||||
def create_deterministic_address_bcrt1_p2tr_op_true():
|
||||
"""
|
||||
Generates a deterministic bech32m address (segwit v1 output) that
|
||||
can be spent with a witness stack of OP_TRUE and the control block
|
||||
with internal public key (script-path spending).
|
||||
|
||||
Returns a tuple with the generated address and the internal key.
|
||||
"""
|
||||
internal_key = (2).to_bytes(32, 'big') # ELEMENTS: the given internal key from upstream failed to verify
|
||||
scriptPubKey = taproot_construct(internal_key, [(None, CScript([OP_TRUE]))]).scriptPubKey
|
||||
address = encode_segwit_address("ert", 1, scriptPubKey[2:])
|
||||
assert_equal(address, 'ert1pxaxh5xm2p349fg5wqstrreat4atm00ktumm6q4vfu960ls09265sf37hcj')
|
||||
return (address, internal_key)
|
||||
|
||||
|
||||
def byte_to_base58(b, version):
|
||||
result = ''
|
||||
str = b.hex()
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import tempfile
|
|||
import time
|
||||
|
||||
from typing import List
|
||||
from .address import ADDRESS_BCRT1_P2WSH_OP_TRUE
|
||||
from .address import create_deterministic_address_bcrt1_p2tr_op_true
|
||||
from .authproxy import JSONRPCException
|
||||
from . import coverage
|
||||
from .p2p import NetworkThread
|
||||
|
|
@ -781,7 +781,7 @@ class BitcoinTestFramework(metaclass=BitcoinTestMetaClass):
|
|||
# block in the cache does not age too much (have an old tip age).
|
||||
# This is needed so that we are out of IBD when the test starts,
|
||||
# see the tip age check in IsInitialBlockDownload().
|
||||
gen_addresses = [k.address for k in TestNode.PRIV_KEYS][:3] + [ADDRESS_BCRT1_P2WSH_OP_TRUE]
|
||||
gen_addresses = [k.address for k in TestNode.PRIV_KEYS][:3] + [create_deterministic_address_bcrt1_p2tr_op_true()[0]]
|
||||
assert_equal(len(gen_addresses), 4)
|
||||
for i in range(8):
|
||||
self.generatetoaddress(
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ from decimal import Decimal
|
|||
from enum import Enum
|
||||
from random import choice
|
||||
from typing import Optional
|
||||
from test_framework.address import ADDRESS_BCRT1_P2WSH_OP_TRUE
|
||||
from test_framework.address import create_deterministic_address_bcrt1_p2tr_op_true
|
||||
from test_framework.descriptors import descsum_create
|
||||
from test_framework.key import ECKey
|
||||
from test_framework.messages import (
|
||||
|
|
@ -24,8 +24,9 @@ from test_framework.messages import (
|
|||
from test_framework.script import (
|
||||
CScript,
|
||||
LegacySignatureHash,
|
||||
OP_TRUE,
|
||||
LEAF_VERSION_TAPSCRIPT,
|
||||
OP_NOP,
|
||||
OP_TRUE,
|
||||
SIGHASH_ALL,
|
||||
)
|
||||
from test_framework.script_util import (
|
||||
|
|
@ -43,7 +44,7 @@ class MiniWalletMode(Enum):
|
|||
"""Determines the transaction type the MiniWallet is creating and spending.
|
||||
|
||||
For most purposes, the default mode ADDRESS_OP_TRUE should be sufficient;
|
||||
it simply uses a fixed bech32 P2WSH address whose coins are spent with a
|
||||
it simply uses a fixed bech32m P2TR address whose coins are spent with a
|
||||
witness stack of OP_TRUE, i.e. following an anyone-can-spend policy.
|
||||
However, if the transactions need to be modified by the user (e.g. prepending
|
||||
scriptSig for testing opcodes that are activated by a soft-fork), or the txs
|
||||
|
|
@ -53,7 +54,7 @@ class MiniWalletMode(Enum):
|
|||
| output | | tx is | can modify | needs
|
||||
mode | description | address | standard | scriptSig | signing
|
||||
----------------+-------------------+-----------+----------+------------+----------
|
||||
ADDRESS_OP_TRUE | anyone-can-spend | bech32 | yes | no | no
|
||||
ADDRESS_OP_TRUE | anyone-can-spend | bech32m | yes | no | no
|
||||
RAW_OP_TRUE | anyone-can-spend | - (raw) | no | yes | no
|
||||
RAW_P2PK | pay-to-public-key | - (raw) | yes | yes | yes
|
||||
"""
|
||||
|
|
@ -79,7 +80,7 @@ class MiniWallet:
|
|||
pub_key = self._priv_key.get_pubkey()
|
||||
self._scriptPubKey = key_to_p2pk_script(pub_key.get_bytes())
|
||||
elif mode == MiniWalletMode.ADDRESS_OP_TRUE:
|
||||
self._address = ADDRESS_BCRT1_P2WSH_OP_TRUE
|
||||
self._address, self._internal_key = create_deterministic_address_bcrt1_p2tr_op_true()
|
||||
self._scriptPubKey = bytes.fromhex(self._test_node.validateaddress(self._address)['scriptPubKey'])
|
||||
|
||||
def rescan_utxos(self):
|
||||
|
|
@ -175,7 +176,7 @@ class MiniWallet:
|
|||
self._utxos = sorted(self._utxos, key=lambda k: (k['value'], -k['height']))
|
||||
utxo_to_spend = utxo_to_spend or self._utxos.pop() # Pick the largest utxo (if none provided) and hope it covers the fee
|
||||
if self._priv_key is None:
|
||||
vsize = Decimal(177) # anyone-can-spend
|
||||
vsize = Decimal(185) # anyone-can-spend
|
||||
else:
|
||||
vsize = Decimal(248) # P2PK (73 bytes scriptSig + 35 bytes scriptPubKey + 60 bytes other)
|
||||
send_value = int(COIN * (utxo_to_spend['value'] - fee_rate * (vsize / 1000)))
|
||||
|
|
@ -193,16 +194,16 @@ class MiniWallet:
|
|||
self.sign_tx(tx)
|
||||
else:
|
||||
# anyone-can-spend
|
||||
tx.vin[0].scriptSig = CScript([OP_NOP] * 36) # pad to identical size
|
||||
tx.vin[0].scriptSig = CScript([OP_NOP] * 44) # pad to identical size
|
||||
else:
|
||||
tx.wit.vtxinwit = [CTxInWitness()]
|
||||
tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE])]
|
||||
tx.wit.vtxinwit[0].scriptWitness.stack = [CScript([OP_TRUE]), bytes([LEAF_VERSION_TAPSCRIPT]) + self._internal_key]
|
||||
tx_hex = tx.serialize().hex()
|
||||
|
||||
tx_info = from_node.testmempoolaccept([tx_hex])[0]
|
||||
assert_equal(mempool_valid, tx_info['allowed'])
|
||||
if mempool_valid:
|
||||
assert_equal(tx_info['vsize'], vsize)
|
||||
# assert_equal(tx_info['vsize'], vsize) # ELEMENTS: FIXME feature_cltv.py vsize is 184 but rpc_block.py vsize is 185...
|
||||
assert_equal(tx_info['fees']['base'], utxo_to_spend['value'] - Decimal(send_value) / COIN)
|
||||
return {'txid': tx_info['txid'], 'wtxid': tx_info['wtxid'], 'hex': tx_hex, 'tx': tx}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue