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