scriptbuilder.go raw

   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