github.com/lbryio/lbcd@v0.22.119/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  //
    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  // AddOps pushes the passed opcodes to the end of the script.  The script will
    78  // not be modified if pushing the opcodes would cause the script to exceed the
    79  // maximum allowed script engine size.
    80  func (b *ScriptBuilder) AddOps(opcodes []byte) *ScriptBuilder {
    81  	if b.err != nil {
    82  		return b
    83  	}
    84  
    85  	// Pushes that would cause the script to exceed the largest allowed
    86  	// script size would result in a non-canonical script.
    87  	if len(b.script)+len(opcodes) > MaxScriptSize {
    88  		str := fmt.Sprintf("adding opcodes would exceed the maximum "+
    89  			"allowed canonical script length of %d", MaxScriptSize)
    90  		b.err = ErrScriptNotCanonical(str)
    91  		return b
    92  	}
    93  
    94  	b.script = append(b.script, opcodes...)
    95  	return b
    96  }
    97  
    98  // canonicalDataSize returns the number of bytes the canonical encoding of the
    99  // data will take.
   100  func canonicalDataSize(data []byte) int {
   101  	dataLen := len(data)
   102  
   103  	// When the data consists of a single number that can be represented
   104  	// by one of the "small integer" opcodes, that opcode will be instead
   105  	// of a data push opcode followed by the number.
   106  	if dataLen == 0 {
   107  		return 1
   108  	} else if dataLen == 1 && data[0] <= 16 {
   109  		return 1
   110  	} else if dataLen == 1 && data[0] == 0x81 {
   111  		return 1
   112  	}
   113  
   114  	if dataLen < OP_PUSHDATA1 {
   115  		return 1 + dataLen
   116  	} else if dataLen <= 0xff {
   117  		return 2 + dataLen
   118  	} else if dataLen <= 0xffff {
   119  		return 3 + dataLen
   120  	}
   121  
   122  	return 5 + dataLen
   123  }
   124  
   125  // addData is the internal function that actually pushes the passed data to the
   126  // end of the script.  It automatically chooses canonical opcodes depending on
   127  // the length of the data.  A zero length buffer will lead to a push of empty
   128  // data onto the stack (OP_0).  No data limits are enforced with this function.
   129  func (b *ScriptBuilder) addData(data []byte) *ScriptBuilder {
   130  	dataLen := len(data)
   131  
   132  	// When the data consists of a single number that can be represented
   133  	// by one of the "small integer" opcodes, use that opcode instead of
   134  	// a data push opcode followed by the number.
   135  	if dataLen == 0 || dataLen == 1 && data[0] == 0 {
   136  		b.script = append(b.script, OP_0)
   137  		return b
   138  	} else if dataLen == 1 && data[0] <= 16 {
   139  		b.script = append(b.script, (OP_1-1)+data[0])
   140  		return b
   141  	} else if dataLen == 1 && data[0] == 0x81 {
   142  		b.script = append(b.script, byte(OP_1NEGATE))
   143  		return b
   144  	}
   145  
   146  	// Use one of the OP_DATA_# opcodes if the length of the data is small
   147  	// enough so the data push instruction is only a single byte.
   148  	// Otherwise, choose the smallest possible OP_PUSHDATA# opcode that
   149  	// can represent the length of the data.
   150  	if dataLen < OP_PUSHDATA1 {
   151  		b.script = append(b.script, byte((OP_DATA_1-1)+dataLen))
   152  	} else if dataLen <= 0xff {
   153  		b.script = append(b.script, OP_PUSHDATA1, byte(dataLen))
   154  	} else if dataLen <= 0xffff {
   155  		buf := make([]byte, 2)
   156  		binary.LittleEndian.PutUint16(buf, uint16(dataLen))
   157  		b.script = append(b.script, OP_PUSHDATA2)
   158  		b.script = append(b.script, buf...)
   159  	} else {
   160  		buf := make([]byte, 4)
   161  		binary.LittleEndian.PutUint32(buf, uint32(dataLen))
   162  		b.script = append(b.script, OP_PUSHDATA4)
   163  		b.script = append(b.script, buf...)
   164  	}
   165  
   166  	// Append the actual data.
   167  	b.script = append(b.script, data...)
   168  
   169  	return b
   170  }
   171  
   172  // AddFullData should not typically be used by ordinary users as it does not
   173  // include the checks which prevent data pushes larger than the maximum allowed
   174  // sizes which leads to scripts that can't be executed.  This is provided for
   175  // testing purposes such as regression tests where sizes are intentionally made
   176  // larger than allowed.
   177  //
   178  // Use AddData instead.
   179  func (b *ScriptBuilder) AddFullData(data []byte) *ScriptBuilder {
   180  	if b.err != nil {
   181  		return b
   182  	}
   183  
   184  	return b.addData(data)
   185  }
   186  
   187  // AddData pushes the passed data to the end of the script.  It automatically
   188  // chooses canonical opcodes depending on the length of the data.  A zero length
   189  // buffer will lead to a push of empty data onto the stack (OP_0) and any push
   190  // of data greater than MaxScriptElementSize will not modify the script since
   191  // that is not allowed by the script engine.  Also, the script will not be
   192  // modified if pushing the data would cause the script to exceed the maximum
   193  // allowed script engine size.
   194  func (b *ScriptBuilder) AddData(data []byte) *ScriptBuilder {
   195  	if b.err != nil {
   196  		return b
   197  	}
   198  
   199  	// Pushes that would cause the script to exceed the largest allowed
   200  	// script size would result in a non-canonical script.
   201  	dataSize := canonicalDataSize(data)
   202  	if len(b.script)+dataSize > MaxScriptSize {
   203  		str := fmt.Sprintf("adding %d bytes of data would exceed the "+
   204  			"maximum allowed canonical script length of %d",
   205  			dataSize, MaxScriptSize)
   206  		b.err = ErrScriptNotCanonical(str)
   207  		return b
   208  	}
   209  
   210  	// Pushes larger than the max script element size would result in a
   211  	// script that is not canonical.
   212  	dataLen := len(data)
   213  	if dataLen > MaxScriptElementSize {
   214  		str := fmt.Sprintf("adding a data element of %d bytes would "+
   215  			"exceed the maximum allowed script element size of %d",
   216  			dataLen, MaxScriptElementSize)
   217  		b.err = ErrScriptNotCanonical(str)
   218  		return b
   219  	}
   220  
   221  	return b.addData(data)
   222  }
   223  
   224  // AddInt64 pushes the passed integer to the end of the script.  The script will
   225  // not be modified if pushing the data would cause the script to exceed the
   226  // maximum allowed script engine size.
   227  func (b *ScriptBuilder) AddInt64(val int64) *ScriptBuilder {
   228  	if b.err != nil {
   229  		return b
   230  	}
   231  
   232  	// Pushes that would cause the script to exceed the largest allowed
   233  	// script size would result in a non-canonical script.
   234  	if len(b.script)+1 > MaxScriptSize {
   235  		str := fmt.Sprintf("adding an integer would exceed the "+
   236  			"maximum allow canonical script length of %d",
   237  			MaxScriptSize)
   238  		b.err = ErrScriptNotCanonical(str)
   239  		return b
   240  	}
   241  
   242  	// Fast path for small integers and OP_1NEGATE.
   243  	if val == 0 {
   244  		b.script = append(b.script, OP_0)
   245  		return b
   246  	}
   247  	if val == -1 || (val >= 1 && val <= 16) {
   248  		b.script = append(b.script, byte((OP_1-1)+val))
   249  		return b
   250  	}
   251  
   252  	return b.AddData(scriptNum(val).Bytes())
   253  }
   254  
   255  // Reset resets the script so it has no content.
   256  func (b *ScriptBuilder) Reset() *ScriptBuilder {
   257  	b.script = b.script[0:0]
   258  	b.err = nil
   259  	return b
   260  }
   261  
   262  // Script returns the currently built script.  When any errors occurred while
   263  // building the script, the script will be returned up the point of the first
   264  // error along with the error.
   265  func (b *ScriptBuilder) Script() ([]byte, error) {
   266  	return b.script, b.err
   267  }
   268  
   269  // NewScriptBuilder returns a new instance of a script builder.  See
   270  // ScriptBuilder for details.
   271  func NewScriptBuilder() *ScriptBuilder {
   272  	return &ScriptBuilder{
   273  		script: make([]byte, 0, defaultScriptAlloc),
   274  	}
   275  }