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}