1 package txscript
2 3 import (
4 "encoding/binary"
5 "fmt"
6 )
7 8 const (
9 // defaultScriptAlloc is the default size used for the backing array for a script being built by the ScriptBuilder.
10 // The array will dynamically grow as needed, but this figure is intended to provide enough space for vast majority
11 // of scripts without needing to grow the backing array multiple times.
12 defaultScriptAlloc = 500
13 )
14 15 // ErrScriptNotCanonical identifies a non-canonical script. The caller can use a
16 // type assertion to detect this error type.
17 type ErrScriptNotCanonical string
18 19 // ScriptError implements the error interface.
20 func (e ErrScriptNotCanonical) Error() string {
21 return string(e)
22 }
23 24 // ScriptBuilder provides a facility for building custom scripts. It allows you
25 // to push opcodes, ints, and data while respecting canonical encoding. In
26 // general it does not ensure the script will execute correctly, however any
27 // data pushes which would exceed the maximum allowed script engine limits and
28 // are therefore guaranteed not to execute will not be pushed and will result in
29 // the Script function returning an error. For example, the following would
30 // podbuild a 2-of-3 multisig script for usage in a pay-to-script-hash (although in
31 // this situation MultiSigScript() would be a better choice to generate the
32 // script):
33 //
34 // builder := txscript.NewScriptBuilder()
35 // builder.AddOp(txscript.OP_2).AddData(pubKey1).AddData(pubKey2)
36 // builder.AddData(pubKey3).AddOp(txscript.OP_3)
37 // builder.AddOp(txscript.OP_CHECKMULTISIG)
38 // script, e := builder.Script()
39 // if e != nil {
40 // L.Script// // Handle the error.
41 // return
42 // }
43 // log.Printf("Final multi-sig script: %x\n", script)
44 type ScriptBuilder struct {
45 script []byte
46 err error
47 }
48 49 // AddOp pushes the passed opcode to the end of the script. The script will not
50 // be modified if pushing the opcode would cause the script to exceed the
51 // maximum allowed script engine size.
52 func (b *ScriptBuilder) AddOp(opcode byte) *ScriptBuilder {
53 if b.err != nil {
54 return b
55 }
56 // Pushes that would cause the script to exceed the largest allowed script size
57 // would result in a non-canonical script.
58 if len(b.script)+1 > MaxScriptSize {
59 str := fmt.Sprintf("adding an opcode would exceed the maximum "+
60 "allowed canonical script length of %d", MaxScriptSize,
61 )
62 b.err = ErrScriptNotCanonical(str)
63 return b
64 }
65 b.script = append(b.script, opcode)
66 return b
67 }
68 69 // AddOps pushes the passed opcodes to the end of the script. The script will
70 // not be modified if pushing the opcodes would cause the script to exceed the
71 // maximum allowed script engine size.
72 func (b *ScriptBuilder) AddOps(opcodes []byte) *ScriptBuilder {
73 if b.err != nil {
74 return b
75 }
76 // Pushes that would cause the script to exceed the largest allowed script size
77 // would result in a non-canonical script.
78 if len(b.script)+len(opcodes) > MaxScriptSize {
79 str := fmt.Sprintf("adding opcodes would exceed the maximum "+
80 "allowed canonical script length of %d", MaxScriptSize,
81 )
82 b.err = ErrScriptNotCanonical(str)
83 return b
84 }
85 b.script = append(b.script, opcodes...)
86 return b
87 }
88 89 // canonicalDataSize returns the number of bytes the canonical encoding of the
90 // data will take.
91 func canonicalDataSize(data []byte) int {
92 dataLen := len(data)
93 // When the data consists of a single number that can be represented by one of
94 // the "small integer" opcodes, that opcode will be instead of a data push
95 // opcode followed by the number.
96 if dataLen == 0 {
97 return 1
98 } else if dataLen == 1 && data[0] <= 16 {
99 return 1
100 } else if dataLen == 1 && data[0] == 0x81 {
101 return 1
102 }
103 if dataLen < OP_PUSHDATA1 {
104 return 1 + dataLen
105 } else if dataLen <= 0xff {
106 return 2 + dataLen
107 } else if dataLen <= 0xffff {
108 return 3 + dataLen
109 }
110 return 5 + dataLen
111 }
112 113 // addData is the internal function that actually pushes the passed data to the
114 // end of the script. It automatically chooses canonical opcodes depending on
115 // the length of the data. A zero length buffer will lead to a push of empty
116 // data onto the stack (OP_0). No data limits are enforced with this function.
117 func (b *ScriptBuilder) addData(data []byte) *ScriptBuilder {
118 dataLen := len(data)
119 // When the data consists of a single number that can be represented by one of
120 // the "small integer" opcodes, use that opcode instead of a data push opcode
121 // followed by the number.
122 if dataLen == 0 || dataLen == 1 && data[0] == 0 {
123 b.script = append(b.script, OP_0)
124 return b
125 } else if dataLen == 1 && data[0] <= 16 {
126 b.script = append(b.script, (OP_1-1)+data[0])
127 return b
128 } else if dataLen == 1 && data[0] == 0x81 {
129 b.script = append(b.script, byte(OP_1NEGATE))
130 return b
131 }
132 // Use one of the OP_DATA_# opcodes if the length of the data is small enough so
133 // the data push instruction is only a single byte. Otherwise, choose the
134 // smallest possible OP_PUSHDATA# opcode that can represent the length of the
135 // data.
136 if dataLen < OP_PUSHDATA1 {
137 b.script = append(b.script, byte((OP_DATA_1-1)+dataLen))
138 } else if dataLen <= 0xff {
139 b.script = append(b.script, OP_PUSHDATA1, byte(dataLen))
140 } else if dataLen <= 0xffff {
141 buf := make([]byte, 2)
142 binary.LittleEndian.PutUint16(buf, uint16(dataLen))
143 b.script = append(b.script, OP_PUSHDATA2)
144 b.script = append(b.script, buf...)
145 } else {
146 buf := make([]byte, 4)
147 binary.LittleEndian.PutUint32(buf, uint32(dataLen))
148 b.script = append(b.script, OP_PUSHDATA4)
149 b.script = append(b.script, buf...)
150 }
151 // Append the actual data.
152 b.script = append(b.script, data...)
153 return b
154 }
155 156 // AddFullData should not typically be used by ordinary users as it does not include the checks which prevent data
157 // pushes larger than the maximum allowed txsizes which leads to scripts that can't be executed. This is provided for
158 // testing purposes such as regression tests where txsizes are intentionally made larger than allowed. Use AddData
159 // instead.
160 func (b *ScriptBuilder) AddFullData(data []byte) *ScriptBuilder {
161 if b.err != nil {
162 return b
163 }
164 return b.addData(data)
165 }
166 167 // AddData pushes the passed data to the end of the script. It automatically chooses canonical opcodes depending on the
168 // length of the data. A zero length buffer will lead to a push of empty data onto the stack (OP_0) and any push of data
169 // greater than MaxScriptElementSize will not modify the script since that is not allowed by the script engine. Also,
170 // the script will not be modified if pushing the data would cause the script to exceed the maximum allowed script
171 // engine size.
172 func (b *ScriptBuilder) AddData(data []byte) *ScriptBuilder {
173 if b.err != nil {
174 return b
175 }
176 // Pushes that would cause the script to exceed the largest allowed script size would result in a non-canonical
177 // script.
178 dataSize := canonicalDataSize(data)
179 if len(b.script)+dataSize > MaxScriptSize {
180 str := fmt.Sprintf("adding %d bytes of data would exceed the "+
181 "maximum allowed canonical script length of %d",
182 dataSize, MaxScriptSize,
183 )
184 b.err = ErrScriptNotCanonical(str)
185 return b
186 }
187 // Pushes larger than the max script element size would result in a script that is not canonical.
188 dataLen := len(data)
189 if dataLen > MaxScriptElementSize {
190 str := fmt.Sprintf("adding a data element of %d bytes would "+
191 "exceed the maximum allowed script element size of %d",
192 dataLen, MaxScriptElementSize,
193 )
194 b.err = ErrScriptNotCanonical(str)
195 return b
196 }
197 return b.addData(data)
198 }
199 200 // AddInt64 pushes the passed integer to the end of the script. The script will not be modified if pushing the data
201 // would cause the script to exceed the maximum allowed script engine size.
202 func (b *ScriptBuilder) AddInt64(val int64) *ScriptBuilder {
203 if b.err != nil {
204 return b
205 }
206 // Pushes that would cause the script to exceed the largest allowed script size would result in a non-canonical
207 // script.
208 if len(b.script)+1 > MaxScriptSize {
209 str := fmt.Sprintf("adding an integer would exceed the "+
210 "maximum allow canonical script length of %d",
211 MaxScriptSize,
212 )
213 b.err = ErrScriptNotCanonical(str)
214 return b
215 }
216 // Fast path for small integers and OP_1NEGATE.
217 if val == 0 {
218 b.script = append(b.script, OP_0)
219 return b
220 }
221 if val == -1 || (val >= 1 && val <= 16) {
222 b.script = append(b.script, byte((OP_1-1)+val))
223 return b
224 }
225 return b.AddData(scriptNum(val).Bytes())
226 }
227 228 // Reset resets the script so it has no content.
229 func (b *ScriptBuilder) Reset() *ScriptBuilder {
230 b.script = b.script[0:0]
231 b.err = nil
232 return b
233 }
234 235 // Script returns the currently built script. When any errors occurred while building the script, the script will be
236 // returned up the point of the first error along with the error.
237 func (b *ScriptBuilder) Script() ([]byte, error) {
238 return b.script, b.err
239 }
240 241 // NewScriptBuilder returns a new instance of a script builder. See ScriptBuilder for details.
242 func NewScriptBuilder() *ScriptBuilder {
243 return &ScriptBuilder{
244 script: make([]byte, 0, defaultScriptAlloc),
245 }
246 }
247