Skip to content

Commit ddf712e

Browse files
committed
test: added exact annex padding test for simplicity spends
1 parent 85e1a75 commit ddf712e

File tree

3 files changed

+147
-0
lines changed

3 files changed

+147
-0
lines changed

src/test/transaction_tests.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ static std::map<std::string, unsigned int> mapFlagNames = {
6565
{std::string("DISCOURAGE_OP_SUCCESS"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS},
6666
{std::string("DISCOURAGE_UPGRADABLE_TAPROOT_VERSION"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION},
6767
{std::string("SIMPLICITY"), (unsigned int)SCRIPT_VERIFY_SIMPLICITY},
68+
{std::string("ANNEX_PADDING"), (unsigned int)SCRIPT_VERIFY_ANNEX_PADDING},
6869
};
6970

7071
unsigned int ParseScriptFlags(std::string strFlags)
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) 2021 The Bitcoin Core developers
3+
# Distributed under the MIT software license, see the accompanying
4+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
from decimal import Decimal
6+
from io import BytesIO
7+
8+
from test_framework.messages import CTransaction, CTxInWitness
9+
from test_framework.test_framework import BitcoinTestFramework
10+
from test_framework.util import assert_equal, assert_raises_rpc_error
11+
12+
13+
class AnnexPaddingTest(BitcoinTestFramework):
14+
"""Test exact annex padding for Simplicity spends."""
15+
16+
def set_test_params(self):
17+
self.setup_clean_chain = True
18+
self.num_nodes = 1
19+
self.extra_args = [["-evbparams=simplicity:-1:::"]] * self.num_nodes
20+
21+
def skip_test_if_missing_module(self):
22+
self.skip_if_no_wallet()
23+
24+
def run_test(self):
25+
self.log.info("Check that Simplicity is active")
26+
simplicity = self.nodes[0].getdeploymentinfo()["deployments"]["simplicity"]
27+
assert simplicity["active"]
28+
self.generate(self.nodes[0], 101)
29+
30+
utxo = self.nodes[0].listunspent()[0]
31+
assert_equal(utxo["amount"], Decimal("50.00000000"))
32+
fee = Decimal("0.00001000")
33+
amount = utxo["amount"] - fee
34+
35+
# this elementsregtest address is generated from the "hash loop" SimplicityHL template
36+
addr = "ert1pzp4xccn92zvhh44z9qwh3ap3jnv677ympuaafmyv4urgfrp2lafsdap5ha"
37+
# ---
38+
# fn hash_counter_8(ctx: Ctx8, unused: (), byte: u8) -> Either<u256, Ctx8> {
39+
# let new_ctx: Ctx8 = jet::sha_256_ctx_8_add_1(ctx, byte);
40+
# match jet::all_8(byte) {
41+
# true => Left(jet::sha_256_ctx_8_finalize(new_ctx)),
42+
# false => Right(new_ctx),
43+
# }
44+
# }
45+
# fn main() {
46+
# // Hash bytes 0x00 to 0xff
47+
# let ctx: Ctx8 = jet::sha_256_ctx_8_init();
48+
# let out: Either<u256, Ctx8> = for_while::<hash_counter_8>(ctx, ());
49+
# let expected: u256 = 0x40aff2e9d2d8922e47afd4648e6967497158785fbd1da870e7110266bf944880;
50+
# assert!(jet::eq_256(expected, unwrap_left::<Ctx8>(out)));
51+
# }
52+
# ---
53+
54+
self.log.info("Fund the contract address")
55+
raw = self.nodes[0].createrawtransaction([{"txid": utxo["txid"], "vout": utxo["vout"]}], [{addr: amount}, {"fee": fee}])
56+
signed = self.nodes[0].signrawtransactionwithwallet(raw)
57+
assert signed["complete"]
58+
txid = self.nodes[0].sendrawtransaction(signed["hex"])
59+
self.generate(self.nodes[0], 1)
60+
61+
in_witness = CTxInWitness()
62+
simplicity_witness = ""
63+
simplicity_program = "e8144eac81081420c48a0f9128a0590da107248150b21b79b8118720e30e3e070a85a02d8370c41c920542c86e2341c920542c86e2a36e30e3f0b30e3f0e38542cc2d6371b1b8e4c39071871f038542d016c1b906839240a8590dc8a41c920542c86e489b71871f90461c7e429c2a16616b1b93a839240a8590dca441c920542c86e559b71871f93861c7e4f9c2a16616b1b96e6e3430e3f204c38fc8438542cc2d6373066e618c39071871f038542d016c1b99041c70b06aa0420507cb3650420506e678e2b5620a203801a00dc0708038980e33039001390ac5f8bdd59a0d0ed8d3bb22cb0ef50f71e3a577040de5bfe99608095e7d53356765e430b9101722c0661c40cc0e4804e4a9792a2e4b85c9a01907681901c9f03958139625e588b966172d80641e0c064072ec273005e6005cc105cc280c83c380c80e6280e6600e694273545e6a85cd605cd780c83c5006407368139b92f3722e6ec2e6f80641e30032039c3039d109ceb179d6173b0173b60320f1c81901cf004e790bcf38b9e60b9ea01907902064073d840a136940aff2e9d2d8922e47afd4648e6967497158785fbd1da870e7110266bf944880042050831061c9160366ce8867390b3cffedf1a67f35f4e6e69c39210d09fddb9189a14c225a77e6c262e1806006616b66dd008c0212283f4060201c180e180740780"
64+
cmr = "988c6d7a1c50012028523debc8ec575ce96920c46a45f663051aa3309f6fc539"
65+
control_block = "be50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0"
66+
67+
self.log.info("Try to spend without no annex")
68+
addr = self.nodes[0].getnewaddress(address_type="bech32")
69+
fee = Decimal("0.00003000")
70+
raw = self.nodes[0].createrawtransaction([{"txid": txid, "vout": 0}], [{addr: amount - fee}, {"fee": fee}])
71+
ctx = CTransaction()
72+
ctx.deserialize(BytesIO(bytes.fromhex(raw)))
73+
74+
in_witness.scriptWitness.stack = [
75+
bytes.fromhex(simplicity_witness),
76+
bytes.fromhex(simplicity_program),
77+
bytes.fromhex(cmr),
78+
bytes.fromhex(control_block),
79+
]
80+
ctx.wit.vtxinwit.append(in_witness)
81+
raw = ctx.serialize().hex()
82+
assert_raises_rpc_error(-26, "Program's execution cost could exceed budget", self.nodes[0].sendrawtransaction, raw)
83+
84+
EXACT_PADDING = 7423
85+
# FIXME: investigate why rust-simplicity `Cost::get_padding` gives exact padding as 7426
86+
# https://github.com/BlockstreamResearch/rust-simplicity/blob/d28440bc0c6be333aa84fa441844541c14dbb563/src/analysis.rs#L148
87+
88+
self.log.info("Try to spend with non-zero padded annex")
89+
annex = [0x50] + [0x01] * EXACT_PADDING
90+
in_witness.scriptWitness.stack = [
91+
bytes.fromhex(simplicity_witness),
92+
bytes.fromhex(simplicity_program),
93+
bytes.fromhex(cmr),
94+
bytes.fromhex(control_block),
95+
bytes(annex),
96+
]
97+
ctx.wit.vtxinwit[0] = in_witness
98+
raw = ctx.serialize().hex()
99+
assert_raises_rpc_error(-26, "Simplicity annex padding must be all zeros", self.nodes[0].sendrawtransaction, raw)
100+
101+
self.log.info("Try to spend with under-padded annex")
102+
annex = [0x50] + [0x00] * (EXACT_PADDING - 1)
103+
in_witness.scriptWitness.stack = [
104+
bytes.fromhex(simplicity_witness),
105+
bytes.fromhex(simplicity_program),
106+
bytes.fromhex(cmr),
107+
bytes.fromhex(control_block),
108+
bytes(annex),
109+
]
110+
ctx.wit.vtxinwit[0] = in_witness
111+
raw = ctx.serialize().hex()
112+
assert_raises_rpc_error(-26, "Program's execution cost could exceed budget", self.nodes[0].sendrawtransaction, raw)
113+
114+
self.log.info("Try to spend with over-padded annex")
115+
annex = [0x50] + [0x00] * (EXACT_PADDING + 1)
116+
in_witness.scriptWitness.stack = [
117+
bytes.fromhex(simplicity_witness),
118+
bytes.fromhex(simplicity_program),
119+
bytes.fromhex(cmr),
120+
bytes.fromhex(control_block),
121+
bytes(annex),
122+
]
123+
ctx.wit.vtxinwit[0] = in_witness
124+
raw = ctx.serialize().hex()
125+
assert_raises_rpc_error(-26, "Program's budget is too large", self.nodes[0].sendrawtransaction, raw)
126+
127+
self.log.info("Spend with exact padded annex")
128+
annex = [0x50] + [0x00] * EXACT_PADDING
129+
in_witness.scriptWitness.stack = [
130+
bytes.fromhex(simplicity_witness),
131+
bytes.fromhex(simplicity_program),
132+
bytes.fromhex(cmr),
133+
bytes.fromhex(control_block),
134+
bytes(annex),
135+
]
136+
ctx.wit.vtxinwit[0] = in_witness
137+
raw = ctx.serialize().hex()
138+
txid = self.nodes[0].sendrawtransaction(raw)
139+
self.generate(self.nodes[0], 1)
140+
tx = self.nodes[0].gettransaction(txid)
141+
assert_equal(tx["confirmations"], 1)
142+
143+
144+
if __name__ == "__main__":
145+
AnnexPaddingTest().main()

test/functional/test_runner.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,7 @@
373373
'feature_help.py',
374374
'feature_shutdown.py',
375375
'p2p_ibd_txrelay.py',
376+
'mempool_annex_padding.py',
376377
'feature_blockfilterindex_prune.py'
377378
# Don't append tests at the end to avoid merge conflicts
378379
# Put them in a random line within the section that fits their approximate run-time

0 commit comments

Comments
 (0)