elements_miniscript/descriptor/csfs_cov/script_internals.rs
1// Miniscript
2// Written in 2021 by
3// Andrew Poelstra <apoelstra@wpsoftware.net>
4// Sanket Kanjalkar <sanket1729@gmail.com>
5//
6// To the extent possible under law, the author(s) have dedicated all
7// copyright and related and neighboring rights to this software to
8// the public domain worldwide. This software is distributed without
9// any warranty.
10//
11// You should have received a copy of the CC0 Public Domain Dedication
12// along with this software.
13// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
14//
15//! Script Internals for Covenant Descriptor Support
16
17use bitcoin;
18use elements::opcodes::all;
19use elements::script;
20/// Additional operations required on script builder
21/// for Covenant operations support
22pub trait CovOperations: Sized {
23 /// Assert that the size of top stack elem is `len`
24 fn chk_size(self, len: usize) -> Self;
25 /// Assert that the top item is a valid confidential Amount
26 /// If it starts with 1, the len must be 9, otherwise the
27 /// len must be 33
28 fn chk_amt(self) -> Self;
29 /// Assuming the 10 sighash components + 1 sig on the top of
30 /// stack for segwit sighash as created by init_stack
31 /// CAT all of them and check sig from stack
32 fn verify_cov(self, key: &bitcoin::PublicKey) -> Self;
33
34 /// Get the script code for the covenant script
35 /// assuming the above construction of covenants
36 /// which uses OP_CODESEP
37 fn post_codesep_script(self) -> Self;
38}
39
40impl CovOperations for script::Builder {
41 fn chk_size(self, len: usize) -> Self {
42 self.push_opcode(all::OP_SIZE)
43 .push_int(len as i64)
44 .push_opcode(all::OP_EQUALVERIFY)
45 }
46
47 fn chk_amt(self) -> Self {
48 let mut builder = self;
49
50 // Copy the first byte onto the stack
51 builder = builder.push_opcode(all::OP_DUP);
52 builder = builder.push_int(1).push_opcode(all::OP_LEFT);
53 // Check if first byte is equal to 1
54 builder = builder.push_int(1).push_opcode(all::OP_EQUAL);
55 // Assert that explicit size is 9
56 builder = builder
57 .push_opcode(all::OP_IF)
58 .push_opcode(all::OP_SIZE)
59 .push_int(9)
60 .push_opcode(all::OP_EQUALVERIFY);
61 // Else assert that commitment size is 33
62 builder
63 .push_opcode(all::OP_ELSE)
64 .push_opcode(all::OP_SIZE)
65 .push_int(33)
66 .push_opcode(all::OP_EQUALVERIFY)
67 .push_opcode(all::OP_ENDIF)
68 }
69
70 #[rustfmt::skip]
71 fn verify_cov(self, key: &bitcoin::PublicKey) -> Self {
72 use elements::opcodes::all::{OP_CAT, OP_SWAP};
73
74 let mut builder = self;
75 // The miniscript is of type B, which should have pushed 1
76 // onto the stack if it satisfied correctly.(which it should)
77 // because this is a top level check
78 // Initially the stack contains the [ec_sig..sighash_items]
79 // where sighash_items are items from segwit bip143 sighash for
80 // elements arranged sequentially such that item 1 is at top,
81 // item 10 is the last. The top of stack is miniscript execution result
82 // denoted by B type
83 // stk = [ecsig i10 i9 i8 i7 i6 i5 i4 i3b i3 i2 i1 B]
84 // alt_stk = []
85 builder = builder.push_verify();
86 // stk = [ecsig i10 i9 i8 i7 i6 i5 i4 i3b i3 i2 i1]
87 // alt_stk = []
88 // pick signature. stk_size = 12
89 // Why can we pick have a fixed pick of 11?
90 // The covenant check enforces that the the next 12 elements
91 // of the stack must be elements from the sighash.
92 // We don't additionally need to check the depth because
93 // cleanstack is a consensus rule in segwit.
94 // Copy the ec_sig to the stack top
95 builder = builder.push_int(11).push_opcode(all::OP_PICK);
96 // convert sighash type into 1 byte(It is 4 byte in sighash calculation)
97 // Since we copied the ecsig onto stack top, this will now be at pos 11
98 builder = builder.push_int(11).push_opcode(all::OP_PICK);
99 builder = builder.push_int(1).push_opcode(all::OP_LEFT);
100 // create a bitcoinsig = [ecsig || sighashtype]cat the sig and hashtype
101 builder = builder.push_opcode(all::OP_CAT);
102 // Push the bitcoinsig to alt stack
103 builder = builder.push_opcode(all::OP_TOALTSTACK);
104 // alt_stk = [bitcoinsig]
105 // stk = [ecsig i10 i9 i8 i7 i6 i5 i4 i3b i3 i2 i1]
106
107 // Do the size checks on all respective items in sighash calculation
108 builder = builder.chk_size(4).push_opcode(OP_SWAP); // item 1: ver
109 builder = builder.chk_size(32).push_opcode(OP_CAT).push_opcode(OP_SWAP);//item 2: hashprevouts
110 builder = builder.chk_size(32).push_opcode(OP_CAT).push_opcode(OP_SWAP);//item 3: hashsequence
111 builder = builder.chk_size(32).push_opcode(OP_CAT).push_opcode(OP_SWAP);//item 3b: hashissuances
112 builder = builder.chk_size(36).push_opcode(OP_CAT).push_opcode(OP_SWAP);//item 4: outpoint
113 // Item 5: Script code is of constant size because we only consider everything after
114 // codeseparator. This will be replaced with a push slice in a later commit
115 builder = builder.chk_size(3).push_opcode(OP_CAT).push_opcode(OP_SWAP); //item 5: script code
116 builder = builder.chk_amt().push_opcode(OP_CAT).push_opcode(OP_SWAP); //item 6: check confAmt
117 builder = builder.chk_size(4).push_opcode(OP_CAT).push_opcode(OP_SWAP); //item 7: sequence
118 builder = builder.chk_size(32).push_opcode(OP_CAT).push_opcode(OP_SWAP);//item 8: hashoutputs
119 builder = builder.chk_size(4).push_opcode(OP_CAT).push_opcode(OP_SWAP); //item 9: nlocktime
120 builder = builder.chk_size(4).push_opcode(OP_CAT); //item 10: sighash type
121
122 // Now sighash is on the top of the stack
123 // alt_stk = [bitcoinsig]
124 // stk = [ecsig (i1||i2||i3||i3b||i4||i5||i6||i7||i8||i9||i10)]
125 // Note that item order is reversed
126 // || denotes concat operation
127 builder = builder.push_opcode(all::OP_SHA256);
128 builder = builder.push_key(key).push_opcode(all::OP_DUP);
129 builder = builder
130 .push_opcode(all::OP_FROMALTSTACK)
131 .push_opcode(all::OP_SWAP);
132 // stk = [ecsig sha2_msg pk btcsig pk]
133 // alt_stk = []
134
135 // Code separator. Everything before this(and including this codesep)
136 // won't be used in script code calculation
137 builder = builder.push_opcode(all::OP_CODESEPARATOR);
138 builder.post_codesep_script()
139 }
140
141 /// The second parameter decides whether the script code should
142 /// a hashlock verifying the entire script
143 fn post_codesep_script(self) -> Self {
144 let builder = self;
145 // Now sighash is on the top of the stack
146 // stk = [ecsig sha2_msg pk btcsig pk]
147 builder
148 .push_opcode(all::OP_CHECKSIGVERIFY)
149 .push_opcode(all::OP_CHECKSIGFROMSTACK)
150 }
151}