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