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  }