txscript: Convert to new scriptnum type.
This commit implements a new type, named scriptNum, for handling all numeric values used in scripts and converts the code over to make use of it. This is being done for a few of reasons. First, the consensus rules for handling numeric values in the scripts require special handling with subtle semantics. By encapsulating those details into a type specifically dedicated to that purpose, it simplifies the code and generally helps prevent improper usage. Second, the new type is quite a bit more efficient than big.Ints which are designed to be arbitrarily large and thus involve a lot of heap allocations and additional multi-precision bookkeeping. Because this new type is based on an int64, it allows the numbers to be stack allocated thereby eliminating a lot of GC and also eliminates the extra multi-precision arithmetic bookkeeping. The use of an int64 is possible because the consensus rules dictate that when data is interpreted as a number, it is limited to an int32 even though results outside of this range are allowed so long as they are not interpreted as integers again themselves. Thus, the maximum possible result comes from multiplying a max int32 by itself which safely fits into an int64 and can then still appropriately provide the serialization of the larger number as required by consensus. Finally, it more closely resembles the implementation used by Bitcoin Core and thus makes is easier to compare the behavior between the two implementations. This commit also includes a full suite of tests with 100% coverage of the semantics of the new type.
This commit is contained in:
parent
6e402deb35
commit
b6e52fbd93
9 changed files with 582 additions and 340 deletions
|
@ -182,7 +182,8 @@ func mergeTxStore(txStoreA blockchain.TxStore, txStoreB blockchain.TxStore) {
|
||||||
// the extra nonce as well as additional coinbase flags.
|
// the extra nonce as well as additional coinbase flags.
|
||||||
func standardCoinbaseScript(nextBlockHeight int64, extraNonce uint64) ([]byte, error) {
|
func standardCoinbaseScript(nextBlockHeight int64, extraNonce uint64) ([]byte, error) {
|
||||||
return txscript.NewScriptBuilder().AddInt64(nextBlockHeight).
|
return txscript.NewScriptBuilder().AddInt64(nextBlockHeight).
|
||||||
AddUint64(extraNonce).AddData([]byte(coinbaseFlags)).Script()
|
AddInt64(int64(extraNonce)).AddData([]byte(coinbaseFlags)).
|
||||||
|
Script()
|
||||||
}
|
}
|
||||||
|
|
||||||
// createCoinbaseTx returns a coinbase transaction paying an appropriate subsidy
|
// createCoinbaseTx returns a coinbase transaction paying an appropriate subsidy
|
||||||
|
|
|
@ -548,7 +548,7 @@ func getStack(stack *stack) [][]byte {
|
||||||
array := make([][]byte, stack.Depth())
|
array := make([][]byte, stack.Depth())
|
||||||
for i := range array {
|
for i := range array {
|
||||||
// PeekByteArry can't fail due to overflow, already checked
|
// PeekByteArry can't fail due to overflow, already checked
|
||||||
array[len(array)-i-1], _ = stack.PeekByteArray(i)
|
array[len(array)-i-1], _ = stack.PeekByteArray(int32(i))
|
||||||
}
|
}
|
||||||
return array
|
return array
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash"
|
"hash"
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcec"
|
"github.com/btcsuite/btcd/btcec"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
|
@ -855,7 +854,7 @@ func opcodePushData(op *parsedOpcode, vm *Engine) error {
|
||||||
|
|
||||||
// opcode1Negate pushes -1, encoded as a number, to the data stack.
|
// opcode1Negate pushes -1, encoded as a number, to the data stack.
|
||||||
func opcode1Negate(op *parsedOpcode, vm *Engine) error {
|
func opcode1Negate(op *parsedOpcode, vm *Engine) error {
|
||||||
vm.dstack.PushInt(big.NewInt(-1))
|
vm.dstack.PushInt(scriptNum(-1))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -865,7 +864,7 @@ func opcode1Negate(op *parsedOpcode, vm *Engine) error {
|
||||||
func opcodeN(op *parsedOpcode, vm *Engine) error {
|
func opcodeN(op *parsedOpcode, vm *Engine) error {
|
||||||
// The opcodes are all defined consecutively, so the numeric value is
|
// The opcodes are all defined consecutively, so the numeric value is
|
||||||
// the difference.
|
// the difference.
|
||||||
vm.dstack.PushInt(big.NewInt(int64(op.opcode.value - (OP_1 - 1))))
|
vm.dstack.PushInt(scriptNum((op.opcode.value - (OP_1 - 1))))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1091,7 +1090,7 @@ func opcodeIfDup(op *parsedOpcode, vm *Engine) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push copy of data iff it isn't zero
|
// Push copy of data iff it isn't zero
|
||||||
if val.Sign() != 0 {
|
if val != 0 {
|
||||||
vm.dstack.PushInt(val)
|
vm.dstack.PushInt(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1105,7 +1104,7 @@ func opcodeIfDup(op *parsedOpcode, vm *Engine) error {
|
||||||
// Example with 2 items: [x1 x2] -> [x1 x2 2]
|
// Example with 2 items: [x1 x2] -> [x1 x2 2]
|
||||||
// Example with 3 items: [x1 x2 x3] -> [x1 x2 x3 3]
|
// Example with 3 items: [x1 x2 x3] -> [x1 x2 x3 3]
|
||||||
func opcodeDepth(op *parsedOpcode, vm *Engine) error {
|
func opcodeDepth(op *parsedOpcode, vm *Engine) error {
|
||||||
vm.dstack.PushInt(big.NewInt(int64(vm.dstack.Depth())))
|
vm.dstack.PushInt(scriptNum(vm.dstack.Depth()))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1144,14 +1143,12 @@ func opcodeOver(op *parsedOpcode, vm *Engine) error {
|
||||||
// Example with n=1: [x2 x1 x0 1] -> [x2 x1 x0 x1]
|
// Example with n=1: [x2 x1 x0 1] -> [x2 x1 x0 x1]
|
||||||
// Example with n=2: [x2 x1 x0 2] -> [x2 x1 x0 x2]
|
// Example with n=2: [x2 x1 x0 2] -> [x2 x1 x0 x2]
|
||||||
func opcodePick(op *parsedOpcode, vm *Engine) error {
|
func opcodePick(op *parsedOpcode, vm *Engine) error {
|
||||||
pidx, err := vm.dstack.PopInt()
|
val, err := vm.dstack.PopInt()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// PopInt promises that the int returned is 32 bit.
|
return vm.dstack.PickN(val.Int32())
|
||||||
val := int(pidx.Int64())
|
|
||||||
return vm.dstack.PickN(val)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// opcodeRoll treats the top item on the data stack as an integer and moves
|
// opcodeRoll treats the top item on the data stack as an integer and moves
|
||||||
|
@ -1161,14 +1158,12 @@ func opcodePick(op *parsedOpcode, vm *Engine) error {
|
||||||
// Example with n=1: [x2 x1 x0 1] -> [x2 x0 x1]
|
// Example with n=1: [x2 x1 x0 1] -> [x2 x0 x1]
|
||||||
// Example with n=2: [x2 x1 x0 2] -> [x1 x0 x2]
|
// Example with n=2: [x2 x1 x0 2] -> [x1 x0 x2]
|
||||||
func opcodeRoll(op *parsedOpcode, vm *Engine) error {
|
func opcodeRoll(op *parsedOpcode, vm *Engine) error {
|
||||||
ridx, err := vm.dstack.PopInt()
|
val, err := vm.dstack.PopInt()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// PopInt promises that the int returned is 32 bit.
|
return vm.dstack.RollN(val.Int32())
|
||||||
val := int(ridx.Int64())
|
|
||||||
return vm.dstack.RollN(val)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// opcodeRot rotates the top 3 items on the data stack to the left.
|
// opcodeRot rotates the top 3 items on the data stack to the left.
|
||||||
|
@ -1203,7 +1198,7 @@ func opcodeSize(op *parsedOpcode, vm *Engine) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.dstack.PushInt(big.NewInt(int64(len(so))))
|
vm.dstack.PushInt(scriptNum(len(so)))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1250,7 +1245,7 @@ func opcode1Add(op *parsedOpcode, vm *Engine) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.dstack.PushInt(new(big.Int).Add(m, big.NewInt(1)))
|
vm.dstack.PushInt(m + 1)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1263,7 +1258,7 @@ func opcode1Sub(op *parsedOpcode, vm *Engine) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
vm.dstack.PushInt(new(big.Int).Sub(m, big.NewInt(1)))
|
vm.dstack.PushInt(m - 1)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1278,7 +1273,7 @@ func opcodeNegate(op *parsedOpcode, vm *Engine) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.dstack.PushInt(new(big.Int).Neg(m))
|
vm.dstack.PushInt(-m)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1292,7 +1287,10 @@ func opcodeAbs(op *parsedOpcode, vm *Engine) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.dstack.PushInt(new(big.Int).Abs(m))
|
if m < 0 {
|
||||||
|
m = -m
|
||||||
|
}
|
||||||
|
vm.dstack.PushInt(m)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1314,10 +1312,10 @@ func opcodeNot(op *parsedOpcode, vm *Engine) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.Sign() == 0 {
|
if m == 0 {
|
||||||
vm.dstack.PushInt(big.NewInt(1))
|
vm.dstack.PushInt(scriptNum(1))
|
||||||
} else {
|
} else {
|
||||||
vm.dstack.PushInt(big.NewInt(0))
|
vm.dstack.PushInt(scriptNum(0))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1334,8 +1332,8 @@ func opcode0NotEqual(op *parsedOpcode, vm *Engine) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.Sign() != 0 {
|
if m != 0 {
|
||||||
m.SetInt64(1)
|
m = 1
|
||||||
}
|
}
|
||||||
vm.dstack.PushInt(m)
|
vm.dstack.PushInt(m)
|
||||||
return nil
|
return nil
|
||||||
|
@ -1356,7 +1354,7 @@ func opcodeAdd(op *parsedOpcode, vm *Engine) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.dstack.PushInt(new(big.Int).Add(v0, v1))
|
vm.dstack.PushInt(v0 + v1)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1376,7 +1374,7 @@ func opcodeSub(op *parsedOpcode, vm *Engine) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
vm.dstack.PushInt(new(big.Int).Sub(v1, v0))
|
vm.dstack.PushInt(v1 - v0)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1398,10 +1396,10 @@ func opcodeBoolAnd(op *parsedOpcode, vm *Engine) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if v0.Sign() != 0 && v1.Sign() != 0 {
|
if v0 != 0 && v1 != 0 {
|
||||||
vm.dstack.PushInt(big.NewInt(1))
|
vm.dstack.PushInt(scriptNum(1))
|
||||||
} else {
|
} else {
|
||||||
vm.dstack.PushInt(big.NewInt(0))
|
vm.dstack.PushInt(scriptNum(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -1425,10 +1423,10 @@ func opcodeBoolOr(op *parsedOpcode, vm *Engine) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if v0.Sign() != 0 || v1.Sign() != 0 {
|
if v0 != 0 || v1 != 0 {
|
||||||
vm.dstack.PushInt(big.NewInt(1))
|
vm.dstack.PushInt(scriptNum(1))
|
||||||
} else {
|
} else {
|
||||||
vm.dstack.PushInt(big.NewInt(0))
|
vm.dstack.PushInt(scriptNum(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -1450,10 +1448,10 @@ func opcodeNumEqual(op *parsedOpcode, vm *Engine) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if v0.Cmp(v1) == 0 {
|
if v0 == v1 {
|
||||||
vm.dstack.PushInt(big.NewInt(1))
|
vm.dstack.PushInt(scriptNum(1))
|
||||||
} else {
|
} else {
|
||||||
vm.dstack.PushInt(big.NewInt(0))
|
vm.dstack.PushInt(scriptNum(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -1491,10 +1489,10 @@ func opcodeNumNotEqual(op *parsedOpcode, vm *Engine) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if v0.Cmp(v1) != 0 {
|
if v0 != v1 {
|
||||||
vm.dstack.PushInt(big.NewInt(1))
|
vm.dstack.PushInt(scriptNum(1))
|
||||||
} else {
|
} else {
|
||||||
vm.dstack.PushInt(big.NewInt(0))
|
vm.dstack.PushInt(scriptNum(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -1516,10 +1514,10 @@ func opcodeLessThan(op *parsedOpcode, vm *Engine) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if v1.Cmp(v0) == -1 {
|
if v1 < v0 {
|
||||||
vm.dstack.PushInt(big.NewInt(1))
|
vm.dstack.PushInt(scriptNum(1))
|
||||||
} else {
|
} else {
|
||||||
vm.dstack.PushInt(big.NewInt(0))
|
vm.dstack.PushInt(scriptNum(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -1541,10 +1539,10 @@ func opcodeGreaterThan(op *parsedOpcode, vm *Engine) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if v1.Cmp(v0) == 1 {
|
if v1 > v0 {
|
||||||
vm.dstack.PushInt(big.NewInt(1))
|
vm.dstack.PushInt(scriptNum(1))
|
||||||
} else {
|
} else {
|
||||||
vm.dstack.PushInt(big.NewInt(0))
|
vm.dstack.PushInt(scriptNum(0))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1565,10 +1563,10 @@ func opcodeLessThanOrEqual(op *parsedOpcode, vm *Engine) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if v1.Cmp(v0) <= 0 {
|
if v1 <= v0 {
|
||||||
vm.dstack.PushInt(big.NewInt(1))
|
vm.dstack.PushInt(scriptNum(1))
|
||||||
} else {
|
} else {
|
||||||
vm.dstack.PushInt(big.NewInt(0))
|
vm.dstack.PushInt(scriptNum(0))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1589,10 +1587,10 @@ func opcodeGreaterThanOrEqual(op *parsedOpcode, vm *Engine) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if v1.Cmp(v0) >= 0 {
|
if v1 >= v0 {
|
||||||
vm.dstack.PushInt(big.NewInt(1))
|
vm.dstack.PushInt(scriptNum(1))
|
||||||
} else {
|
} else {
|
||||||
vm.dstack.PushInt(big.NewInt(0))
|
vm.dstack.PushInt(scriptNum(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -1613,10 +1611,10 @@ func opcodeMin(op *parsedOpcode, vm *Engine) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if v1.Cmp(v0) == -1 {
|
if v1 < v0 {
|
||||||
vm.dstack.PushInt(new(big.Int).Set(v1))
|
vm.dstack.PushInt(v1)
|
||||||
} else {
|
} else {
|
||||||
vm.dstack.PushInt(new(big.Int).Set(v0))
|
vm.dstack.PushInt(v0)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -1637,10 +1635,10 @@ func opcodeMax(op *parsedOpcode, vm *Engine) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if v1.Cmp(v0) == 1 {
|
if v1 > v0 {
|
||||||
vm.dstack.PushInt(new(big.Int).Set(v1))
|
vm.dstack.PushInt(v1)
|
||||||
} else {
|
} else {
|
||||||
vm.dstack.PushInt(new(big.Int).Set(v0))
|
vm.dstack.PushInt(v0)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -1670,10 +1668,10 @@ func opcodeWithin(op *parsedOpcode, vm *Engine) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if x.Cmp(minVal) >= 0 && x.Cmp(maxVal) == -1 {
|
if x >= minVal && x < maxVal {
|
||||||
vm.dstack.PushInt(big.NewInt(1))
|
vm.dstack.PushInt(scriptNum(1))
|
||||||
} else {
|
} else {
|
||||||
vm.dstack.PushInt(big.NewInt(0))
|
vm.dstack.PushInt(scriptNum(0))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1904,8 +1902,7 @@ func opcodeCheckMultiSig(op *parsedOpcode, vm *Engine) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// PopInt promises that the int returned is 32 bit.
|
numPubKeys := int(numKeys.Int32())
|
||||||
numPubKeys := int(numKeys.Int64())
|
|
||||||
if numPubKeys < 0 || numPubKeys > MaxPubKeysPerMultiSig {
|
if numPubKeys < 0 || numPubKeys > MaxPubKeysPerMultiSig {
|
||||||
return ErrStackTooManyPubkeys
|
return ErrStackTooManyPubkeys
|
||||||
}
|
}
|
||||||
|
@ -1927,8 +1924,7 @@ func opcodeCheckMultiSig(op *parsedOpcode, vm *Engine) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// PopInt promises that the int returned is 32 bit.
|
numSignatures := int(numSigs.Int32())
|
||||||
numSignatures := int(numSigs.Int64())
|
|
||||||
if numSignatures < 0 {
|
if numSignatures < 0 {
|
||||||
return fmt.Errorf("number of signatures '%d' is less than 0",
|
return fmt.Errorf("number of signatures '%d' is less than 0",
|
||||||
numSignatures)
|
numSignatures)
|
||||||
|
|
|
@ -7,7 +7,6 @@ package txscript
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -228,38 +227,7 @@ func (b *ScriptBuilder) AddInt64(val int64) *ScriptBuilder {
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
return b.AddData(fromInt(new(big.Int).SetInt64(val)))
|
return b.AddData(scriptNum(val).Bytes())
|
||||||
}
|
|
||||||
|
|
||||||
// AddUint64 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) AddUint64(val uint64) *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 unsigned integer would exceed "+
|
|
||||||
"the maximum allow canonical script length of %d",
|
|
||||||
maxScriptSize)
|
|
||||||
b.err = ErrScriptNotCanonical(str)
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fast path for small integers.
|
|
||||||
if val == 0 {
|
|
||||||
b.script = append(b.script, OP_0)
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
if val >= 1 && val <= 16 {
|
|
||||||
b.script = append(b.script, byte((OP_1-1)+val))
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
return b.AddData(fromInt(new(big.Int).SetUint64(val)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset resets the script so it has no content.
|
// Reset resets the script so it has no content.
|
||||||
|
|
|
@ -129,62 +129,6 @@ func TestScriptBuilderAddInt64(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestScriptBuilderAddUint64 tests that pushing unsigned integers to a script
|
|
||||||
// via the ScriptBuilder API works as expected.
|
|
||||||
func TestScriptBuilderAddUint64(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
val uint64
|
|
||||||
expected []byte
|
|
||||||
}{
|
|
||||||
{name: "push small int 0", val: 0, expected: []byte{txscript.OP_0}},
|
|
||||||
{name: "push small int 1", val: 1, expected: []byte{txscript.OP_1}},
|
|
||||||
{name: "push small int 2", val: 2, expected: []byte{txscript.OP_2}},
|
|
||||||
{name: "push small int 3", val: 3, expected: []byte{txscript.OP_3}},
|
|
||||||
{name: "push small int 4", val: 4, expected: []byte{txscript.OP_4}},
|
|
||||||
{name: "push small int 5", val: 5, expected: []byte{txscript.OP_5}},
|
|
||||||
{name: "push small int 6", val: 6, expected: []byte{txscript.OP_6}},
|
|
||||||
{name: "push small int 7", val: 7, expected: []byte{txscript.OP_7}},
|
|
||||||
{name: "push small int 8", val: 8, expected: []byte{txscript.OP_8}},
|
|
||||||
{name: "push small int 9", val: 9, expected: []byte{txscript.OP_9}},
|
|
||||||
{name: "push small int 10", val: 10, expected: []byte{txscript.OP_10}},
|
|
||||||
{name: "push small int 11", val: 11, expected: []byte{txscript.OP_11}},
|
|
||||||
{name: "push small int 12", val: 12, expected: []byte{txscript.OP_12}},
|
|
||||||
{name: "push small int 13", val: 13, expected: []byte{txscript.OP_13}},
|
|
||||||
{name: "push small int 14", val: 14, expected: []byte{txscript.OP_14}},
|
|
||||||
{name: "push small int 15", val: 15, expected: []byte{txscript.OP_15}},
|
|
||||||
{name: "push small int 16", val: 16, expected: []byte{txscript.OP_16}},
|
|
||||||
{name: "push 17", val: 17, expected: []byte{txscript.OP_DATA_1, 0x11}},
|
|
||||||
{name: "push 65", val: 65, expected: []byte{txscript.OP_DATA_1, 0x41}},
|
|
||||||
{name: "push 127", val: 127, expected: []byte{txscript.OP_DATA_1, 0x7f}},
|
|
||||||
{name: "push 128", val: 128, expected: []byte{txscript.OP_DATA_2, 0x80, 0}},
|
|
||||||
{name: "push 255", val: 255, expected: []byte{txscript.OP_DATA_2, 0xff, 0}},
|
|
||||||
{name: "push 256", val: 256, expected: []byte{txscript.OP_DATA_2, 0, 0x01}},
|
|
||||||
{name: "push 32767", val: 32767, expected: []byte{txscript.OP_DATA_2, 0xff, 0x7f}},
|
|
||||||
{name: "push 32768", val: 32768, expected: []byte{txscript.OP_DATA_3, 0, 0x80, 0}},
|
|
||||||
}
|
|
||||||
|
|
||||||
builder := txscript.NewScriptBuilder()
|
|
||||||
t.Logf("Running %d tests", len(tests))
|
|
||||||
for i, test := range tests {
|
|
||||||
builder.Reset().AddUint64(test.val)
|
|
||||||
result, err := builder.Script()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("ScriptBuilder.AddUint64 #%d (%s) unexpected "+
|
|
||||||
"error: %v", i, test.name, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !bytes.Equal(result, test.expected) {
|
|
||||||
t.Errorf("ScriptBuilder.AddUint64 #%d (%s) wrong result\n"+
|
|
||||||
"got: %x\nwant: %x", i, test.name, result,
|
|
||||||
test.expected)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestScriptBuilderAddData tests that pushing data to a script via the
|
// TestScriptBuilderAddData tests that pushing data to a script via the
|
||||||
// ScriptBuilder API works as expected and conforms to BIP0062.
|
// ScriptBuilder API works as expected and conforms to BIP0062.
|
||||||
func TestScriptBuilderAddData(t *testing.T) {
|
func TestScriptBuilderAddData(t *testing.T) {
|
||||||
|
@ -373,19 +317,6 @@ func TestExceedMaxScriptSize(t *testing.T) {
|
||||||
t.Fatalf("ScriptBuilder.AddInt64 unexpected modified script - "+
|
t.Fatalf("ScriptBuilder.AddInt64 unexpected modified script - "+
|
||||||
"got len %d, want len %d", len(script), len(origScript))
|
"got len %d, want len %d", len(script), len(origScript))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure adding an unsigned integer that would exceed the maximum size
|
|
||||||
// of the script does not add the data.
|
|
||||||
builder.Reset().AddFullData(make([]byte, maxScriptSize-3))
|
|
||||||
script, err = builder.AddUint64(0).Script()
|
|
||||||
if _, ok := err.(txscript.ErrScriptNotCanonical); !ok || err == nil {
|
|
||||||
t.Fatalf("ScriptBuilder.AddUint64 unexpected modified script - "+
|
|
||||||
"got len %d, want len %d", len(script), len(origScript))
|
|
||||||
}
|
|
||||||
if !bytes.Equal(script, origScript) {
|
|
||||||
t.Fatalf("ScriptBuilder.AddUint64 unexpected modified script - "+
|
|
||||||
"got len %d, want len %d", len(script), len(origScript))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestErroredScript ensures that all of the functions that can be used to add
|
// TestErroredScript ensures that all of the functions that can be used to add
|
||||||
|
@ -457,17 +388,6 @@ func TestErroredScript(t *testing.T) {
|
||||||
"got len %d, want len %d", len(script), len(origScript))
|
"got len %d, want len %d", len(script), len(origScript))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure adding an unsigned integer to a script that has errored
|
|
||||||
// doesn't succeed.
|
|
||||||
script, err = builder.AddUint64(0).Script()
|
|
||||||
if _, ok := err.(txscript.ErrScriptNotCanonical); !ok || err == nil {
|
|
||||||
t.Fatal("ScriptBuilder.AddUint64 succeeded on errored script")
|
|
||||||
}
|
|
||||||
if !bytes.Equal(script, origScript) {
|
|
||||||
t.Fatalf("ScriptBuilder.AddUint64 unexpected modified script - "+
|
|
||||||
"got len %d, want len %d", len(script), len(origScript))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the error has a message set.
|
// Ensure the error has a message set.
|
||||||
if err.Error() == "" {
|
if err.Error() == "" {
|
||||||
t.Fatal("ErrScriptNotCanonical.Error does not have any text")
|
t.Fatal("ErrScriptNotCanonical.Error does not have any text")
|
||||||
|
|
205
txscript/scriptnum.go
Normal file
205
txscript/scriptnum.go
Normal file
|
@ -0,0 +1,205 @@
|
||||||
|
// Copyright (c) 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
|
||||||
|
|
||||||
|
const (
|
||||||
|
maxInt32 = 1<<31 - 1
|
||||||
|
minInt32 = -1 << 31
|
||||||
|
|
||||||
|
// maxScriptNumLen is the maximum number of bytes data being interpreted
|
||||||
|
// as an integer may be.
|
||||||
|
maxScriptNumLen = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
// scriptNum represents a numeric value used in the scripting engine with
|
||||||
|
// special handling to deal with the subtle semantics required by consensus.
|
||||||
|
//
|
||||||
|
// All numbers are stored on the data and alternate stacks encoded as little
|
||||||
|
// endian with a sign bit. All numeric opcodes such as OP_ADD, OP_SUB,
|
||||||
|
// and OP_MUL, are only allowed to operate on 4-byte integers in the range
|
||||||
|
// [-2^31 + 1, 2^31 - 1], however the results of numeric operations may overflow
|
||||||
|
// and remain valid so long as they are not used as inputs to other numeric
|
||||||
|
// operations or otherwise interpreted as an integer.
|
||||||
|
//
|
||||||
|
// For example, it is possible for OP_ADD to have 2^31 - 1 for its two operands
|
||||||
|
// resulting 2^32 - 2, which overflows, but is still pushed to the stack as the
|
||||||
|
// result of the addition. That value can then be used as input to OP_VERIFY
|
||||||
|
// which will succeed because the data is being interpreted as a boolean.
|
||||||
|
// However, if that same value were to be used as input to another numeric
|
||||||
|
// opcode, such as OP_SUB, it must fail.
|
||||||
|
//
|
||||||
|
// This type handles the aforementioned requirements by storing all numeric
|
||||||
|
// operation results as an int64 to handle overflow and provides the Bytes
|
||||||
|
// method to get the serialized representation (including values that overflow).
|
||||||
|
//
|
||||||
|
// Then, whenever data is interpreted as an integer, it is converted to this
|
||||||
|
// type by using the makeScriptNum function which will return an error if the
|
||||||
|
// number is out of range (or not minimally encoded depending on a flag). Since
|
||||||
|
// all numeric opcodes involve pulling data from the stack and interpreting it
|
||||||
|
// as an integer, it provides the required behavior.
|
||||||
|
type scriptNum int64
|
||||||
|
|
||||||
|
// checkMinimalDataEncoding returns whether or not the passed byte array adheres
|
||||||
|
// to the minimal encoding requirements.
|
||||||
|
func checkMinimalDataEncoding(v []byte) error {
|
||||||
|
if len(v) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the number is encoded with the minimum possible
|
||||||
|
// number of bytes.
|
||||||
|
//
|
||||||
|
// If the most-significant-byte - excluding the sign bit - is zero
|
||||||
|
// then we're not minimal. Note how this test also rejects the
|
||||||
|
// negative-zero encoding, [0x80].
|
||||||
|
if v[len(v)-1]&0x7f == 0 {
|
||||||
|
// One exception: if there's more than one byte and the most
|
||||||
|
// significant bit of the second-most-significant-byte is set
|
||||||
|
// it would conflict with the sign bit. An example of this case
|
||||||
|
// is +-255, which encode to 0xff00 and 0xff80 respectively.
|
||||||
|
// (big-endian).
|
||||||
|
if len(v) == 1 || v[len(v)-2]&0x80 == 0 {
|
||||||
|
return ErrStackMinimalData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes returns the number serialized as a little endian with a sign bit.
|
||||||
|
//
|
||||||
|
// Example encodings:
|
||||||
|
// 127 -> [0x7f]
|
||||||
|
// -127 -> [0xff]
|
||||||
|
// 128 -> [0x80 0x00]
|
||||||
|
// -128 -> [0x80 0x80]
|
||||||
|
// 129 -> [0x81 0x00]
|
||||||
|
// -129 -> [0x81 0x80]
|
||||||
|
// 256 -> [0x00 0x01]
|
||||||
|
// -256 -> [0x00 0x81]
|
||||||
|
// 32767 -> [0xff 0x7f]
|
||||||
|
// -32767 -> [0xff 0xff]
|
||||||
|
// 32768 -> [0x00 0x80 0x00]
|
||||||
|
// -32768 -> [0x00 0x80 0x80]
|
||||||
|
func (n scriptNum) Bytes() []byte {
|
||||||
|
// Zero encodes as an empty byte slice.
|
||||||
|
if n == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Take the absolute value and keep track of whether it was originally
|
||||||
|
// negative.
|
||||||
|
isNegative := n < 0
|
||||||
|
if isNegative {
|
||||||
|
n = -n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode to little endian. The maximum number of encoded bytes is 9
|
||||||
|
// (8 bytes for max int64 plus a potential byte for sign extension).
|
||||||
|
result := make([]byte, 0, 9)
|
||||||
|
for n > 0 {
|
||||||
|
result = append(result, byte(n&0xff))
|
||||||
|
n >>= 8
|
||||||
|
}
|
||||||
|
|
||||||
|
// When the most significant byte already has the high bit set, an
|
||||||
|
// additional high byte is required to indicate whether the number is
|
||||||
|
// negative or positive. The additional byte is removed when converting
|
||||||
|
// back to an integral and its high bit is used to denote the sign.
|
||||||
|
//
|
||||||
|
// Otherwise, when the most significant byte does not already have the
|
||||||
|
// high bit set, use it to indicate the value is negative, if needed.
|
||||||
|
if result[len(result)-1]&0x80 != 0 {
|
||||||
|
extraByte := byte(0x00)
|
||||||
|
if isNegative {
|
||||||
|
extraByte = 0x80
|
||||||
|
}
|
||||||
|
result = append(result, extraByte)
|
||||||
|
|
||||||
|
} else if isNegative {
|
||||||
|
result[len(result)-1] |= 0x80
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Int32 returns the script number clamped to a valid int32. That is to say
|
||||||
|
// when the script number is higher than the max allowed int32, the max int32
|
||||||
|
// value is returned and vice versa for the minimum value. Note that this
|
||||||
|
// behavior is different from a simple int32 cast because that truncates
|
||||||
|
// and the consensus rules dictate numbers which are directly cast to ints
|
||||||
|
// provide this behavior.
|
||||||
|
//
|
||||||
|
// In practice, the number should never really be out of range since it will
|
||||||
|
// have been created with makeScriptNum which rejects them, but in case
|
||||||
|
// something in the future ends up calling this function against the result
|
||||||
|
// of some arithmetic, which IS allowed to be out of range before being
|
||||||
|
// reinterpreted as an integer, this will provide the correct behavior.
|
||||||
|
func (n scriptNum) Int32() int32 {
|
||||||
|
if n > maxInt32 {
|
||||||
|
return maxInt32
|
||||||
|
}
|
||||||
|
|
||||||
|
if n < minInt32 {
|
||||||
|
return minInt32
|
||||||
|
}
|
||||||
|
|
||||||
|
return int32(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// makeScriptNum interprets the passed serialized bytes as an encoded integer
|
||||||
|
// and returns the result as a script number.
|
||||||
|
//
|
||||||
|
// Since the consensus rules dictate the serialized bytes interpreted as ints
|
||||||
|
// are only allowed to be in the range [-2^31 + 1, 2^31 - 1], an error will be
|
||||||
|
// returned when the provided bytes would result in a number outside of that
|
||||||
|
// range.
|
||||||
|
//
|
||||||
|
// The requireMinimal flag causes an error to be returned if additional checks
|
||||||
|
// on the encoding determine it is not represented with the smallest possible
|
||||||
|
// number of bytes or is the negative 0 encoding, [0x80]. For example, consider
|
||||||
|
// the number 127. It could be encoded as [0x7f], [0x7f 0x00],
|
||||||
|
// [0x7f 0x00 0x00 ...], etc. All forms except [0x7f] will return an error with
|
||||||
|
// requireMinimal enabled.
|
||||||
|
//
|
||||||
|
// See the Bytes function documentation for example encodings.
|
||||||
|
func makeScriptNum(v []byte, requireMinimal bool) (scriptNum, error) {
|
||||||
|
// Interpreting data as an integer requires that it is not larger than
|
||||||
|
// a 32-bit integer.
|
||||||
|
if len(v) > maxScriptNumLen {
|
||||||
|
return 0, ErrStackNumberTooBig
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enforce minimal encoded if requested.
|
||||||
|
if requireMinimal {
|
||||||
|
if err := checkMinimalDataEncoding(v); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zero is encoded as an empty byte slice.
|
||||||
|
if len(v) == 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode from little endian.
|
||||||
|
var result int64
|
||||||
|
for i, val := range v {
|
||||||
|
result |= int64(val) << uint8(8*i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// When the most significant byte of the input bytes has the sign bit
|
||||||
|
// set, the result is negative. So, remove the sign bit from the result
|
||||||
|
// and make it negative.
|
||||||
|
if v[len(v)-1]&0x80 != 0 {
|
||||||
|
// The maximum length of v has already been determined to be 4
|
||||||
|
// above, so uint8 is enough to cover the max possible shift
|
||||||
|
// value of 24.
|
||||||
|
result &= ^(int64(0x80) << uint8(8*(len(v)-1)))
|
||||||
|
return scriptNum(-result), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return scriptNum(result), nil
|
||||||
|
}
|
256
txscript/scriptnum_test.go
Normal file
256
txscript/scriptnum_test.go
Normal file
|
@ -0,0 +1,256 @@
|
||||||
|
// Copyright (c) 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 (
|
||||||
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// hexToBytes converts the passed hex string into bytes and will panic if there
|
||||||
|
// is an error. This is only provided for the hard-coded constants so errors in
|
||||||
|
// the source code can be detected. It will only (and must only) be called with
|
||||||
|
// hard-coded values.
|
||||||
|
func hexToBytes(s string) []byte {
|
||||||
|
b, err := hex.DecodeString(s)
|
||||||
|
if err != nil {
|
||||||
|
panic("invalid hex in source file: " + s)
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestScriptNumBytes ensures that converting from integral script numbers to
|
||||||
|
// byte representations works as expected.
|
||||||
|
func TestScriptNumBytes(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
num scriptNum
|
||||||
|
serialized []byte
|
||||||
|
}{
|
||||||
|
{0, nil},
|
||||||
|
{1, hexToBytes("01")},
|
||||||
|
{-1, hexToBytes("81")},
|
||||||
|
{127, hexToBytes("7f")},
|
||||||
|
{-127, hexToBytes("ff")},
|
||||||
|
{128, hexToBytes("8000")},
|
||||||
|
{-128, hexToBytes("8080")},
|
||||||
|
{129, hexToBytes("8100")},
|
||||||
|
{-129, hexToBytes("8180")},
|
||||||
|
{256, hexToBytes("0001")},
|
||||||
|
{-256, hexToBytes("0081")},
|
||||||
|
{32767, hexToBytes("ff7f")},
|
||||||
|
{-32767, hexToBytes("ffff")},
|
||||||
|
{32768, hexToBytes("008000")},
|
||||||
|
{-32768, hexToBytes("008080")},
|
||||||
|
{65535, hexToBytes("ffff00")},
|
||||||
|
{-65535, hexToBytes("ffff80")},
|
||||||
|
{524288, hexToBytes("000008")},
|
||||||
|
{-524288, hexToBytes("000088")},
|
||||||
|
{7340032, hexToBytes("000070")},
|
||||||
|
{-7340032, hexToBytes("0000f0")},
|
||||||
|
{8388608, hexToBytes("00008000")},
|
||||||
|
{-8388608, hexToBytes("00008080")},
|
||||||
|
{2147483647, hexToBytes("ffffff7f")},
|
||||||
|
{-2147483647, hexToBytes("ffffffff")},
|
||||||
|
|
||||||
|
// Values that are out of range for data that is interpreted as
|
||||||
|
// numbers, but are allowed as the result of numeric operations.
|
||||||
|
{2147483648, hexToBytes("0000008000")},
|
||||||
|
{-2147483648, hexToBytes("0000008080")},
|
||||||
|
{2415919104, hexToBytes("0000009000")},
|
||||||
|
{-2415919104, hexToBytes("0000009080")},
|
||||||
|
{4294967295, hexToBytes("ffffffff00")},
|
||||||
|
{-4294967295, hexToBytes("ffffffff80")},
|
||||||
|
{4294967296, hexToBytes("0000000001")},
|
||||||
|
{-4294967296, hexToBytes("0000000081")},
|
||||||
|
{281474976710655, hexToBytes("ffffffffffff00")},
|
||||||
|
{-281474976710655, hexToBytes("ffffffffffff80")},
|
||||||
|
{72057594037927935, hexToBytes("ffffffffffffff00")},
|
||||||
|
{-72057594037927935, hexToBytes("ffffffffffffff80")},
|
||||||
|
{9223372036854775807, hexToBytes("ffffffffffffff7f")},
|
||||||
|
{-9223372036854775807, hexToBytes("ffffffffffffffff")},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
gotBytes := test.num.Bytes()
|
||||||
|
if !bytes.Equal(gotBytes, test.serialized) {
|
||||||
|
t.Errorf("Bytes: did not get expected bytes for %d - "+
|
||||||
|
"got %x, want %x", test.num, gotBytes,
|
||||||
|
test.serialized)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestMakeScriptNum ensures that converting from byte representations to
|
||||||
|
// integral script numbers works as expected.
|
||||||
|
func TestMakeScriptNum(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
serialized []byte
|
||||||
|
num scriptNum
|
||||||
|
minimalEncoding bool
|
||||||
|
err error
|
||||||
|
}{
|
||||||
|
// Minimal encoding must reject negative 0.
|
||||||
|
{hexToBytes("80"), 0, true, ErrStackMinimalData},
|
||||||
|
|
||||||
|
// Minimally encoded valid values with minimal encoding flag.
|
||||||
|
// Should not error and return expected integral number.
|
||||||
|
{nil, 0, true, nil},
|
||||||
|
{hexToBytes("01"), 1, true, nil},
|
||||||
|
{hexToBytes("81"), -1, true, nil},
|
||||||
|
{hexToBytes("7f"), 127, true, nil},
|
||||||
|
{hexToBytes("ff"), -127, true, nil},
|
||||||
|
{hexToBytes("8000"), 128, true, nil},
|
||||||
|
{hexToBytes("8080"), -128, true, nil},
|
||||||
|
{hexToBytes("8100"), 129, true, nil},
|
||||||
|
{hexToBytes("8180"), -129, true, nil},
|
||||||
|
{hexToBytes("0001"), 256, true, nil},
|
||||||
|
{hexToBytes("0081"), -256, true, nil},
|
||||||
|
{hexToBytes("ff7f"), 32767, true, nil},
|
||||||
|
{hexToBytes("ffff"), -32767, true, nil},
|
||||||
|
{hexToBytes("008000"), 32768, true, nil},
|
||||||
|
{hexToBytes("008080"), -32768, true, nil},
|
||||||
|
{hexToBytes("ffff00"), 65535, true, nil},
|
||||||
|
{hexToBytes("ffff80"), -65535, true, nil},
|
||||||
|
{hexToBytes("000008"), 524288, true, nil},
|
||||||
|
{hexToBytes("000088"), -524288, true, nil},
|
||||||
|
{hexToBytes("000070"), 7340032, true, nil},
|
||||||
|
{hexToBytes("0000f0"), -7340032, true, nil},
|
||||||
|
{hexToBytes("00008000"), 8388608, true, nil},
|
||||||
|
{hexToBytes("00008080"), -8388608, true, nil},
|
||||||
|
{hexToBytes("ffffff7f"), 2147483647, true, nil},
|
||||||
|
{hexToBytes("ffffffff"), -2147483647, true, nil},
|
||||||
|
|
||||||
|
// Minimally encoded values that are out of range for data that
|
||||||
|
// is interpreted as script numbers with the minimal encoding
|
||||||
|
// flag set. Should error and return 0.
|
||||||
|
{hexToBytes("0000008000"), 0, true, ErrStackNumberTooBig},
|
||||||
|
{hexToBytes("0000008080"), 0, true, ErrStackNumberTooBig},
|
||||||
|
{hexToBytes("0000009000"), 0, true, ErrStackNumberTooBig},
|
||||||
|
{hexToBytes("0000009080"), 0, true, ErrStackNumberTooBig},
|
||||||
|
{hexToBytes("ffffffff00"), 0, true, ErrStackNumberTooBig},
|
||||||
|
{hexToBytes("ffffffff80"), 0, true, ErrStackNumberTooBig},
|
||||||
|
{hexToBytes("0000000001"), 0, true, ErrStackNumberTooBig},
|
||||||
|
{hexToBytes("0000000081"), 0, true, ErrStackNumberTooBig},
|
||||||
|
{hexToBytes("ffffffffffff00"), 0, true, ErrStackNumberTooBig},
|
||||||
|
{hexToBytes("ffffffffffff80"), 0, true, ErrStackNumberTooBig},
|
||||||
|
{hexToBytes("ffffffffffffff00"), 0, true, ErrStackNumberTooBig},
|
||||||
|
{hexToBytes("ffffffffffffff80"), 0, true, ErrStackNumberTooBig},
|
||||||
|
{hexToBytes("ffffffffffffff7f"), 0, true, ErrStackNumberTooBig},
|
||||||
|
{hexToBytes("ffffffffffffffff"), 0, true, ErrStackNumberTooBig},
|
||||||
|
|
||||||
|
// Non-minimally encoded, but otherwise valid values with
|
||||||
|
// minimal encoding flag. Should error and return 0.
|
||||||
|
{hexToBytes("00"), 0, true, ErrStackMinimalData}, // 0
|
||||||
|
{hexToBytes("0100"), 0, true, ErrStackMinimalData}, // 1
|
||||||
|
{hexToBytes("7f00"), 0, true, ErrStackMinimalData}, // 127
|
||||||
|
{hexToBytes("800000"), 0, true, ErrStackMinimalData}, // 128
|
||||||
|
{hexToBytes("810000"), 0, true, ErrStackMinimalData}, // 129
|
||||||
|
{hexToBytes("000100"), 0, true, ErrStackMinimalData}, // 256
|
||||||
|
{hexToBytes("ff7f00"), 0, true, ErrStackMinimalData}, // 32767
|
||||||
|
{hexToBytes("00800000"), 0, true, ErrStackMinimalData}, // 32768
|
||||||
|
{hexToBytes("ffff0000"), 0, true, ErrStackMinimalData}, // 65535
|
||||||
|
{hexToBytes("00000800"), 0, true, ErrStackMinimalData}, // 524288
|
||||||
|
{hexToBytes("00007000"), 0, true, ErrStackMinimalData}, // 7340032
|
||||||
|
|
||||||
|
// Non-minimally encoded, but otherwise valid values without
|
||||||
|
// minimal encoding flag. Should not error and return expected
|
||||||
|
// integral number.
|
||||||
|
{hexToBytes("00"), 0, false, nil},
|
||||||
|
{hexToBytes("0100"), 1, false, nil},
|
||||||
|
{hexToBytes("7f00"), 127, false, nil},
|
||||||
|
{hexToBytes("800000"), 128, false, nil},
|
||||||
|
{hexToBytes("810000"), 129, false, nil},
|
||||||
|
{hexToBytes("000100"), 256, false, nil},
|
||||||
|
{hexToBytes("ff7f00"), 32767, false, nil},
|
||||||
|
{hexToBytes("00800000"), 32768, false, nil},
|
||||||
|
{hexToBytes("ffff0000"), 65535, false, nil},
|
||||||
|
{hexToBytes("00000800"), 524288, false, nil},
|
||||||
|
{hexToBytes("00007000"), 7340032, false, nil},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
gotNum, err := makeScriptNum(test.serialized, test.minimalEncoding)
|
||||||
|
if err != test.err {
|
||||||
|
t.Errorf("makeScriptNum: did not received expected "+
|
||||||
|
"error for %x - got %v, want %v",
|
||||||
|
test.serialized, err, test.err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if gotNum != test.num {
|
||||||
|
t.Errorf("makeScriptNum: did not get expected number "+
|
||||||
|
"for %x - got %d, want %d", test.serialized,
|
||||||
|
gotNum, test.num)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestScriptNumInt32 ensures that the Int32 function on script number behaves
|
||||||
|
// as expected.
|
||||||
|
func TestScriptNumInt32(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
in scriptNum
|
||||||
|
want int32
|
||||||
|
}{
|
||||||
|
// Values inside the valid int32 range are just the values
|
||||||
|
// themselves cast to an int32.
|
||||||
|
{0, 0},
|
||||||
|
{1, 1},
|
||||||
|
{-1, -1},
|
||||||
|
{127, 127},
|
||||||
|
{-127, -127},
|
||||||
|
{128, 128},
|
||||||
|
{-128, -128},
|
||||||
|
{129, 129},
|
||||||
|
{-129, -129},
|
||||||
|
{256, 256},
|
||||||
|
{-256, -256},
|
||||||
|
{32767, 32767},
|
||||||
|
{-32767, -32767},
|
||||||
|
{32768, 32768},
|
||||||
|
{-32768, -32768},
|
||||||
|
{65535, 65535},
|
||||||
|
{-65535, -65535},
|
||||||
|
{524288, 524288},
|
||||||
|
{-524288, -524288},
|
||||||
|
{7340032, 7340032},
|
||||||
|
{-7340032, -7340032},
|
||||||
|
{8388608, 8388608},
|
||||||
|
{-8388608, -8388608},
|
||||||
|
{2147483647, 2147483647},
|
||||||
|
{-2147483647, -2147483647},
|
||||||
|
{-2147483648, -2147483648},
|
||||||
|
|
||||||
|
// Values outside of the valid int32 range are limited to int32.
|
||||||
|
{2147483648, 2147483647},
|
||||||
|
{-2147483649, -2147483648},
|
||||||
|
{1152921504606846975, 2147483647},
|
||||||
|
{-1152921504606846975, -2147483648},
|
||||||
|
{2305843009213693951, 2147483647},
|
||||||
|
{-2305843009213693951, -2147483648},
|
||||||
|
{4611686018427387903, 2147483647},
|
||||||
|
{-4611686018427387903, -2147483648},
|
||||||
|
{9223372036854775807, 2147483647},
|
||||||
|
{-9223372036854775808, -2147483648},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
got := test.in.Int32()
|
||||||
|
if got != test.want {
|
||||||
|
t.Errorf("Int32: did not get expected value for %d - "+
|
||||||
|
"got %d, want %d", test.in, got, test.want)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,79 +4,7 @@
|
||||||
|
|
||||||
package txscript
|
package txscript
|
||||||
|
|
||||||
import (
|
import "encoding/hex"
|
||||||
"encoding/hex"
|
|
||||||
"math/big"
|
|
||||||
)
|
|
||||||
|
|
||||||
// asInt converts a byte array to a bignum by treating it as a little endian
|
|
||||||
// number with sign bit.
|
|
||||||
func asInt(v []byte) (*big.Int, error) {
|
|
||||||
// Only 32bit numbers allowed.
|
|
||||||
if len(v) > 4 {
|
|
||||||
return nil, ErrStackNumberTooBig
|
|
||||||
}
|
|
||||||
if len(v) == 0 {
|
|
||||||
return big.NewInt(0), nil
|
|
||||||
}
|
|
||||||
negative := false
|
|
||||||
origlen := len(v)
|
|
||||||
msb := v[len(v)-1]
|
|
||||||
if msb&0x80 == 0x80 {
|
|
||||||
negative = true
|
|
||||||
// remove sign bit
|
|
||||||
msb &= 0x7f
|
|
||||||
}
|
|
||||||
// trim leading 0 bytes
|
|
||||||
for ; msb == 0; msb = v[len(v)-1] {
|
|
||||||
v = v[:len(v)-1]
|
|
||||||
if len(v) == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// reverse bytes with a copy since stack is immutable.
|
|
||||||
intArray := make([]byte, len(v))
|
|
||||||
for i := range v {
|
|
||||||
intArray[len(v)-i-1] = v[i]
|
|
||||||
}
|
|
||||||
// IFF the value is negative and no 0 bytes were trimmed,
|
|
||||||
// the leading byte needs to be sign corrected
|
|
||||||
if negative && len(intArray) == origlen {
|
|
||||||
intArray[0] &= 0x7f
|
|
||||||
}
|
|
||||||
|
|
||||||
num := new(big.Int).SetBytes(intArray)
|
|
||||||
if negative {
|
|
||||||
num = num.Neg(num)
|
|
||||||
}
|
|
||||||
return num, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// fromInt provies a Big.Int in little endian format with the high bit of the
|
|
||||||
// msb donating sign.
|
|
||||||
func fromInt(v *big.Int) []byte {
|
|
||||||
negative := false
|
|
||||||
if v.Sign() == -1 {
|
|
||||||
negative = true
|
|
||||||
}
|
|
||||||
// Int.Bytes() trims leading zeros for us, so we don't have to.
|
|
||||||
b := v.Bytes()
|
|
||||||
if len(b) == 0 {
|
|
||||||
return []byte{}
|
|
||||||
}
|
|
||||||
arr := make([]byte, len(b))
|
|
||||||
for i := range b {
|
|
||||||
arr[len(b)-i-1] = b[i]
|
|
||||||
}
|
|
||||||
// if would otherwise be negative, add a zero byte
|
|
||||||
if arr[len(arr)-1]&0x80 == 0x80 {
|
|
||||||
arr = append(arr, 0)
|
|
||||||
}
|
|
||||||
if negative {
|
|
||||||
arr[len(arr)-1] |= 0x80
|
|
||||||
}
|
|
||||||
return arr
|
|
||||||
}
|
|
||||||
|
|
||||||
// asBool gets the boolean value of the byte array.
|
// asBool gets the boolean value of the byte array.
|
||||||
func asBool(t []byte) bool {
|
func asBool(t []byte) bool {
|
||||||
|
@ -105,35 +33,9 @@ type stack struct {
|
||||||
verifyMinimalData bool
|
verifyMinimalData bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// checkMinimalData returns whether or not the passed byte array adheres to
|
|
||||||
// the minimal encoding requirements, if enabled.
|
|
||||||
func (s *stack) checkMinimalData(so []byte) error {
|
|
||||||
if !s.verifyMinimalData || len(so) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that the number is encoded with the minimum possible
|
|
||||||
// number of bytes.
|
|
||||||
//
|
|
||||||
// If the most-significant-byte - excluding the sign bit - is zero
|
|
||||||
// then we're not minimal. Note how this test also rejects the
|
|
||||||
// negative-zero encoding, 0x80.
|
|
||||||
if so[len(so)-1]&0x7f == 0 {
|
|
||||||
// One exception: if there's more than one byte and the most
|
|
||||||
// significant bit of the second-most-significant-byte is set
|
|
||||||
// it would conflict with the sign bit. An example of this case
|
|
||||||
// is +-255, which encode to 0xff00 and 0xff80 respectively.
|
|
||||||
// (big-endian).
|
|
||||||
if len(so) == 1 || so[len(so)-2]&0x80 == 0 {
|
|
||||||
return ErrStackMinimalData
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Depth returns the number of items on the stack.
|
// Depth returns the number of items on the stack.
|
||||||
func (s *stack) Depth() int {
|
func (s *stack) Depth() int32 {
|
||||||
return len(s.stk)
|
return int32(len(s.stk))
|
||||||
}
|
}
|
||||||
|
|
||||||
// PushByteArray adds the given back array to the top of the stack.
|
// PushByteArray adds the given back array to the top of the stack.
|
||||||
|
@ -143,12 +45,12 @@ func (s *stack) PushByteArray(so []byte) {
|
||||||
s.stk = append(s.stk, so)
|
s.stk = append(s.stk, so)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PushInt converts the provided bignum to a suitable byte array then pushes
|
// PushInt converts the provided scriptNum to a suitable byte array then pushes
|
||||||
// it onto the top of the stack.
|
// it onto the top of the stack.
|
||||||
//
|
//
|
||||||
// Stack transformation: [... x1 x2] -> [... x1 x2 int]
|
// Stack transformation: [... x1 x2] -> [... x1 x2 int]
|
||||||
func (s *stack) PushInt(val *big.Int) {
|
func (s *stack) PushInt(val scriptNum) {
|
||||||
s.PushByteArray(fromInt(val))
|
s.PushByteArray(val.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
// PushBool converts the provided boolean to a suitable byte array then pushes
|
// PushBool converts the provided boolean to a suitable byte array then pushes
|
||||||
|
@ -166,21 +68,18 @@ func (s *stack) PopByteArray() ([]byte, error) {
|
||||||
return s.nipN(0)
|
return s.nipN(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PopInt pops the value off the top of the stack, converts it into a bignum and
|
// PopInt pops the value off the top of the stack, converts it into a script
|
||||||
// returns it.
|
// num, and returns it. The act of converting to a script num enforces the
|
||||||
|
// consensus rules imposed on data interpreted as numbers.
|
||||||
//
|
//
|
||||||
// Stack transformation: [... x1 x2 x3] -> [... x1 x2]
|
// Stack transformation: [... x1 x2 x3] -> [... x1 x2]
|
||||||
func (s *stack) PopInt() (*big.Int, error) {
|
func (s *stack) PopInt() (scriptNum, error) {
|
||||||
so, err := s.PopByteArray()
|
so, err := s.PopByteArray()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.checkMinimalData(so); err != nil {
|
return makeScriptNum(so, s.verifyMinimalData)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return asInt(so)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PopBool pops the value off the top of the stack, converts it into a bool, and
|
// PopBool pops the value off the top of the stack, converts it into a bool, and
|
||||||
|
@ -196,9 +95,9 @@ func (s *stack) PopBool() (bool, error) {
|
||||||
return asBool(so), nil
|
return asBool(so), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PeekByteArray returns the nth item on the stack without removing it.
|
// PeekByteArray returns the Nth item on the stack without removing it.
|
||||||
func (s *stack) PeekByteArray(idx int) ([]byte, error) {
|
func (s *stack) PeekByteArray(idx int32) ([]byte, error) {
|
||||||
sz := len(s.stk)
|
sz := int32(len(s.stk))
|
||||||
if idx < 0 || idx >= sz {
|
if idx < 0 || idx >= sz {
|
||||||
return nil, ErrStackUnderflow
|
return nil, ErrStackUnderflow
|
||||||
}
|
}
|
||||||
|
@ -206,22 +105,20 @@ func (s *stack) PeekByteArray(idx int) ([]byte, error) {
|
||||||
return s.stk[sz-idx-1], nil
|
return s.stk[sz-idx-1], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PeekInt returns the Nth item on the stack as a bignum without removing it.
|
// PeekInt returns the Nth item on the stack as a script num without removing
|
||||||
func (s *stack) PeekInt(idx int) (*big.Int, error) {
|
// it. The act of converting to a script num enforces the consensus rules
|
||||||
|
// imposed on data interpreted as numbers.
|
||||||
|
func (s *stack) PeekInt(idx int32) (scriptNum, error) {
|
||||||
so, err := s.PeekByteArray(idx)
|
so, err := s.PeekByteArray(idx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := s.checkMinimalData(so); err != nil {
|
return makeScriptNum(so, s.verifyMinimalData)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return asInt(so)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PeekBool returns the Nth item on the stack as a bool without removing it.
|
// PeekBool returns the Nth item on the stack as a bool without removing it.
|
||||||
func (s *stack) PeekBool(idx int) (i bool, err error) {
|
func (s *stack) PeekBool(idx int32) (bool, error) {
|
||||||
so, err := s.PeekByteArray(idx)
|
so, err := s.PeekByteArray(idx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
@ -237,8 +134,8 @@ func (s *stack) PeekBool(idx int) (i bool, err error) {
|
||||||
// nipN(0): [... x1 x2 x3] -> [... x1 x2]
|
// nipN(0): [... x1 x2 x3] -> [... x1 x2]
|
||||||
// nipN(1): [... x1 x2 x3] -> [... x1 x3]
|
// nipN(1): [... x1 x2 x3] -> [... x1 x3]
|
||||||
// nipN(2): [... x1 x2 x3] -> [... x2 x3]
|
// nipN(2): [... x1 x2 x3] -> [... x2 x3]
|
||||||
func (s *stack) nipN(idx int) ([]byte, error) {
|
func (s *stack) nipN(idx int32) ([]byte, error) {
|
||||||
sz := len(s.stk)
|
sz := int32(len(s.stk))
|
||||||
if idx < 0 || idx > sz-1 {
|
if idx < 0 || idx > sz-1 {
|
||||||
return nil, ErrStackUnderflow
|
return nil, ErrStackUnderflow
|
||||||
}
|
}
|
||||||
|
@ -264,7 +161,7 @@ func (s *stack) nipN(idx int) ([]byte, error) {
|
||||||
// NipN(0): [... x1 x2 x3] -> [... x1 x2]
|
// NipN(0): [... x1 x2 x3] -> [... x1 x2]
|
||||||
// NipN(1): [... x1 x2 x3] -> [... x1 x3]
|
// NipN(1): [... x1 x2 x3] -> [... x1 x3]
|
||||||
// NipN(2): [... x1 x2 x3] -> [... x2 x3]
|
// NipN(2): [... x1 x2 x3] -> [... x2 x3]
|
||||||
func (s *stack) NipN(idx int) error {
|
func (s *stack) NipN(idx int32) error {
|
||||||
_, err := s.nipN(idx)
|
_, err := s.nipN(idx)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -294,7 +191,7 @@ func (s *stack) Tuck() error {
|
||||||
// Stack transformation:
|
// Stack transformation:
|
||||||
// DropN(1): [... x1 x2] -> [... x1]
|
// DropN(1): [... x1 x2] -> [... x1]
|
||||||
// DropN(2): [... x1 x2] -> [...]
|
// DropN(2): [... x1 x2] -> [...]
|
||||||
func (s *stack) DropN(n int) error {
|
func (s *stack) DropN(n int32) error {
|
||||||
if n < 1 {
|
if n < 1 {
|
||||||
return ErrStackInvalidArgs
|
return ErrStackInvalidArgs
|
||||||
}
|
}
|
||||||
|
@ -313,7 +210,7 @@ func (s *stack) DropN(n int) error {
|
||||||
// Stack transformation:
|
// Stack transformation:
|
||||||
// DupN(1): [... x1 x2] -> [... x1 x2 x2]
|
// DupN(1): [... x1 x2] -> [... x1 x2 x2]
|
||||||
// DupN(2): [... x1 x2] -> [... x1 x2 x1 x2]
|
// DupN(2): [... x1 x2] -> [... x1 x2 x1 x2]
|
||||||
func (s *stack) DupN(n int) error {
|
func (s *stack) DupN(n int32) error {
|
||||||
if n < 1 {
|
if n < 1 {
|
||||||
return ErrStackInvalidArgs
|
return ErrStackInvalidArgs
|
||||||
}
|
}
|
||||||
|
@ -335,7 +232,7 @@ func (s *stack) DupN(n int) error {
|
||||||
// Stack transformation:
|
// Stack transformation:
|
||||||
// RotN(1): [... x1 x2 x3] -> [... x2 x3 x1]
|
// RotN(1): [... x1 x2 x3] -> [... x2 x3 x1]
|
||||||
// RotN(2): [... x1 x2 x3 x4 x5 x6] -> [... x3 x4 x5 x6 x1 x2]
|
// RotN(2): [... x1 x2 x3 x4 x5 x6] -> [... x3 x4 x5 x6 x1 x2]
|
||||||
func (s *stack) RotN(n int) error {
|
func (s *stack) RotN(n int32) error {
|
||||||
if n < 1 {
|
if n < 1 {
|
||||||
return ErrStackInvalidArgs
|
return ErrStackInvalidArgs
|
||||||
}
|
}
|
||||||
|
@ -359,7 +256,7 @@ func (s *stack) RotN(n int) error {
|
||||||
// Stack transformation:
|
// Stack transformation:
|
||||||
// SwapN(1): [... x1 x2] -> [... x2 x1]
|
// SwapN(1): [... x1 x2] -> [... x2 x1]
|
||||||
// SwapN(2): [... x1 x2 x3 x4] -> [... x3 x4 x1 x2]
|
// SwapN(2): [... x1 x2 x3 x4] -> [... x3 x4 x1 x2]
|
||||||
func (s *stack) SwapN(n int) error {
|
func (s *stack) SwapN(n int32) error {
|
||||||
if n < 1 {
|
if n < 1 {
|
||||||
return ErrStackInvalidArgs
|
return ErrStackInvalidArgs
|
||||||
}
|
}
|
||||||
|
@ -382,7 +279,7 @@ func (s *stack) SwapN(n int) error {
|
||||||
// Stack transformation:
|
// Stack transformation:
|
||||||
// OverN(1): [... x1 x2 x3] -> [... x1 x2 x3 x2]
|
// OverN(1): [... x1 x2 x3] -> [... x1 x2 x3 x2]
|
||||||
// OverN(2): [... x1 x2 x3 x4] -> [... x1 x2 x3 x4 x1 x2]
|
// OverN(2): [... x1 x2 x3 x4] -> [... x1 x2 x3 x4 x1 x2]
|
||||||
func (s *stack) OverN(n int) error {
|
func (s *stack) OverN(n int32) error {
|
||||||
if n < 1 {
|
if n < 1 {
|
||||||
return ErrStackInvalidArgs
|
return ErrStackInvalidArgs
|
||||||
}
|
}
|
||||||
|
@ -406,7 +303,7 @@ func (s *stack) OverN(n int) error {
|
||||||
// PickN(0): [x1 x2 x3] -> [x1 x2 x3 x3]
|
// PickN(0): [x1 x2 x3] -> [x1 x2 x3 x3]
|
||||||
// PickN(1): [x1 x2 x3] -> [x1 x2 x3 x2]
|
// PickN(1): [x1 x2 x3] -> [x1 x2 x3 x2]
|
||||||
// PickN(2): [x1 x2 x3] -> [x1 x2 x3 x1]
|
// PickN(2): [x1 x2 x3] -> [x1 x2 x3 x1]
|
||||||
func (s *stack) PickN(n int) error {
|
func (s *stack) PickN(n int32) error {
|
||||||
so, err := s.PeekByteArray(n)
|
so, err := s.PeekByteArray(n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -422,7 +319,7 @@ func (s *stack) PickN(n int) error {
|
||||||
// RollN(0): [x1 x2 x3] -> [x1 x2 x3]
|
// RollN(0): [x1 x2 x3] -> [x1 x2 x3]
|
||||||
// RollN(1): [x1 x2 x3] -> [x1 x3 x2]
|
// RollN(1): [x1 x2 x3] -> [x1 x3 x2]
|
||||||
// RollN(2): [x1 x2 x3] -> [x2 x3 x1]
|
// RollN(2): [x1 x2 x3] -> [x2 x3 x1]
|
||||||
func (s *stack) RollN(n int) error {
|
func (s *stack) RollN(n int32) error {
|
||||||
so, err := s.nipN(n)
|
so, err := s.nipN(n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -180,7 +179,7 @@ func TestStack(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if v.Sign() != 0 {
|
if v != 0 {
|
||||||
return errors.New("0 != 0 on popInt")
|
return errors.New("0 != 0 on popInt")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -196,7 +195,7 @@ func TestStack(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if v.Sign() != 0 {
|
if v != 0 {
|
||||||
return errors.New("-0 != 0 on popInt")
|
return errors.New("-0 != 0 on popInt")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -212,7 +211,7 @@ func TestStack(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if v.Cmp(big.NewInt(1)) != 0 {
|
if v != 1 {
|
||||||
return errors.New("1 != 1 on popInt")
|
return errors.New("1 != 1 on popInt")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -228,8 +227,8 @@ func TestStack(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if v.Cmp(big.NewInt(1)) != 0 {
|
if v != 1 {
|
||||||
fmt.Printf("%v != %v\n", v, big.NewInt(1))
|
fmt.Printf("%v != %v\n", v, 1)
|
||||||
return errors.New("1 != 1 on popInt")
|
return errors.New("1 != 1 on popInt")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -245,8 +244,8 @@ func TestStack(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if v.Cmp(big.NewInt(-1)) != 0 {
|
if v != -1 {
|
||||||
return errors.New("1 != 1 on popInt")
|
return errors.New("-1 != -1 on popInt")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
|
@ -261,8 +260,8 @@ func TestStack(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if v.Cmp(big.NewInt(-1)) != 0 {
|
if v != -1 {
|
||||||
fmt.Printf("%v != %v\n", v, big.NewInt(-1))
|
fmt.Printf("%v != %v\n", v, -1)
|
||||||
return errors.New("-1 != -1 on popInt")
|
return errors.New("-1 != -1 on popInt")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -279,8 +278,8 @@ func TestStack(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if v.Cmp(big.NewInt(-513)) != 0 {
|
if v != -513 {
|
||||||
fmt.Printf("%v != %v\n", v, big.NewInt(-513))
|
fmt.Printf("%v != %v\n", v, -513)
|
||||||
return errors.New("1 != 1 on popInt")
|
return errors.New("1 != 1 on popInt")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -297,8 +296,8 @@ func TestStack(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if v.Cmp(big.NewInt(-1)) != 0 {
|
if v != -1 {
|
||||||
fmt.Printf("%v != %v\n", v, big.NewInt(-1))
|
fmt.Printf("%v != %v\n", v, -1)
|
||||||
return errors.New("-1 != -1 on popInt")
|
return errors.New("-1 != -1 on popInt")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -310,7 +309,7 @@ func TestStack(t *testing.T) {
|
||||||
"PushInt 0",
|
"PushInt 0",
|
||||||
[][]byte{},
|
[][]byte{},
|
||||||
func(s *stack) error {
|
func(s *stack) error {
|
||||||
s.PushInt(big.NewInt(0))
|
s.PushInt(scriptNum(0))
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
@ -320,7 +319,7 @@ func TestStack(t *testing.T) {
|
||||||
"PushInt 1",
|
"PushInt 1",
|
||||||
[][]byte{},
|
[][]byte{},
|
||||||
func(s *stack) error {
|
func(s *stack) error {
|
||||||
s.PushInt(big.NewInt(1))
|
s.PushInt(scriptNum(1))
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
@ -330,7 +329,7 @@ func TestStack(t *testing.T) {
|
||||||
"PushInt -1",
|
"PushInt -1",
|
||||||
[][]byte{},
|
[][]byte{},
|
||||||
func(s *stack) error {
|
func(s *stack) error {
|
||||||
s.PushInt(big.NewInt(-1))
|
s.PushInt(scriptNum(-1))
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
@ -340,7 +339,7 @@ func TestStack(t *testing.T) {
|
||||||
"PushInt two bytes",
|
"PushInt two bytes",
|
||||||
[][]byte{},
|
[][]byte{},
|
||||||
func(s *stack) error {
|
func(s *stack) error {
|
||||||
s.PushInt(big.NewInt(256))
|
s.PushInt(scriptNum(256))
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
@ -352,7 +351,7 @@ func TestStack(t *testing.T) {
|
||||||
[][]byte{},
|
[][]byte{},
|
||||||
func(s *stack) error {
|
func(s *stack) error {
|
||||||
// this will have the highbit set
|
// this will have the highbit set
|
||||||
s.PushInt(big.NewInt(128))
|
s.PushInt(scriptNum(128))
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
nil,
|
nil,
|
||||||
|
@ -518,7 +517,7 @@ func TestStack(t *testing.T) {
|
||||||
"PushInt PopBool",
|
"PushInt PopBool",
|
||||||
[][]byte{},
|
[][]byte{},
|
||||||
func(s *stack) error {
|
func(s *stack) error {
|
||||||
s.PushInt(big.NewInt(1))
|
s.PushInt(scriptNum(1))
|
||||||
val, err := s.PopBool()
|
val, err := s.PopBool()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -536,7 +535,7 @@ func TestStack(t *testing.T) {
|
||||||
"PushInt PopBool 2",
|
"PushInt PopBool 2",
|
||||||
[][]byte{},
|
[][]byte{},
|
||||||
func(s *stack) error {
|
func(s *stack) error {
|
||||||
s.PushInt(big.NewInt(0))
|
s.PushInt(scriptNum(0))
|
||||||
val, err := s.PopBool()
|
val, err := s.PopBool()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -554,7 +553,7 @@ func TestStack(t *testing.T) {
|
||||||
"PushInt PopBool 2",
|
"PushInt PopBool 2",
|
||||||
[][]byte{},
|
[][]byte{},
|
||||||
func(s *stack) error {
|
func(s *stack) error {
|
||||||
s.PushInt(big.NewInt(0))
|
s.PushInt(scriptNum(0))
|
||||||
val, err := s.PopBool()
|
val, err := s.PopBool()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -894,7 +893,7 @@ func TestStack(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if val.Cmp(big.NewInt(1)) != 0 {
|
if val != 1 {
|
||||||
return errors.New("invalid result")
|
return errors.New("invalid result")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -912,7 +911,7 @@ func TestStack(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if val.Cmp(big.NewInt(0)) != 0 {
|
if val != 0 {
|
||||||
return errors.New("invalid result")
|
return errors.New("invalid result")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -924,14 +923,14 @@ func TestStack(t *testing.T) {
|
||||||
"pop int",
|
"pop int",
|
||||||
[][]byte{},
|
[][]byte{},
|
||||||
func(s *stack) error {
|
func(s *stack) error {
|
||||||
s.PushInt(big.NewInt(1))
|
s.PushInt(scriptNum(1))
|
||||||
// Peek int is otherwise pretty well tested,
|
// Peek int is otherwise pretty well tested,
|
||||||
// just check it works.
|
// just check it works.
|
||||||
val, err := s.PopInt()
|
val, err := s.PopInt()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if val.Cmp(big.NewInt(1)) != 0 {
|
if val != 1 {
|
||||||
return errors.New("invalid result")
|
return errors.New("invalid result")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -968,14 +967,14 @@ func TestStack(t *testing.T) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(test.after) != s.Depth() {
|
if int32(len(test.after)) != s.Depth() {
|
||||||
t.Errorf("%s: stack depth doesn't match expected: %v "+
|
t.Errorf("%s: stack depth doesn't match expected: %v "+
|
||||||
"vs %v", test.name, len(test.after),
|
"vs %v", test.name, len(test.after),
|
||||||
s.Depth())
|
s.Depth())
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range test.after {
|
for i := range test.after {
|
||||||
val, err := s.PeekByteArray(s.Depth() - i - 1)
|
val, err := s.PeekByteArray(s.Depth() - int32(i) - 1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("%s: can't peek %dth stack entry: %v",
|
t.Errorf("%s: can't peek %dth stack entry: %v",
|
||||||
test.name, i, err)
|
test.name, i, err)
|
||||||
|
|
Loading…
Reference in a new issue