Skip to content

Commit a2ae36d

Browse files
committed
add state commitment ability to simplicity info
1 parent 8deded7 commit a2ae36d

File tree

4 files changed

+54
-9
lines changed

4 files changed

+54
-9
lines changed

src/bin/hal-simplicity/cmd/simplicity/info.rs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use super::{Error, ErrorExt as _};
77

88
use hal_simplicity::hal_simplicity::{elements_address, Program};
99
use hal_simplicity::simplicity::{jet, Amr, Cmr, Ihr};
10+
use simplicity::hex::parse::FromHex as _;
1011

1112
use serde::Serialize;
1213

@@ -42,20 +43,32 @@ pub fn cmd<'a>() -> clap::App<'a, 'a> {
4243
cmd::arg("witness", "a hex encoding of all the witness data for the program")
4344
.takes_value(true)
4445
.required(false),
46+
cmd::opt(
47+
"state",
48+
"32-byte state commitment to put alongside the program when generating addresess (hex)",
49+
)
50+
.takes_value(true)
51+
.short("s")
52+
.required(false),
4553
])
4654
}
4755

4856
pub fn exec<'a>(matches: &clap::ArgMatches<'a>) {
4957
let program = matches.value_of("program").expect("program is mandatory");
5058
let witness = matches.value_of("witness");
59+
let state = matches.value_of("state");
5160

52-
match exec_inner(program, witness) {
61+
match exec_inner(program, witness, state) {
5362
Ok(info) => cmd::print_output(matches, &info),
5463
Err(e) => cmd::print_output(matches, &e),
5564
}
5665
}
5766

58-
fn exec_inner(program: &str, witness: Option<&str>) -> Result<ProgramInfo, Error> {
67+
fn exec_inner(
68+
program: &str,
69+
witness: Option<&str>,
70+
state: Option<&str>,
71+
) -> Result<ProgramInfo, Error> {
5972
// In the future we should attempt to parse as a Bitcoin program if parsing as
6073
// Elements fails. May be tricky/annoying in Rust since Program<Elements> is a
6174
// different type from Program<Bitcoin>.
@@ -73,17 +86,27 @@ fn exec_inner(program: &str, witness: Option<&str>) -> Result<ProgramInfo, Error
7386
x // binding needed for truly stupid borrowck reasons
7487
});
7588

89+
let state = state
90+
.map(<[u8; 32]>::from_hex)
91+
.transpose()
92+
.result_context("parsing 32-byte state commitment as hex")?;
93+
7694
Ok(ProgramInfo {
7795
jets: "core",
7896
commit_base64: program.commit_prog().to_string(),
7997
// FIXME this is, in general, exponential in size. Need to limit it somehow; probably need upstream support
8098
commit_decode: program.commit_prog().display_expr().to_string(),
8199
type_arrow: program.commit_prog().arrow().to_string(),
82100
cmr: program.cmr(),
83-
liquid_address_unconf: elements_address(program.cmr(), &elements::AddressParams::LIQUID)
84-
.to_string(),
101+
liquid_address_unconf: elements_address(
102+
program.cmr(),
103+
state,
104+
&elements::AddressParams::LIQUID,
105+
)
106+
.to_string(),
85107
liquid_testnet_address_unconf: elements_address(
86108
program.cmr(),
109+
state,
87110
&elements::AddressParams::LIQUID_TESTNET,
88111
)
89112
.to_string(),

src/bin/hal-simplicity/cmd/simplicity/pset/update_input.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ fn exec_inner(
9797
// Guess that the given program is the only Tapleaf. This is the case for addresses
9898
// generated from the web IDE, and from `hal-simplicity simplicity info`, and for
9999
// most "test" scenarios. We need to design an API to handle more general cases.
100-
let spend_info = taproot_spend_info(internal_key, cmr);
100+
let spend_info = taproot_spend_info(internal_key, None, cmr);
101101
if spend_info.output_key().as_inner().serialize() != input_utxo.script_pubkey[2..] {
102102
// If our guess was wrong, at least error out..
103103
return Err(format!("CMR and internal key imply output key {}, which does not match input scriptPubKey {}", spend_info.output_key().as_inner(), input_utxo.script_pubkey))

src/hal_simplicity.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,29 @@ fn script_ver(cmr: simplicity::Cmr) -> (elements::Script, elements::taproot::Lea
112112
/// for a Taptree with this CMR as its single leaf.
113113
pub fn taproot_spend_info(
114114
internal_key: secp256k1::XOnlyPublicKey,
115+
state: Option<[u8; 32]>,
115116
cmr: simplicity::Cmr,
116117
) -> TaprootSpendInfo {
117118
let builder = TaprootBuilder::new();
118119
let (script, version) = script_ver(cmr);
119-
let builder = builder.add_leaf_with_ver(0, script, version).expect("tap tree should be valid");
120+
let builder = if let Some(state) = state {
121+
use elements::hashes::{sha256, Hash as _, HashEngine as _};
122+
let tag = sha256::Hash::hash(b"TapData");
123+
let mut eng = sha256::Hash::engine();
124+
eng.input(tag.as_byte_array());
125+
eng.input(tag.as_byte_array());
126+
eng.input(&state);
127+
let state_hash = sha256::Hash::from_engine(eng);
128+
129+
builder
130+
.add_leaf_with_ver(1, script, version)
131+
.expect("tap tree should be valid")
132+
.add_hidden(1, state_hash)
133+
.expect("tap tree should be valid")
134+
} else {
135+
builder.add_leaf_with_ver(0, script, version).expect("tap tree should be valid")
136+
};
137+
120138
builder.finalize(secp256k1::SECP256K1, internal_key).expect("tap tree should be valid")
121139
}
122140

@@ -125,9 +143,10 @@ pub fn taproot_spend_info(
125143
/// internal key and this CMR as its single leaf.
126144
pub fn elements_address(
127145
cmr: simplicity::Cmr,
146+
state: Option<[u8; 32]>,
128147
params: &'static elements::AddressParams,
129148
) -> elements::Address {
130-
let info = taproot_spend_info(unspendable_internal_key(), cmr);
149+
let info = taproot_spend_info(unspendable_internal_key(), state, cmr);
131150
let blinder = None;
132151
elements::Address::p2tr(
133152
secp256k1::SECP256K1,

tests/cli.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,7 +1077,7 @@ hal-simplicity-simplicity-info 0.1.0
10771077
Parse a base64-encoded Simplicity program and decode it
10781078
10791079
USAGE:
1080-
hal-simplicity simplicity info [FLAGS] <program> [witness]
1080+
hal-simplicity simplicity info [FLAGS] [OPTIONS] <program> [witness]
10811081
10821082
FLAGS:
10831083
-r, --elementsregtest run in elementsregtest mode
@@ -1086,6 +1086,9 @@ FLAGS:
10861086
-v, --verbose print verbose logging output to stderr
10871087
-y, --yaml print output in YAML instead of JSON
10881088
1089+
OPTIONS:
1090+
-s, --state <state> 32-byte state commitment to put alongside the program when generating addresess (hex)
1091+
10891092
ARGS:
10901093
<program> a Simplicity program in base64
10911094
<witness> a hex encoding of all the witness data for the program
@@ -1103,7 +1106,7 @@ error: The following required arguments were not provided:
11031106
<program>
11041107
11051108
USAGE:
1106-
hal-simplicity simplicity info [FLAGS] <program> [witness]
1109+
hal-simplicity simplicity info [FLAGS] [OPTIONS] <program> [witness]
11071110
11081111
For more information try --help
11091112
",

0 commit comments

Comments
 (0)