mirror of
https://github.com/muun/recovery.git
synced 2025-11-11 06:20:16 -05:00
Release v0.1.0
This commit is contained in:
274
vendor/github.com/btcsuite/btcd/txscript/scriptbuilder.go
generated
vendored
Normal file
274
vendor/github.com/btcsuite/btcd/txscript/scriptbuilder.go
generated
vendored
Normal file
@@ -0,0 +1,274 @@
|
||||
// Copyright (c) 2013-2015 The btcsuite developers
|
||||
// Use of this source code is governed by an ISC
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package txscript
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
// defaultScriptAlloc is the default size used for the backing array
|
||||
// for a script being built by the ScriptBuilder. The array will
|
||||
// dynamically grow as needed, but this figure is intended to provide
|
||||
// enough space for vast majority of scripts without needing to grow the
|
||||
// backing array multiple times.
|
||||
defaultScriptAlloc = 500
|
||||
)
|
||||
|
||||
// ErrScriptNotCanonical identifies a non-canonical script. The caller can use
|
||||
// a type assertion to detect this error type.
|
||||
type ErrScriptNotCanonical string
|
||||
|
||||
// Error implements the error interface.
|
||||
func (e ErrScriptNotCanonical) Error() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
// ScriptBuilder provides a facility for building custom scripts. It allows
|
||||
// you to push opcodes, ints, and data while respecting canonical encoding. In
|
||||
// general it does not ensure the script will execute correctly, however any
|
||||
// data pushes which would exceed the maximum allowed script engine limits and
|
||||
// are therefore guaranteed not to execute will not be pushed and will result in
|
||||
// the Script function returning an error.
|
||||
//
|
||||
// For example, the following would build a 2-of-3 multisig script for usage in
|
||||
// a pay-to-script-hash (although in this situation MultiSigScript() would be a
|
||||
// better choice to generate the script):
|
||||
// builder := txscript.NewScriptBuilder()
|
||||
// builder.AddOp(txscript.OP_2).AddData(pubKey1).AddData(pubKey2)
|
||||
// builder.AddData(pubKey3).AddOp(txscript.OP_3)
|
||||
// builder.AddOp(txscript.OP_CHECKMULTISIG)
|
||||
// script, err := builder.Script()
|
||||
// if err != nil {
|
||||
// // Handle the error.
|
||||
// return
|
||||
// }
|
||||
// fmt.Printf("Final multi-sig script: %x\n", script)
|
||||
type ScriptBuilder struct {
|
||||
script []byte
|
||||
err error
|
||||
}
|
||||
|
||||
// AddOp pushes the passed opcode to the end of the script. The script will not
|
||||
// be modified if pushing the opcode would cause the script to exceed the
|
||||
// maximum allowed script engine size.
|
||||
func (b *ScriptBuilder) AddOp(opcode byte) *ScriptBuilder {
|
||||
if b.err != nil {
|
||||
return b
|
||||
}
|
||||
|
||||
// Pushes that would cause the script to exceed the largest allowed
|
||||
// script size would result in a non-canonical script.
|
||||
if len(b.script)+1 > MaxScriptSize {
|
||||
str := fmt.Sprintf("adding an opcode would exceed the maximum "+
|
||||
"allowed canonical script length of %d", MaxScriptSize)
|
||||
b.err = ErrScriptNotCanonical(str)
|
||||
return b
|
||||
}
|
||||
|
||||
b.script = append(b.script, opcode)
|
||||
return b
|
||||
}
|
||||
|
||||
// AddOps pushes the passed opcodes to the end of the script. The script will
|
||||
// not be modified if pushing the opcodes would cause the script to exceed the
|
||||
// maximum allowed script engine size.
|
||||
func (b *ScriptBuilder) AddOps(opcodes []byte) *ScriptBuilder {
|
||||
if b.err != nil {
|
||||
return b
|
||||
}
|
||||
|
||||
// Pushes that would cause the script to exceed the largest allowed
|
||||
// script size would result in a non-canonical script.
|
||||
if len(b.script)+len(opcodes) > MaxScriptSize {
|
||||
str := fmt.Sprintf("adding opcodes would exceed the maximum "+
|
||||
"allowed canonical script length of %d", MaxScriptSize)
|
||||
b.err = ErrScriptNotCanonical(str)
|
||||
return b
|
||||
}
|
||||
|
||||
b.script = append(b.script, opcodes...)
|
||||
return b
|
||||
}
|
||||
|
||||
// canonicalDataSize returns the number of bytes the canonical encoding of the
|
||||
// data will take.
|
||||
func canonicalDataSize(data []byte) int {
|
||||
dataLen := len(data)
|
||||
|
||||
// When the data consists of a single number that can be represented
|
||||
// by one of the "small integer" opcodes, that opcode will be instead
|
||||
// of a data push opcode followed by the number.
|
||||
if dataLen == 0 {
|
||||
return 1
|
||||
} else if dataLen == 1 && data[0] <= 16 {
|
||||
return 1
|
||||
} else if dataLen == 1 && data[0] == 0x81 {
|
||||
return 1
|
||||
}
|
||||
|
||||
if dataLen < OP_PUSHDATA1 {
|
||||
return 1 + dataLen
|
||||
} else if dataLen <= 0xff {
|
||||
return 2 + dataLen
|
||||
} else if dataLen <= 0xffff {
|
||||
return 3 + dataLen
|
||||
}
|
||||
|
||||
return 5 + dataLen
|
||||
}
|
||||
|
||||
// addData is the internal function that actually pushes the passed data to the
|
||||
// end of the script. It automatically chooses canonical opcodes depending on
|
||||
// the length of the data. A zero length buffer will lead to a push of empty
|
||||
// data onto the stack (OP_0). No data limits are enforced with this function.
|
||||
func (b *ScriptBuilder) addData(data []byte) *ScriptBuilder {
|
||||
dataLen := len(data)
|
||||
|
||||
// When the data consists of a single number that can be represented
|
||||
// by one of the "small integer" opcodes, use that opcode instead of
|
||||
// a data push opcode followed by the number.
|
||||
if dataLen == 0 || dataLen == 1 && data[0] == 0 {
|
||||
b.script = append(b.script, OP_0)
|
||||
return b
|
||||
} else if dataLen == 1 && data[0] <= 16 {
|
||||
b.script = append(b.script, (OP_1-1)+data[0])
|
||||
return b
|
||||
} else if dataLen == 1 && data[0] == 0x81 {
|
||||
b.script = append(b.script, byte(OP_1NEGATE))
|
||||
return b
|
||||
}
|
||||
|
||||
// Use one of the OP_DATA_# opcodes if the length of the data is small
|
||||
// enough so the data push instruction is only a single byte.
|
||||
// Otherwise, choose the smallest possible OP_PUSHDATA# opcode that
|
||||
// can represent the length of the data.
|
||||
if dataLen < OP_PUSHDATA1 {
|
||||
b.script = append(b.script, byte((OP_DATA_1-1)+dataLen))
|
||||
} else if dataLen <= 0xff {
|
||||
b.script = append(b.script, OP_PUSHDATA1, byte(dataLen))
|
||||
} else if dataLen <= 0xffff {
|
||||
buf := make([]byte, 2)
|
||||
binary.LittleEndian.PutUint16(buf, uint16(dataLen))
|
||||
b.script = append(b.script, OP_PUSHDATA2)
|
||||
b.script = append(b.script, buf...)
|
||||
} else {
|
||||
buf := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(buf, uint32(dataLen))
|
||||
b.script = append(b.script, OP_PUSHDATA4)
|
||||
b.script = append(b.script, buf...)
|
||||
}
|
||||
|
||||
// Append the actual data.
|
||||
b.script = append(b.script, data...)
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// AddFullData should not typically be used by ordinary users as it does not
|
||||
// include the checks which prevent data pushes larger than the maximum allowed
|
||||
// sizes which leads to scripts that can't be executed. This is provided for
|
||||
// testing purposes such as regression tests where sizes are intentionally made
|
||||
// larger than allowed.
|
||||
//
|
||||
// Use AddData instead.
|
||||
func (b *ScriptBuilder) AddFullData(data []byte) *ScriptBuilder {
|
||||
if b.err != nil {
|
||||
return b
|
||||
}
|
||||
|
||||
return b.addData(data)
|
||||
}
|
||||
|
||||
// AddData pushes the passed data to the end of the script. It automatically
|
||||
// chooses canonical opcodes depending on the 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 greater than MaxScriptElementSize will not modify the script since
|
||||
// that is not allowed by the script engine. Also, the script will not be
|
||||
// modified if pushing the data would cause the script to exceed the maximum
|
||||
// allowed script engine size.
|
||||
func (b *ScriptBuilder) AddData(data []byte) *ScriptBuilder {
|
||||
if b.err != nil {
|
||||
return b
|
||||
}
|
||||
|
||||
// Pushes that would cause the script to exceed the largest allowed
|
||||
// script size would result in a non-canonical script.
|
||||
dataSize := canonicalDataSize(data)
|
||||
if len(b.script)+dataSize > MaxScriptSize {
|
||||
str := fmt.Sprintf("adding %d bytes of data would exceed the "+
|
||||
"maximum allowed canonical script length of %d",
|
||||
dataSize, MaxScriptSize)
|
||||
b.err = ErrScriptNotCanonical(str)
|
||||
return b
|
||||
}
|
||||
|
||||
// Pushes larger than the max script element size would result in a
|
||||
// script that is not canonical.
|
||||
dataLen := len(data)
|
||||
if dataLen > MaxScriptElementSize {
|
||||
str := fmt.Sprintf("adding a data element of %d bytes would "+
|
||||
"exceed the maximum allowed script element size of %d",
|
||||
dataLen, MaxScriptElementSize)
|
||||
b.err = ErrScriptNotCanonical(str)
|
||||
return b
|
||||
}
|
||||
|
||||
return b.addData(data)
|
||||
}
|
||||
|
||||
// AddInt64 pushes the passed integer to the end of the script. The script will
|
||||
// not be modified if pushing the data would cause the script to exceed the
|
||||
// maximum allowed script engine size.
|
||||
func (b *ScriptBuilder) AddInt64(val int64) *ScriptBuilder {
|
||||
if b.err != nil {
|
||||
return b
|
||||
}
|
||||
|
||||
// Pushes that would cause the script to exceed the largest allowed
|
||||
// script size would result in a non-canonical script.
|
||||
if len(b.script)+1 > MaxScriptSize {
|
||||
str := fmt.Sprintf("adding an integer would exceed the "+
|
||||
"maximum allow canonical script length of %d",
|
||||
MaxScriptSize)
|
||||
b.err = ErrScriptNotCanonical(str)
|
||||
return b
|
||||
}
|
||||
|
||||
// Fast path for small integers and OP_1NEGATE.
|
||||
if val == 0 {
|
||||
b.script = append(b.script, OP_0)
|
||||
return b
|
||||
}
|
||||
if val == -1 || (val >= 1 && val <= 16) {
|
||||
b.script = append(b.script, byte((OP_1-1)+val))
|
||||
return b
|
||||
}
|
||||
|
||||
return b.AddData(scriptNum(val).Bytes())
|
||||
}
|
||||
|
||||
// Reset resets the script so it has no content.
|
||||
func (b *ScriptBuilder) Reset() *ScriptBuilder {
|
||||
b.script = b.script[0:0]
|
||||
b.err = nil
|
||||
return b
|
||||
}
|
||||
|
||||
// Script returns the currently built script. When any errors occurred while
|
||||
// building the script, the script will be returned up the point of the first
|
||||
// error along with the error.
|
||||
func (b *ScriptBuilder) Script() ([]byte, error) {
|
||||
return b.script, b.err
|
||||
}
|
||||
|
||||
// NewScriptBuilder returns a new instance of a script builder. See
|
||||
// ScriptBuilder for details.
|
||||
func NewScriptBuilder() *ScriptBuilder {
|
||||
return &ScriptBuilder{
|
||||
script: make([]byte, 0, defaultScriptAlloc),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user