From ea790cc424b952e082fa9c92bff926323aa64908 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=ADghearn=C3=A1n=20Carroll?= Date: Thu, 4 Nov 2021 14:24:13 +0000 Subject: [PATCH] Fix: Max Number Length after Genesis (#78) --- bscript/interpreter/config.go | 10 ++ bscript/interpreter/operations.go | 6 +- bscript/interpreter/scriptnum.go | 4 - bscript/interpreter/scriptnum_test.go | 126 +++++++++++++------------- bscript/interpreter/stack.go | 12 ++- bscript/interpreter/stack_test.go | 2 +- bscript/interpreter/thread.go | 6 +- 7 files changed, 89 insertions(+), 77 deletions(-) diff --git a/bscript/interpreter/config.go b/bscript/interpreter/config.go index 881b6c77..f07ce57b 100644 --- a/bscript/interpreter/config.go +++ b/bscript/interpreter/config.go @@ -7,6 +7,7 @@ type config interface { MaxStackSize() int MaxScriptSize() int MaxScriptElementSize() int + MaxScriptNumberLength() int MaxPubKeysPerMultiSig() int } @@ -16,6 +17,7 @@ const ( MaxStackSizeBeforeGenesis = 1000 MaxScriptSizeBeforeGenesis = 10000 MaxScriptElementSizeBeforeGenesis = 520 + MaxScriptNumberLengthBeforeGenesis = 4 MaxPubKeysPerMultiSigBeforeGenesis = 20 ) @@ -46,6 +48,14 @@ func (b *beforeGenesisConfig) MaxScriptElementSize() int { return MaxScriptElementSizeBeforeGenesis } +func (a *afterGenesisConfig) MaxScriptNumberLength() int { + return 750 * 1000 // 750 * 1Kb +} + +func (b *beforeGenesisConfig) MaxScriptNumberLength() int { + return MaxScriptNumberLengthBeforeGenesis +} + func (a *afterGenesisConfig) MaxOps() int { return math.MaxInt32 } diff --git a/bscript/interpreter/operations.go b/bscript/interpreter/operations.go index fb19d90d..07088d81 100644 --- a/bscript/interpreter/operations.go +++ b/bscript/interpreter/operations.go @@ -1008,7 +1008,7 @@ func opcodeNum2bin(op *ParsedOp, t *thread) error { size := int(n.Int32()) if size > t.cfg.MaxScriptElementSize() { - return errs.NewError(errs.ErrNumberTooBig, "n is larger than the max of %d", defaultScriptNumLen) + return errs.NewError(errs.ErrNumberTooBig, "n is larger than the max of %d", t.cfg.MaxScriptElementSize()) } // encode a as a script num so that we we take the bytes it @@ -1058,8 +1058,8 @@ func opcodeBin2num(op *ParsedOp, t *thread) error { if err != nil { return err } - if len(n.Bytes()) > defaultScriptNumLen { - return errs.NewError(errs.ErrNumberTooBig, "script numbers are limited to %d bytes", defaultScriptNumLen) + if len(n.Bytes()) > t.cfg.MaxScriptNumberLength() { + return errs.NewError(errs.ErrNumberTooBig, "script numbers are limited to %d bytes", t.cfg.MaxScriptNumberLength()) } t.dstack.PushInt(n) diff --git a/bscript/interpreter/scriptnum.go b/bscript/interpreter/scriptnum.go index f9c19b25..8cbc59e8 100644 --- a/bscript/interpreter/scriptnum.go +++ b/bscript/interpreter/scriptnum.go @@ -11,10 +11,6 @@ import ( const ( maxInt32 = 1<<31 - 1 minInt32 = -1 << 31 - - // defaultScriptNumLen is the default number of bytes - // data being interpreted as an integer may be. - defaultScriptNumLen = 4 ) // scriptNum represents a numeric value used in the scripting engine with diff --git a/bscript/interpreter/scriptnum_test.go b/bscript/interpreter/scriptnum_test.go index ffff91ab..2e17d120 100644 --- a/bscript/interpreter/scriptnum_test.go +++ b/bscript/interpreter/scriptnum_test.go @@ -109,35 +109,35 @@ func TestMakeScriptNum(t *testing.T) { err error }{ // Minimal encoding must reject negative 0. - {hexToBytes("80"), 0, defaultScriptNumLen, true, errMinimalData}, + {hexToBytes("80"), 0, MaxScriptNumberLengthBeforeGenesis, true, errMinimalData}, // Minimally encoded valid values with minimal encoding flag. // Should not error and return expected integral number. - {nil, 0, defaultScriptNumLen, true, nil}, - {hexToBytes("01"), 1, defaultScriptNumLen, true, nil}, - {hexToBytes("81"), -1, defaultScriptNumLen, true, nil}, - {hexToBytes("7f"), 127, defaultScriptNumLen, true, nil}, - {hexToBytes("ff"), -127, defaultScriptNumLen, true, nil}, - {hexToBytes("8000"), 128, defaultScriptNumLen, true, nil}, - {hexToBytes("8080"), -128, defaultScriptNumLen, true, nil}, - {hexToBytes("8100"), 129, defaultScriptNumLen, true, nil}, - {hexToBytes("8180"), -129, defaultScriptNumLen, true, nil}, - {hexToBytes("0001"), 256, defaultScriptNumLen, true, nil}, - {hexToBytes("0081"), -256, defaultScriptNumLen, true, nil}, - {hexToBytes("ff7f"), 32767, defaultScriptNumLen, true, nil}, - {hexToBytes("ffff"), -32767, defaultScriptNumLen, true, nil}, - {hexToBytes("008000"), 32768, defaultScriptNumLen, true, nil}, - {hexToBytes("008080"), -32768, defaultScriptNumLen, true, nil}, - {hexToBytes("ffff00"), 65535, defaultScriptNumLen, true, nil}, - {hexToBytes("ffff80"), -65535, defaultScriptNumLen, true, nil}, - {hexToBytes("000008"), 524288, defaultScriptNumLen, true, nil}, - {hexToBytes("000088"), -524288, defaultScriptNumLen, true, nil}, - {hexToBytes("000070"), 7340032, defaultScriptNumLen, true, nil}, - {hexToBytes("0000f0"), -7340032, defaultScriptNumLen, true, nil}, - {hexToBytes("00008000"), 8388608, defaultScriptNumLen, true, nil}, - {hexToBytes("00008080"), -8388608, defaultScriptNumLen, true, nil}, - {hexToBytes("ffffff7f"), 2147483647, defaultScriptNumLen, true, nil}, - {hexToBytes("ffffffff"), -2147483647, defaultScriptNumLen, true, nil}, + {nil, 0, MaxScriptNumberLengthBeforeGenesis, true, nil}, + {hexToBytes("01"), 1, MaxScriptNumberLengthBeforeGenesis, true, nil}, + {hexToBytes("81"), -1, MaxScriptNumberLengthBeforeGenesis, true, nil}, + {hexToBytes("7f"), 127, MaxScriptNumberLengthBeforeGenesis, true, nil}, + {hexToBytes("ff"), -127, MaxScriptNumberLengthBeforeGenesis, true, nil}, + {hexToBytes("8000"), 128, MaxScriptNumberLengthBeforeGenesis, true, nil}, + {hexToBytes("8080"), -128, MaxScriptNumberLengthBeforeGenesis, true, nil}, + {hexToBytes("8100"), 129, MaxScriptNumberLengthBeforeGenesis, true, nil}, + {hexToBytes("8180"), -129, MaxScriptNumberLengthBeforeGenesis, true, nil}, + {hexToBytes("0001"), 256, MaxScriptNumberLengthBeforeGenesis, true, nil}, + {hexToBytes("0081"), -256, MaxScriptNumberLengthBeforeGenesis, true, nil}, + {hexToBytes("ff7f"), 32767, MaxScriptNumberLengthBeforeGenesis, true, nil}, + {hexToBytes("ffff"), -32767, MaxScriptNumberLengthBeforeGenesis, true, nil}, + {hexToBytes("008000"), 32768, MaxScriptNumberLengthBeforeGenesis, true, nil}, + {hexToBytes("008080"), -32768, MaxScriptNumberLengthBeforeGenesis, true, nil}, + {hexToBytes("ffff00"), 65535, MaxScriptNumberLengthBeforeGenesis, true, nil}, + {hexToBytes("ffff80"), -65535, MaxScriptNumberLengthBeforeGenesis, true, nil}, + {hexToBytes("000008"), 524288, MaxScriptNumberLengthBeforeGenesis, true, nil}, + {hexToBytes("000088"), -524288, MaxScriptNumberLengthBeforeGenesis, true, nil}, + {hexToBytes("000070"), 7340032, MaxScriptNumberLengthBeforeGenesis, true, nil}, + {hexToBytes("0000f0"), -7340032, MaxScriptNumberLengthBeforeGenesis, true, nil}, + {hexToBytes("00008000"), 8388608, MaxScriptNumberLengthBeforeGenesis, true, nil}, + {hexToBytes("00008080"), -8388608, MaxScriptNumberLengthBeforeGenesis, true, nil}, + {hexToBytes("ffffff7f"), 2147483647, MaxScriptNumberLengthBeforeGenesis, true, nil}, + {hexToBytes("ffffffff"), -2147483647, MaxScriptNumberLengthBeforeGenesis, true, nil}, {hexToBytes("ffffffff7f"), 549755813887, 5, true, nil}, {hexToBytes("ffffffffff"), -549755813887, 5, true, nil}, {hexToBytes("ffffffffffffff7f"), 9223372036854775807, 8, true, nil}, @@ -150,50 +150,50 @@ func TestMakeScriptNum(t *testing.T) { // 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, defaultScriptNumLen, true, errNumTooBig}, - {hexToBytes("0000008080"), 0, defaultScriptNumLen, true, errNumTooBig}, - {hexToBytes("0000009000"), 0, defaultScriptNumLen, true, errNumTooBig}, - {hexToBytes("0000009080"), 0, defaultScriptNumLen, true, errNumTooBig}, - {hexToBytes("ffffffff00"), 0, defaultScriptNumLen, true, errNumTooBig}, - {hexToBytes("ffffffff80"), 0, defaultScriptNumLen, true, errNumTooBig}, - {hexToBytes("0000000001"), 0, defaultScriptNumLen, true, errNumTooBig}, - {hexToBytes("0000000081"), 0, defaultScriptNumLen, true, errNumTooBig}, - {hexToBytes("ffffffffffff00"), 0, defaultScriptNumLen, true, errNumTooBig}, - {hexToBytes("ffffffffffff80"), 0, defaultScriptNumLen, true, errNumTooBig}, - {hexToBytes("ffffffffffffff00"), 0, defaultScriptNumLen, true, errNumTooBig}, - {hexToBytes("ffffffffffffff80"), 0, defaultScriptNumLen, true, errNumTooBig}, - {hexToBytes("ffffffffffffff7f"), 0, defaultScriptNumLen, true, errNumTooBig}, - {hexToBytes("ffffffffffffffff"), 0, defaultScriptNumLen, true, errNumTooBig}, + {hexToBytes("0000008000"), 0, MaxScriptNumberLengthBeforeGenesis, true, errNumTooBig}, + {hexToBytes("0000008080"), 0, MaxScriptNumberLengthBeforeGenesis, true, errNumTooBig}, + {hexToBytes("0000009000"), 0, MaxScriptNumberLengthBeforeGenesis, true, errNumTooBig}, + {hexToBytes("0000009080"), 0, MaxScriptNumberLengthBeforeGenesis, true, errNumTooBig}, + {hexToBytes("ffffffff00"), 0, MaxScriptNumberLengthBeforeGenesis, true, errNumTooBig}, + {hexToBytes("ffffffff80"), 0, MaxScriptNumberLengthBeforeGenesis, true, errNumTooBig}, + {hexToBytes("0000000001"), 0, MaxScriptNumberLengthBeforeGenesis, true, errNumTooBig}, + {hexToBytes("0000000081"), 0, MaxScriptNumberLengthBeforeGenesis, true, errNumTooBig}, + {hexToBytes("ffffffffffff00"), 0, MaxScriptNumberLengthBeforeGenesis, true, errNumTooBig}, + {hexToBytes("ffffffffffff80"), 0, MaxScriptNumberLengthBeforeGenesis, true, errNumTooBig}, + {hexToBytes("ffffffffffffff00"), 0, MaxScriptNumberLengthBeforeGenesis, true, errNumTooBig}, + {hexToBytes("ffffffffffffff80"), 0, MaxScriptNumberLengthBeforeGenesis, true, errNumTooBig}, + {hexToBytes("ffffffffffffff7f"), 0, MaxScriptNumberLengthBeforeGenesis, true, errNumTooBig}, + {hexToBytes("ffffffffffffffff"), 0, MaxScriptNumberLengthBeforeGenesis, true, errNumTooBig}, // Non-minimally encoded, but otherwise valid values with // minimal encoding flag. Should error and return 0. - {hexToBytes("00"), 0, defaultScriptNumLen, true, errMinimalData}, // 0 - {hexToBytes("0100"), 0, defaultScriptNumLen, true, errMinimalData}, // 1 - {hexToBytes("7f00"), 0, defaultScriptNumLen, true, errMinimalData}, // 127 - {hexToBytes("800000"), 0, defaultScriptNumLen, true, errMinimalData}, // 128 - {hexToBytes("810000"), 0, defaultScriptNumLen, true, errMinimalData}, // 129 - {hexToBytes("000100"), 0, defaultScriptNumLen, true, errMinimalData}, // 256 - {hexToBytes("ff7f00"), 0, defaultScriptNumLen, true, errMinimalData}, // 32767 - {hexToBytes("00800000"), 0, defaultScriptNumLen, true, errMinimalData}, // 32768 - {hexToBytes("ffff0000"), 0, defaultScriptNumLen, true, errMinimalData}, // 65535 - {hexToBytes("00000800"), 0, defaultScriptNumLen, true, errMinimalData}, // 524288 - {hexToBytes("00007000"), 0, defaultScriptNumLen, true, errMinimalData}, // 7340032 - {hexToBytes("0009000100"), 0, 5, true, errMinimalData}, // 16779520 + {hexToBytes("00"), 0, MaxScriptNumberLengthBeforeGenesis, true, errMinimalData}, // 0 + {hexToBytes("0100"), 0, MaxScriptNumberLengthBeforeGenesis, true, errMinimalData}, // 1 + {hexToBytes("7f00"), 0, MaxScriptNumberLengthBeforeGenesis, true, errMinimalData}, // 127 + {hexToBytes("800000"), 0, MaxScriptNumberLengthBeforeGenesis, true, errMinimalData}, // 128 + {hexToBytes("810000"), 0, MaxScriptNumberLengthBeforeGenesis, true, errMinimalData}, // 129 + {hexToBytes("000100"), 0, MaxScriptNumberLengthBeforeGenesis, true, errMinimalData}, // 256 + {hexToBytes("ff7f00"), 0, MaxScriptNumberLengthBeforeGenesis, true, errMinimalData}, // 32767 + {hexToBytes("00800000"), 0, MaxScriptNumberLengthBeforeGenesis, true, errMinimalData}, // 32768 + {hexToBytes("ffff0000"), 0, MaxScriptNumberLengthBeforeGenesis, true, errMinimalData}, // 65535 + {hexToBytes("00000800"), 0, MaxScriptNumberLengthBeforeGenesis, true, errMinimalData}, // 524288 + {hexToBytes("00007000"), 0, MaxScriptNumberLengthBeforeGenesis, true, errMinimalData}, // 7340032 + {hexToBytes("0009000100"), 0, 5, true, errMinimalData}, // 16779520 // Non-minimally encoded, but otherwise valid values without // minimal encoding flag. Should not error and return expected // integral number. - {hexToBytes("00"), 0, defaultScriptNumLen, false, nil}, - {hexToBytes("0100"), 1, defaultScriptNumLen, false, nil}, - {hexToBytes("7f00"), 127, defaultScriptNumLen, false, nil}, - {hexToBytes("800000"), 128, defaultScriptNumLen, false, nil}, - {hexToBytes("810000"), 129, defaultScriptNumLen, false, nil}, - {hexToBytes("000100"), 256, defaultScriptNumLen, false, nil}, - {hexToBytes("ff7f00"), 32767, defaultScriptNumLen, false, nil}, - {hexToBytes("00800000"), 32768, defaultScriptNumLen, false, nil}, - {hexToBytes("ffff0000"), 65535, defaultScriptNumLen, false, nil}, - {hexToBytes("00000800"), 524288, defaultScriptNumLen, false, nil}, - {hexToBytes("00007000"), 7340032, defaultScriptNumLen, false, nil}, + {hexToBytes("00"), 0, MaxScriptNumberLengthBeforeGenesis, false, nil}, + {hexToBytes("0100"), 1, MaxScriptNumberLengthBeforeGenesis, false, nil}, + {hexToBytes("7f00"), 127, MaxScriptNumberLengthBeforeGenesis, false, nil}, + {hexToBytes("800000"), 128, MaxScriptNumberLengthBeforeGenesis, false, nil}, + {hexToBytes("810000"), 129, MaxScriptNumberLengthBeforeGenesis, false, nil}, + {hexToBytes("000100"), 256, MaxScriptNumberLengthBeforeGenesis, false, nil}, + {hexToBytes("ff7f00"), 32767, MaxScriptNumberLengthBeforeGenesis, false, nil}, + {hexToBytes("00800000"), 32768, MaxScriptNumberLengthBeforeGenesis, false, nil}, + {hexToBytes("ffff0000"), 65535, MaxScriptNumberLengthBeforeGenesis, false, nil}, + {hexToBytes("00000800"), 524288, MaxScriptNumberLengthBeforeGenesis, false, nil}, + {hexToBytes("00007000"), 7340032, MaxScriptNumberLengthBeforeGenesis, false, nil}, {hexToBytes("0009000100"), 16779520, 5, false, nil}, } diff --git a/bscript/interpreter/stack.go b/bscript/interpreter/stack.go index 3586a43e..7bb3bb5e 100644 --- a/bscript/interpreter/stack.go +++ b/bscript/interpreter/stack.go @@ -38,9 +38,17 @@ func fromBool(v bool) []byte { // stack. type stack struct { stk [][]byte + maxNumLength int verifyMinimalData bool } +func newStack(cfg config, verifyMinimalData bool) stack { + return stack{ + maxNumLength: cfg.MaxScriptNumberLength(), + verifyMinimalData: verifyMinimalData, + } +} + // Depth returns the number of items on the stack. func (s *stack) Depth() int32 { return int32(len(s.stk)) @@ -87,7 +95,7 @@ func (s *stack) PopInt() (scriptNum, error) { return 0, err } - return makeScriptNum(so, s.verifyMinimalData, defaultScriptNumLen) + return makeScriptNum(so, s.verifyMinimalData, s.maxNumLength) } // PopBool pops the value off the top of the stack, converts it into a bool, and @@ -122,7 +130,7 @@ func (s *stack) PeekInt(idx int32) (scriptNum, error) { return 0, err } - return makeScriptNum(so, s.verifyMinimalData, defaultScriptNumLen) + return makeScriptNum(so, s.verifyMinimalData, s.maxNumLength) } // PeekBool returns the Nth item on the stack as a bool without removing it. diff --git a/bscript/interpreter/stack_test.go b/bscript/interpreter/stack_test.go index c1e7ba59..58253408 100644 --- a/bscript/interpreter/stack_test.go +++ b/bscript/interpreter/stack_test.go @@ -905,7 +905,7 @@ func TestStack(t *testing.T) { for _, test := range tests { // Setup the initial stack state and perform the test operation. - s := stack{} + s := newStack(&beforeGenesisConfig{}, false) for i := range test.before { s.PushByteArray(test.before[i]) } diff --git a/bscript/interpreter/thread.go b/bscript/interpreter/thread.go index 20c78189..a3876646 100644 --- a/bscript/interpreter/thread.go +++ b/bscript/interpreter/thread.go @@ -429,10 +429,8 @@ func (t *thread) apply(opts *execOpts) error { t.bip16 = true } - if t.hasFlag(scriptflag.VerifyMinimalData) { - t.dstack.verifyMinimalData = true - t.astack.verifyMinimalData = true - } + t.dstack = newStack(t.cfg, t.hasFlag(scriptflag.VerifyMinimalData)) + t.astack = newStack(t.cfg, t.hasFlag(scriptflag.VerifyMinimalData)) if t.tx != nil { t.tx.InputIdx(t.inputIdx).PreviousTxScript = t.prevOutput.LockingScript