Skip to content

Commit fffec67

Browse files
committed
Merge #288: Introduce bitcoin-consensus-encoding 1.0 and implement traits for Transaction and subtypes
5eb4f8d add fuzztest for equivalence between new and old transaction decoding (Andrew Poelstra) faf6ab5 implement Encode/Decode for Transaction (Andrew Poelstra) b7b5886 implement Encode/Decode for TxInWitness (Andrew Poelstra) d72094a implement Encode/Decode for TxIn (Andrew Poelstra) 02ab70d implement Encode/Decode for TxOutWitness (Andrew Poelstra) 28a3d6d implement Encode/Decode for TxOut (Andrew Poelstra) a61cbab transaction: introduce PeginWitness type (Andrew Poelstra) 0b8fa43 transaction: strongly type script_witness (Andrew Poelstra) 9f82b4b serde: remove impls for PSET and Transaction (Andrew Poelstra) c13d392 rename transaction to transaction/mod.rs (Andrew Poelstra) c7e16cd confidential: implement Encode and Decode for rangeproof/surjectionproof (Andrew Poelstra) aacd081 confidential: implement Encode/Decode for confidential commitments (Andrew Poelstra) 6baad08 hashes: remove deprecated macro (Andrew Poelstra) dca9bb0 update bitcoin dependency to 0.32.102, hashes to 1.1 (Andrew Poelstra) Pull request description: This is a big PR. Please forgive me. The goal is to replace the `Encodable` and `Decodable` traits from rust-elements with the new [`Encode` and `Decode` traits from bitcoin-consensus-encoding](https://docs.rs/bitcoin-consensus-encoding/latest/bitcoin_consensus_encoding/index.html). There are many benefits to this: * Unlike the old rust-bitcoin traits, these ones are designed to be used by the general public for wire encoding formats, so we don't need to create our own duplicate crate and add a bunch of shim implementations; we can also write generic code that works between Bitcoin and Elements... * ...but we largely won't need to, because `bitcoin-consensus-encoding` ships with functions for decoding/encoding to `std::io` types, unbuffered `std::io` types, Vecs/slices, and hex strings, while `bitcoin-hashes` ships with functions for encoding into hash engines, and `bitcoin-io` with functions for decoding/encoding into `bitcoin::io` types. * These traits use the "sans-IO paradigm", meaning that they can be used in no-std environments without the need for shim crates like `core2` (deleted from crates.io) or `bitcoin-io`, whose types tend to pollute every codebase that touches them. * The traits also require no allocators: it is easy to encode even complex objects like `Transaction` without needing to copy any data into auxliary buffers, and some for decoding. If an object does not itself hold a `Box` or `Vec` it should be possible to encode/decode it completely allocator-free. Again, without shim crates like `ArrayVec` or whatever. Coupled with allocator-free encoders like `rust-bech32`, it is now possible to write arbitrary error correcting codes and encode bitcoin/elements types into them without needing any auxiliary buffers * Because these traits are defined by a stable 1.0 crate, after *years* of the rust-bitcoin ecosystem fighting over API breakage and promising not to 2.0 basic stuff, they should remain stable across rust-elements versions. Since rust-elements is far from stable, this should make upgrades *much* easier since it will be increasingly possible to interoperate multiple versions of it. * It paves the way for us to replace `Sequence` and `LockTime` with `bitcoin-units 1.0` types; this may invite type confusion between "bitcoin locktimes" and "elements locktimes" but in exchange we get stable types that have had years of bugfixes and API improvements. (And locktimes in Bitcoin, and therefore Elements, are notoriously fickle and hand to use correctly. Every API improvement we shipped found bugs in rust-miniscript.) * Along the way, I made many API improvements to rust-elements that aren't directly related to encoding, but without which it was quite difficult to implement. These increase the size of this PR but they're all changes that need to be made eventually. ACKs for top commit: delta1: ACK 5eb4f8d; tested locally Tree-SHA512: 2796f84295db4bf784e45a672eb108ece4406982fd897cd3dfc218d93d4b345ce6c742a7e9b6fef1e3ecaf48ec64f21fb5a10c20eeed9dc744a66d088330220e
2 parents bf066e2 + 5eb4f8d commit fffec67

34 files changed

Lines changed: 4601 additions & 591 deletions

Cargo-recent.lock

Lines changed: 34 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,11 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
2222

2323
[[package]]
2424
name = "base58ck"
25-
version = "0.1.0"
25+
version = "0.1.101"
2626
source = "registry+https://github.com/rust-lang/crates.io-index"
27-
checksum = "2c8d66485a3a2ea485c1913c4572ce0256067a5377ac8c75c4960e1cda98605f"
27+
checksum = "365c0acd5b2e8dd0111a46c4faea83fb3cfb6e39a49a7c73a06e090db7b2eff0"
2828
dependencies = [
29-
"bitcoin-internals 0.3.0",
30-
"bitcoin_hashes 0.14.0",
29+
"bitcoin_hashes 0.14.101",
3130
]
3231

3332
[[package]]
@@ -59,52 +58,48 @@ dependencies = [
5958

6059
[[package]]
6160
name = "bitcoin"
62-
version = "0.32.6"
61+
version = "0.32.102"
6362
source = "registry+https://github.com/rust-lang/crates.io-index"
64-
checksum = "ad8929a18b8e33ea6b3c09297b687baaa71fb1b97353243a3f1029fad5c59c5b"
63+
checksum = "bb0ce8bd5baaa0d303a19915a6d93afed161f528654e42da2a7a97d05c59499a"
6564
dependencies = [
6665
"base58ck",
6766
"base64 0.21.7",
6867
"bech32",
69-
"bitcoin-internals 0.3.0",
68+
"bitcoin-consensus-encoding",
7069
"bitcoin-io",
7170
"bitcoin-units",
72-
"bitcoin_hashes 0.14.0",
73-
"hex-conservative 0.2.1",
71+
"bitcoin_hashes 0.14.101",
72+
"hex-conservative 0.2.2",
7473
"hex_lit",
7574
"secp256k1",
7675
"serde",
7776
]
7877

7978
[[package]]
8079
name = "bitcoin-consensus-encoding"
81-
version = "1.0.0"
82-
source = "registry+https://github.com/rust-lang/crates.io-index"
83-
checksum = "b2d6094e2a1ba3c93b5a596fe5a10d1a10c3c6e06785cde89f693a044c01aa40"
84-
dependencies = [
85-
"bitcoin-internals 0.5.0",
86-
]
87-
88-
[[package]]
89-
name = "bitcoin-internals"
90-
version = "0.3.0"
80+
version = "1.1.0"
9181
source = "registry+https://github.com/rust-lang/crates.io-index"
92-
checksum = "30bdbe14aa07b06e6cfeffc529a1f099e5fbe249524f8125358604df99a4bed2"
82+
checksum = "207311705279250ba465076a1bac4b1ac982855fff73fc5f67e22158ac58cdc9"
9383
dependencies = [
84+
"bitcoin-internals",
85+
"hex-conservative 1.1.0",
9486
"serde",
9587
]
9688

9789
[[package]]
9890
name = "bitcoin-internals"
99-
version = "0.5.0"
91+
version = "0.6.0"
10092
source = "registry+https://github.com/rust-lang/crates.io-index"
101-
checksum = "a30a22d1f112dde8e16be7b45c63645dc165cef254f835b3e1e9553e485cfa64"
93+
checksum = "d573f4cf32996a8dce612e4348cece65a241f1882ed594047c9ba348e8869fa5"
10294

10395
[[package]]
10496
name = "bitcoin-io"
105-
version = "0.1.3"
97+
version = "0.1.101"
10698
source = "registry+https://github.com/rust-lang/crates.io-index"
107-
checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf"
99+
checksum = "bb5de036369d1ac59d3c1819ebc4d850f89466f5401c571a285b6ed564a4cb78"
100+
dependencies = [
101+
"bitcoin-consensus-encoding",
102+
]
108103

109104
[[package]]
110105
name = "bitcoin-private"
@@ -114,33 +109,33 @@ checksum = "73290177011694f38ec25e165d0387ab7ea749a4b81cd4c80dae5988229f7a57"
114109

115110
[[package]]
116111
name = "bitcoin-units"
117-
version = "0.1.2"
112+
version = "0.1.101"
118113
source = "registry+https://github.com/rust-lang/crates.io-index"
119-
checksum = "5285c8bcaa25876d07f37e3d30c303f2609179716e11d688f51e8f1fe70063e2"
114+
checksum = "9cb95693f371d089a4b5b6fc41c6f3ea6e01ee8c15388335dfac8ea685173b51"
120115
dependencies = [
121-
"bitcoin-internals 0.3.0",
116+
"bitcoin-consensus-encoding",
122117
"serde",
123118
]
124119

125120
[[package]]
126121
name = "bitcoin_hashes"
127-
version = "0.14.0"
122+
version = "0.14.101"
128123
source = "registry+https://github.com/rust-lang/crates.io-index"
129-
checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16"
124+
checksum = "bca4c7abb40c8817d77403c880988cfd484f23ab2365726afb2f798363e2c4a2"
130125
dependencies = [
131126
"bitcoin-io",
132-
"hex-conservative 0.2.1",
127+
"hex-conservative 0.2.2",
133128
"serde",
134129
]
135130

136131
[[package]]
137132
name = "bitcoin_hashes"
138-
version = "1.0.0"
133+
version = "1.1.0"
139134
source = "registry+https://github.com/rust-lang/crates.io-index"
140-
checksum = "8f70c29ac06e7effa19682e91318deae86bdb46c4fd1bbd0f12fd196ff427ab0"
135+
checksum = "a67800fcf7f3ca52f7796d4466948a424326b225950c79d1e76d0458760bb25d"
141136
dependencies = [
142137
"bitcoin-consensus-encoding",
143-
"bitcoin-internals 0.5.0",
138+
"bitcoin-internals",
144139
"hex-conservative 1.1.0",
145140
"serde",
146141
]
@@ -230,8 +225,9 @@ dependencies = [
230225
"bech32",
231226
"bincode",
232227
"bitcoin",
233-
"bitcoin-internals 0.5.0",
234-
"bitcoin_hashes 1.0.0",
228+
"bitcoin-consensus-encoding",
229+
"bitcoin-internals",
230+
"bitcoin_hashes 1.1.0",
235231
"getrandom 0.2.16",
236232
"hex-conservative 1.1.0",
237233
"rand",
@@ -313,9 +309,9 @@ dependencies = [
313309

314310
[[package]]
315311
name = "hex-conservative"
316-
version = "0.2.1"
312+
version = "0.2.2"
317313
source = "registry+https://github.com/rust-lang/crates.io-index"
318-
checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd"
314+
checksum = "fda06d18ac606267c40c04e41b9947729bf8b9efe74bd4e82b61a5f26a510b9f"
319315
dependencies = [
320316
"arrayvec",
321317
]
@@ -512,7 +508,7 @@ version = "0.29.1"
512508
source = "registry+https://github.com/rust-lang/crates.io-index"
513509
checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113"
514510
dependencies = [
515-
"bitcoin_hashes 0.14.0",
511+
"bitcoin_hashes 0.14.101",
516512
"rand",
517513
"secp256k1-sys",
518514
"serde",

Cargo.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,10 @@ base64 = ["bitcoin/base64"]
2929

3030
[dependencies]
3131
bech32 = "0.11.0"
32-
bitcoin = "0.32.2"
33-
hashes = { package = "bitcoin_hashes", version = "1.0", features = [ "hex" ] }
34-
internals = { package = "bitcoin-internals", version = "0.5" }
32+
bitcoin = { version = "0.32.102", default-features = false, features = [ "encoding", "std" ] }
33+
encoding = { package = "bitcoin-consensus-encoding", version = "1.1.0", default-features = false, features = [ "hex", "std" ] }
34+
hashes = { package = "bitcoin_hashes", version = "1.1", features = [ "hex" ] }
35+
internals = { package = "bitcoin-internals", version = "0.6" }
3536
secp256k1-zkp = { version = "0.11.0", features = ["global-context", "hashes"] }
3637

3738
# Used for ContractHash::from_json_contract.
@@ -73,6 +74,7 @@ match_bool = "allow" # Adds extra indentation and LOC.
7374
match_same_arms = "allow" # Collapses things that are conceptually unrelated to each other.
7475
must_use_candidate = "allow" # Useful for audit but many false positives.
7576
similar_names = "allow" # Too many (subjectively) false positives.
77+
struct_field_names = "allow" # Dumb
7678
# Exhaustive list of pedantic clippy lints
7779
assigning_clones = "warn"
7880
bool_to_int_with_if = "warn"
@@ -167,7 +169,6 @@ stable_sort_primitive = "warn"
167169
str_split_at_newline = "warn"
168170
string_add_assign = "warn"
169171
struct_excessive_bools = "warn"
170-
struct_field_names = "warn"
171172
too_many_lines = "allow" # FIXME 14 triggers for this lint; probably most should be fixed
172173
transmute_ptr_to_ptr = "warn"
173174
trivially_copy_pass_by_ref = "warn"
@@ -191,7 +192,6 @@ zero_sized_map_values = "warn"
191192
[package.metadata.rbmt.lint]
192193
allowed_duplicates = [
193194
"bitcoin_hashes",
194-
"bitcoin-internals",
195195
"hex-conservative",
196196
]
197197

elementsd-tests/src/taproot.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ fn taproot_spend_test(
223223
hash_ty: sighash_ty,
224224
};
225225

226-
tx.input[0].witness.script_witness = vec![schnorr_sig.to_vec()];
226+
tx.input[0].witness.script_witness.push(schnorr_sig.to_vec());
227227
} else {
228228
// script spend
229229
// try spending using leaf1
@@ -250,11 +250,9 @@ fn taproot_spend_test(
250250
hash_ty: sighash_ty,
251251
};
252252

253-
tx.input[0].witness.script_witness = vec![
254-
schnorr_sig.to_vec(), // witness
255-
script_ver.0.into_bytes(), // leaf script
256-
ctrl_block.serialize(), // control block
257-
];
253+
tx.input[0].witness.script_witness.push(schnorr_sig.to_vec()); // witness
254+
tx.input[0].witness.script_witness.push(script_ver.0.into_bytes()); // leaf script
255+
tx.input[0].witness.script_witness.push(ctrl_block.serialize()); // control block
258256
}
259257

260258
let tx_hex = serialize_hex(&tx);

examples/raw_blind.rs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -305,15 +305,13 @@ fn main() {
305305

306306
// Finalize(TODO in miniscript)
307307
pset.inputs_mut()[0].partial_sigs.clear();
308-
pset.inputs_mut()[0].final_script_witness = Some(vec![
309-
inp0_sig,
310-
inp0_pk.to_bytes(),
311-
]);
308+
let wit = pset.inputs_mut()[0].final_script_witness.insert(elements::Witness::new());
309+
wit.push(inp0_sig);
310+
wit.push(inp0_pk.to_bytes());
312311
pset.inputs_mut()[1].partial_sigs.clear();
313-
pset.inputs_mut()[1].final_script_witness = Some(vec![
314-
inp1_sig,
315-
inp1_pk.to_bytes(),
316-
]);
312+
let wit = pset.inputs_mut()[1].final_script_witness.insert(elements::Witness::new());
313+
wit.push(inp1_sig);
314+
wit.push(inp1_pk.to_bytes());
317315
assert_eq!(pset, deser_pset(&tests["finalized"]));
318316

319317
// Extracted tx

fuzz/Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@ allowed_duplicates = [
3333
"wasi",
3434
]
3535

36+
[[bin]]
37+
name = "decode_equiv_transaction"
38+
path = "fuzz_targets/decode_equiv_transaction.rs"
39+
test = false
40+
doc = false
41+
bench = false
42+
3643
[[bin]]
3744
name = "deserialize_block"
3845
path = "fuzz_targets/deserialize_block.rs"
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// SPDX-License-Identifier: MIT OR Apache-2.0
2+
3+
//! Fuzz test for equivalent decoding between the legacy and consensus-encoding decoding schemes.
4+
5+
#![cfg_attr(fuzzing, no_main)]
6+
#![cfg_attr(not(fuzzing), allow(unused))]
7+
use libfuzzer_sys::fuzz_target;
8+
9+
use elements::bitcoin::hex::DisplayHex as _;
10+
11+
type Target = elements::Transaction;
12+
13+
#[cfg(not(fuzzing))]
14+
fn main() {}
15+
16+
struct DisplayError<'e>(&'e dyn std::error::Error);
17+
18+
impl core::fmt::Display for DisplayError<'_> {
19+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20+
writeln!(f, "{}", self.0)?;
21+
let mut e = self.0;
22+
while let Some(source) = e.source() {
23+
writeln!(f, "caused by {}", source)?;
24+
e = source;
25+
}
26+
Ok(())
27+
}
28+
}
29+
30+
fn do_test(data: &[u8]) {
31+
let old: Result<Target, _> = elements::encode::deserialize(data);
32+
let new: Result<Target, _> = elements::encoding::decode_from_slice(data);
33+
34+
match (old, new) {
35+
(Err(_), Err(_)) => {},
36+
(Err(e), Ok(new)) => {
37+
let e = DisplayError(&e);
38+
panic!("Decodable trait failed with {e}; Decode parsed {:?}", new);
39+
}
40+
(Ok(old), Err(e)) => {
41+
let e = DisplayError(&e);
42+
panic!("Decode trait failed with {e}; Decodable parsed {:?}", old);
43+
}
44+
(Ok(old), Ok(new)) => {
45+
assert_eq!(
46+
old, new,
47+
"Decodable (left) did not match Decode (right)",
48+
);
49+
50+
let reser_old = elements::encode::serialize(&old);
51+
let reser_new = elements::encoding::encode_to_vec(&new);
52+
assert_eq!(
53+
reser_old, reser_new,
54+
"Encodable (left) did not match Encode (right)\nOld hex: {}\nNew hex: {}\nTransaction: {:?}",
55+
reser_old.as_hex(),
56+
reser_new.as_hex(),
57+
old,
58+
);
59+
}
60+
}
61+
}
62+
63+
fuzz_target!(|data: &[u8]| {
64+
do_test(data);
65+
});
66+
67+
#[cfg(test)]
68+
mod tests {
69+
#[test]
70+
fn duplicate_crash() {
71+
let v = elements::hex::decode_to_vec("abcd").unwrap();
72+
super::do_test(&v);
73+
}
74+
}

rustfmt.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ ignore = [
22
"/",
33
"!/src/lib.rs",
44
"!/src/confidential/*.rs",
5+
"!/src/transaction/decoders.rs",
6+
"!/src/transaction/encoders.rs",
7+
"!/src/transaction/pegin_witness.rs"
58
]
69
hard_tabs = false
710
tab_spaces = 4

src/block.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,6 @@ pub struct Block {
360360
/// Complete list of transaction in the block
361361
pub txdata: Vec<Transaction>,
362362
}
363-
serde_struct_impl!(Block, header, txdata);
364363
impl_consensus_encoding!(Block, header, txdata);
365364

366365
impl Block {

0 commit comments

Comments
 (0)