From 45fecd3becd4ddaf8e6863c315aa072a2a7e8f77 Mon Sep 17 00:00:00 2001 From: ZhangTao1596 Date: Fri, 3 Mar 2023 10:37:28 +0800 Subject: [PATCH] vm: use uint256.Int (fixes #1581) --- cli/nep_test/nep11_test.go | 6 +- cli/vm/cli_test.go | 8 +- go.work | 6 + pkg/compiler/for_test.go | 11 +- pkg/compiler/interop_test.go | 2 +- pkg/compiler/native_test.go | 4 +- pkg/compiler/slice_test.go | 11 +- pkg/compiler/struct_test.go | 5 +- pkg/compiler/syscall_test.go | 3 +- pkg/core/block/block.go | 12 +- pkg/core/blockchain.go | 2 +- pkg/core/interop/contract/call.go | 6 +- pkg/core/interop/contract/call_test.go | 3 +- pkg/core/interop/iterator/interop_test.go | 2 +- pkg/core/interop/runtime/engine.go | 17 +- pkg/core/interop/runtime/ext_test.go | 11 +- pkg/core/interop/runtime/util.go | 14 +- pkg/core/interop/storage/find.go | 3 +- pkg/core/interop/storage/storage_test.go | 4 +- pkg/core/native/crypto.go | 8 +- pkg/core/native/designate.go | 6 +- pkg/core/native/interop.go | 2 +- pkg/core/native/ledger.go | 4 +- pkg/core/native/management.go | 7 +- pkg/core/native/native_neo.go | 25 +- pkg/core/native/native_neo_candidate.go | 5 +- pkg/core/native/native_nep17.go | 30 +- pkg/core/native/native_test/ledger_test.go | 24 +- pkg/core/native/native_test/neo_test.go | 19 +- pkg/core/native/native_test/oracle_test.go | 4 +- pkg/core/native/neo_types.go | 5 +- pkg/core/native/notary.go | 9 +- pkg/core/native/oracle.go | 5 +- pkg/core/native/oracle_types.go | 4 +- pkg/core/native/policy.go | 8 +- pkg/core/native/std.go | 12 +- pkg/core/native/std_test.go | 5 +- pkg/core/state/deposit.go | 5 +- pkg/core/state/native_state.go | 14 +- pkg/core/state/oracle.go | 4 +- pkg/core/state/oracle_test.go | 4 +- pkg/core/transaction/transaction.go | 12 +- pkg/core/transaction/witness_condition.go | 4 +- pkg/core/transaction/witness_rule.go | 4 +- pkg/encoding/bigint/bigint.go | 71 +++++ pkg/neorpc/result/invoke_test.go | 6 +- pkg/rpcclient/neo/neo.go | 4 +- pkg/rpcclient/rpc_test.go | 3 +- pkg/rpcclient/unwrap/unwrap.go | 12 +- pkg/services/rpcsrv/client_test.go | 7 +- pkg/services/rpcsrv/server.go | 10 +- pkg/smartcontract/manifest/event_test.go | 3 +- pkg/smartcontract/manifest/manifest_test.go | 10 +- pkg/smartcontract/manifest/method.go | 5 +- pkg/smartcontract/manifest/method_test.go | 12 +- pkg/smartcontract/manifest/parameter.go | 3 +- pkg/smartcontract/manifest/parameter_test.go | 5 +- pkg/smartcontract/parameter_test.go | 5 +- pkg/util/bigint.go | 38 +++ pkg/util/bigint_test.go | 64 ++++ pkg/vm/emit/emit_test.go | 8 +- pkg/vm/exception.go | 4 +- pkg/vm/json_test.go | 5 +- pkg/vm/slot_test.go | 6 +- pkg/vm/stack.go | 10 +- pkg/vm/stack_test.go | 59 ++-- pkg/vm/stackitem/item.go | 87 ++++-- pkg/vm/stackitem/item_test.go | 89 +++--- pkg/vm/stackitem/json.go | 8 +- pkg/vm/stackitem/json_test.go | 14 +- pkg/vm/stackitem/serialization.go | 5 +- pkg/vm/vm.go | 145 ++++++--- pkg/vm/vm_test.go | 311 ++++++++++++------- 73 files changed, 882 insertions(+), 486 deletions(-) create mode 100644 go.work create mode 100644 pkg/util/bigint.go create mode 100644 pkg/util/bigint_test.go diff --git a/cli/nep_test/nep11_test.go b/cli/nep_test/nep11_test.go index a8ea2741f7..6c3b78fbc8 100644 --- a/cli/nep_test/nep11_test.go +++ b/cli/nep_test/nep11_test.go @@ -7,12 +7,12 @@ import ( "encoding/json" "fmt" "io" - "math/big" "os" "path/filepath" "strconv" "testing" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/internal/testcli" "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" "github.com/nspcc-dev/neo-go/pkg/core/state" @@ -351,7 +351,7 @@ func TestNEP11_ND_OwnerOf_BalanceOf_Transfer(t *testing.T) { Name: "OnNEP11Payment", Item: stackitem.NewArray([]stackitem.Item{ stackitem.NewByteArray(nftOwnerHash.BytesBE()), - stackitem.NewBigInteger(big.NewInt(1)), + stackitem.NewBigInteger(uint256.NewInt(1)), stackitem.NewByteArray(tokenID1), stackitem.NewByteArray([]byte("some_data")), }), @@ -636,7 +636,7 @@ func TestNEP11_D_OwnerOf_BalanceOf_Transfer(t *testing.T) { Name: "OnNEP11Payment", Item: stackitem.NewArray([]stackitem.Item{ stackitem.NewByteArray(validatorHash.BytesBE()), - stackitem.NewBigInteger(big.NewInt(25)), + stackitem.NewBigInteger(uint256.NewInt(25)), stackitem.NewByteArray(token2ID), stackitem.NewByteArray([]byte("some_data")), }), diff --git a/cli/vm/cli_test.go b/cli/vm/cli_test.go index 2b6e7db451..8541a72cb2 100644 --- a/cli/vm/cli_test.go +++ b/cli/vm/cli_test.go @@ -8,7 +8,6 @@ import ( "errors" "fmt" gio "io" - "math/big" "os" "path/filepath" "strconv" @@ -18,6 +17,7 @@ import ( "time" "github.com/chzyer/readline" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/cli/paramcontext" "github.com/nspcc-dev/neo-go/internal/basicchain" "github.com/nspcc-dev/neo-go/internal/random" @@ -590,9 +590,9 @@ func TestRunWithDifferentArguments(t *testing.T) { e.checkNextLine(t, "READY: loaded \\d.* instructions") e.checkStack(t, []stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(1)), - stackitem.NewBigInteger(big.NewInt(2)), - stackitem.NewBigInteger(big.NewInt(3)), + stackitem.NewBigInteger(uint256.NewInt(1)), + stackitem.NewBigInteger(uint256.NewInt(2)), + stackitem.NewBigInteger(uint256.NewInt(3)), }) } diff --git a/go.work b/go.work new file mode 100644 index 0000000000..22ac056232 --- /dev/null +++ b/go.work @@ -0,0 +1,6 @@ +go 1.20 + +use ( + . + ./pkg/interop +) diff --git a/pkg/compiler/for_test.go b/pkg/compiler/for_test.go index c7bd36cc15..44e4d263d1 100644 --- a/pkg/compiler/for_test.go +++ b/pkg/compiler/for_test.go @@ -7,6 +7,7 @@ import ( "strings" "testing" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/pkg/compiler" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/vm" @@ -36,7 +37,7 @@ func TestEntryPointWithArgs(t *testing.T) { return 2 + args[1].(int) } ` - args := []stackitem.Item{stackitem.NewBigInteger(big.NewInt(0)), stackitem.NewBigInteger(big.NewInt(1))} + args := []stackitem.Item{stackitem.NewBigInteger(uint256.NewInt(0)), stackitem.NewBigInteger(uint256.NewInt(1))} evalWithArgs(t, src, nil, args, big.NewInt(3)) } @@ -51,7 +52,7 @@ func TestEntryPointWithMethodAndArgs(t *testing.T) { return 0 } ` - args := []stackitem.Item{stackitem.NewBigInteger(big.NewInt(0)), stackitem.NewBigInteger(big.NewInt(1))} + args := []stackitem.Item{stackitem.NewBigInteger(uint256.NewInt(0)), stackitem.NewBigInteger(uint256.NewInt(1))} evalWithArgs(t, src, []byte("foobar"), args, big.NewInt(3)) } @@ -156,9 +157,9 @@ func TestIntArray(t *testing.T) { } ` eval(t, src, []stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(1)), - stackitem.NewBigInteger(big.NewInt(2)), - stackitem.NewBigInteger(big.NewInt(3)), + stackitem.NewBigInteger(uint256.NewInt(1)), + stackitem.NewBigInteger(uint256.NewInt(2)), + stackitem.NewBigInteger(uint256.NewInt(3)), }) } diff --git a/pkg/compiler/interop_test.go b/pkg/compiler/interop_test.go index 2ba7a5f2f6..6a5b02d5c1 100644 --- a/pkg/compiler/interop_test.go +++ b/pkg/compiler/interop_test.go @@ -586,7 +586,7 @@ func TestForcedNotifyArgumentsConversion(t *testing.T) { Name: strconv.Itoa(i), Type: targetSCParamTypes[i], } - defaultValue := stackitem.NewBigInteger(big.NewInt(int64(i))) + defaultValue := stackitem.NewBigIntegerFromInt64(int64(i)) var ( val stackitem.Item err error diff --git a/pkg/compiler/native_test.go b/pkg/compiler/native_test.go index 06db9f919d..acfbf3f1c0 100644 --- a/pkg/compiler/native_test.go +++ b/pkg/compiler/native_test.go @@ -4,11 +4,11 @@ import ( "bytes" "errors" "fmt" - "math/big" "strconv" "strings" "testing" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/pkg/compiler" "github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/core/interop" @@ -362,7 +362,7 @@ func getTestStackItem(typ smartcontract.ParamType) stackitem.Item { case smartcontract.BoolType: return stackitem.NewBool(true) case smartcontract.IntegerType: - return stackitem.NewBigInteger(big.NewInt(42)) + return stackitem.NewBigInteger(uint256.NewInt(42)) case smartcontract.ByteArrayType, smartcontract.StringType, smartcontract.Hash160Type, smartcontract.Hash256Type, smartcontract.PublicKeyType, smartcontract.SignatureType: return stackitem.NewByteArray([]byte("result")) diff --git a/pkg/compiler/slice_test.go b/pkg/compiler/slice_test.go index 8152959cd3..3edd20fa41 100644 --- a/pkg/compiler/slice_test.go +++ b/pkg/compiler/slice_test.go @@ -7,6 +7,7 @@ import ( "strings" "testing" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/pkg/compiler" "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" @@ -162,11 +163,11 @@ var sliceTestCases = []testCase{ } `, []stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(1)), - stackitem.NewBigInteger(big.NewInt(2)), - stackitem.NewBigInteger(big.NewInt(3)), - stackitem.NewBigInteger(big.NewInt(4)), - stackitem.NewBigInteger(big.NewInt(5)), + stackitem.NewBigInteger(uint256.NewInt(1)), + stackitem.NewBigInteger(uint256.NewInt(2)), + stackitem.NewBigInteger(uint256.NewInt(3)), + stackitem.NewBigInteger(uint256.NewInt(4)), + stackitem.NewBigInteger(uint256.NewInt(5)), }, }, { diff --git a/pkg/compiler/struct_test.go b/pkg/compiler/struct_test.go index d799f18053..72a474ce55 100644 --- a/pkg/compiler/struct_test.go +++ b/pkg/compiler/struct_test.go @@ -7,6 +7,7 @@ import ( "strings" "testing" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/pkg/compiler" "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" @@ -278,8 +279,8 @@ var structTestCases = []testCase{ } `, []stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(1)), - stackitem.NewBigInteger(big.NewInt(2)), + stackitem.NewBigInteger(uint256.NewInt(1)), + stackitem.NewBigInteger(uint256.NewInt(2)), stackitem.NewByteArray([]byte("hello")), stackitem.NewBool(false), }, diff --git a/pkg/compiler/syscall_test.go b/pkg/compiler/syscall_test.go index ed0e655d67..65025f7bf1 100644 --- a/pkg/compiler/syscall_test.go +++ b/pkg/compiler/syscall_test.go @@ -8,6 +8,7 @@ import ( "strings" "testing" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/pkg/compiler" "github.com/nspcc-dev/neo-go/pkg/core" "github.com/nspcc-dev/neo-go/pkg/core/interop" @@ -201,7 +202,7 @@ func TestNotify(t *testing.T) { require.NoError(t, v.Run()) require.Equal(t, 2, len(s.events)) - exp0 := []stackitem.Item{stackitem.NewBigInteger(big.NewInt(11)), stackitem.NewByteArray([]byte("sum")), stackitem.NewBigInteger(big.NewInt(12))} + exp0 := []stackitem.Item{stackitem.NewBigInteger(uint256.NewInt(11)), stackitem.NewByteArray([]byte("sum")), stackitem.NewBigInteger(uint256.NewInt(12))} assert.Equal(t, "Event1", s.events[0].Name) assert.Equal(t, exp0, s.events[0].Item.Value()) assert.Equal(t, "single", s.events[1].Name) diff --git a/pkg/core/block/block.go b/pkg/core/block/block.go index 32215098b7..de121c1696 100644 --- a/pkg/core/block/block.go +++ b/pkg/core/block/block.go @@ -4,8 +4,8 @@ import ( "encoding/json" "errors" "math" - "math/big" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/io" @@ -223,14 +223,14 @@ func (b *Block) GetExpectedBlockSizeWithoutTransactions(txCount int) int { func (b *Block) ToStackItem() stackitem.Item { items := []stackitem.Item{ stackitem.NewByteArray(b.Hash().BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(b.Version))), + stackitem.NewBigInteger(uint256.NewInt(uint64(b.Version))), stackitem.NewByteArray(b.PrevHash.BytesBE()), stackitem.NewByteArray(b.MerkleRoot.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(b.Timestamp))), - stackitem.NewBigInteger(new(big.Int).SetUint64(b.Nonce)), - stackitem.NewBigInteger(big.NewInt(int64(b.Index))), + stackitem.NewBigInteger(uint256.NewInt(uint64(b.Timestamp))), + stackitem.NewBigInteger(uint256.NewInt(b.Nonce)), + stackitem.NewBigInteger(uint256.NewInt(uint64(b.Index))), stackitem.NewByteArray(b.NextConsensus.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(len(b.Transactions)))), + stackitem.NewBigInteger(uint256.NewInt(uint64(len(b.Transactions)))), } if b.StateRootEnabled { items = append(items, stackitem.NewByteArray(b.PrevStateRoot.BytesBE())) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index b56f68b134..a0ab160785 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -1736,7 +1736,7 @@ func (bc *Blockchain) handleNotification(note *state.NotificationEvent, d *dao.S return } } - bc.processTokenTransfer(d, transCache, h, b, note.ScriptHash, from, to, amount, id) + bc.processTokenTransfer(d, transCache, h, b, note.ScriptHash, from, to, util.ToBig(amount), id) } func parseUint160(itm stackitem.Item) (util.Uint160, error) { diff --git a/pkg/core/interop/contract/call.go b/pkg/core/interop/contract/call.go index 2f84657d52..3da56fbd86 100644 --- a/pkg/core/interop/contract/call.go +++ b/pkg/core/interop/contract/call.go @@ -3,9 +3,9 @@ package contract import ( "errors" "fmt" - "math/big" "strings" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/pkg/core/dao" "github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" @@ -47,7 +47,7 @@ func LoadToken(ic *interop.Context, id int32) error { func Call(ic *interop.Context) error { h := ic.VM.Estack().Pop().Bytes() method := ic.VM.Estack().Pop().String() - fs := callflag.CallFlag(int32(ic.VM.Estack().Pop().BigInt().Int64())) + fs := callflag.CallFlag(int32(ic.VM.Estack().Pop().BigInt().Uint64())) if fs&^callflag.All != 0 { return errors.New("call flags out of range") } @@ -176,6 +176,6 @@ func CallFromNative(ic *interop.Context, caller util.Uint160, cs *state.Contract // GetCallFlags returns current context calling flags. func GetCallFlags(ic *interop.Context) error { - ic.VM.Estack().PushItem(stackitem.NewBigInteger(big.NewInt(int64(ic.VM.Context().GetCallFlags())))) + ic.VM.Estack().PushItem(stackitem.NewBigInteger(uint256.NewInt(uint64(ic.VM.Context().GetCallFlags())))) return nil } diff --git a/pkg/core/interop/contract/call_test.go b/pkg/core/interop/contract/call_test.go index 50648a4b68..3f92f0f9b2 100644 --- a/pkg/core/interop/contract/call_test.go +++ b/pkg/core/interop/contract/call_test.go @@ -8,6 +8,7 @@ import ( "strings" "testing" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/internal/contracts" "github.com/nspcc-dev/neo-go/internal/random" "github.com/nspcc-dev/neo-go/pkg/compiler" @@ -189,7 +190,7 @@ func TestLoadToken(t *testing.T) { t.Run("good", func(t *testing.T) { realBalance, _ := bc.GetGoverningTokenBalance(acc.ScriptHash()) - cInvoker.Invoke(t, stackitem.NewBigInteger(big.NewInt(realBalance.Int64()+1)), "callT0", acc.ScriptHash()) + cInvoker.Invoke(t, stackitem.NewBigInteger(uint256.NewInt(uint64(realBalance.Int64()+1))), "callT0", acc.ScriptHash()) }) t.Run("invalid param count", func(t *testing.T) { cInvoker.InvokeFail(t, "method not found: callT2/1", "callT2", acc.ScriptHash()) diff --git a/pkg/core/interop/iterator/interop_test.go b/pkg/core/interop/iterator/interop_test.go index f8d025be2f..bca2141fef 100644 --- a/pkg/core/interop/iterator/interop_test.go +++ b/pkg/core/interop/iterator/interop_test.go @@ -23,7 +23,7 @@ func (t *testIter) Next() bool { } func (t testIter) Value() stackitem.Item { - return stackitem.NewBigInteger(big.NewInt(int64(t.arr[t.index]))) + return stackitem.NewBigIntegerFromInt64(int64(t.arr[t.index])) } // Iterator is thoroughly tested in VM package, these are smoke tests. diff --git a/pkg/core/interop/runtime/engine.go b/pkg/core/interop/runtime/engine.go index be9e78764a..767e00ba3a 100644 --- a/pkg/core/interop/runtime/engine.go +++ b/pkg/core/interop/runtime/engine.go @@ -3,8 +3,8 @@ package runtime import ( "errors" "fmt" - "math/big" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/vm" @@ -65,7 +65,7 @@ func Platform(ic *interop.Context) error { // GetTrigger returns the script trigger. func GetTrigger(ic *interop.Context) error { - ic.VM.Estack().PushItem(stackitem.NewBigInteger(big.NewInt(int64(ic.Trigger)))) + ic.VM.Estack().PushItem(stackitem.NewBigInteger(uint256.NewInt(uint64(ic.Trigger)))) return nil } @@ -110,7 +110,7 @@ func Notify(ic *interop.Context) error { // LoadScript takes a script and arguments from the stack and loads it into the VM. func LoadScript(ic *interop.Context) error { script := ic.VM.Estack().Pop().Bytes() - fs := callflag.CallFlag(int32(ic.VM.Estack().Pop().BigInt().Int64())) + fs := callflag.CallFlag(int32(ic.VM.Estack().Pop().BigInt().Uint64())) if fs&^callflag.All != 0 { return errors.New("call flags out of range") } @@ -148,23 +148,20 @@ func Log(ic *interop.Context) error { // GetTime returns timestamp of the block being verified, or the latest // one in the blockchain if no block is given to Context. func GetTime(ic *interop.Context) error { - ic.VM.Estack().PushItem(stackitem.NewBigInteger(new(big.Int).SetUint64(ic.Block.Timestamp))) + ic.VM.Estack().PushItem(stackitem.NewBigInteger(uint256.NewInt((ic.Block.Timestamp)))) return nil } // BurnGas burns GAS to benefit Neo ecosystem. func BurnGas(ic *interop.Context) error { gas := ic.VM.Estack().Pop().BigInt() - if !gas.IsInt64() { + if !gas.IsUint64() { return errors.New("invalid GAS value") } - g := gas.Int64() - if g <= 0 { - return errors.New("GAS must be positive") - } + g := gas.Uint64() - if !ic.VM.AddGas(g) { + if !ic.VM.AddGas(int64(g)) { return errors.New("GAS limit exceeded") } return nil diff --git a/pkg/core/interop/runtime/ext_test.go b/pkg/core/interop/runtime/ext_test.go index d423ee7012..22cc6893ce 100644 --- a/pkg/core/interop/runtime/ext_test.go +++ b/pkg/core/interop/runtime/ext_test.go @@ -7,6 +7,7 @@ import ( "path/filepath" "testing" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/internal/contracts" "github.com/nspcc-dev/neo-go/internal/random" "github.com/nspcc-dev/neo-go/pkg/config" @@ -491,7 +492,7 @@ func TestGetAddressVersion(t *testing.T) { emit.Syscall(w.BinWriter, interopnames.SystemRuntimeGetAddressVersion) require.NoError(t, w.Err) - e.InvokeScriptCheckHALT(t, w.Bytes(), []neotest.Signer{acc}, stackitem.NewBigInteger(big.NewInt(int64(address.NEO3Prefix)))) + e.InvokeScriptCheckHALT(t, w.Bytes(), []neotest.Signer{acc}, stackitem.NewBigInteger(uint256.NewInt(uint64(address.NEO3Prefix)))) } func TestGetInvocationCounter(t *testing.T) { @@ -506,19 +507,19 @@ func TestGetInvocationCounter(t *testing.T) { v.Load([]byte{1}) // do not return an error in this case. require.NoError(t, runtime.GetInvocationCounter(ic)) - require.EqualValues(t, 1, v.Estack().Pop().BigInt().Int64()) + require.EqualValues(t, 1, v.Estack().Pop().BigInt().Uint64()) }) t.Run("NonZero", func(t *testing.T) { v.Load([]byte{2}) require.NoError(t, runtime.GetInvocationCounter(ic)) - require.EqualValues(t, 42, v.Estack().Pop().BigInt().Int64()) + require.EqualValues(t, 42, v.Estack().Pop().BigInt().Uint64()) }) t.Run("Contract", func(t *testing.T) { script, err := smartcontract.CreateCallScript(cs.Hash, "invocCounter") require.NoError(t, err) v.LoadWithFlags(script, callflag.All) require.NoError(t, v.Run()) - require.EqualValues(t, 1, v.Estack().Pop().BigInt().Int64()) + require.EqualValues(t, 1, v.Estack().Pop().BigInt().Uint64()) }) } @@ -529,7 +530,7 @@ func TestGetNetwork(t *testing.T) { emit.Syscall(w.BinWriter, interopnames.SystemRuntimeGetNetwork) require.NoError(t, w.Err) - e.InvokeScriptCheckHALT(t, w.Bytes(), []neotest.Signer{acc}, stackitem.NewBigInteger(big.NewInt(int64(bc.GetConfig().Magic)))) + e.InvokeScriptCheckHALT(t, w.Bytes(), []neotest.Signer{acc}, stackitem.NewBigInteger(uint256.NewInt(uint64(bc.GetConfig().Magic)))) } func TestGetNotifications(t *testing.T) { diff --git a/pkg/core/interop/runtime/util.go b/pkg/core/interop/runtime/util.go index 7689b5e1a4..a30bcf81f3 100644 --- a/pkg/core/interop/runtime/util.go +++ b/pkg/core/interop/runtime/util.go @@ -3,8 +3,8 @@ package runtime import ( "encoding/binary" "errors" - "math/big" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/state" @@ -19,9 +19,9 @@ import ( // GasLeft returns the remaining amount of GAS. func GasLeft(ic *interop.Context) error { if ic.VM.GasLimit == -1 { - ic.VM.Estack().PushItem(stackitem.NewBigInteger(big.NewInt(ic.VM.GasLimit))) + ic.VM.Estack().PushItem(stackitem.NewBigIntegerFromInt64(ic.VM.GasLimit)) } else { - ic.VM.Estack().PushItem(stackitem.NewBigInteger(big.NewInt(ic.VM.GasLimit - ic.VM.GasConsumed()))) + ic.VM.Estack().PushItem(stackitem.NewBigIntegerFromInt64(ic.VM.GasLimit - ic.VM.GasConsumed())) } return nil } @@ -70,20 +70,20 @@ func GetInvocationCounter(ic *interop.Context) error { count = 1 ic.Invocations[currentScriptHash] = count } - ic.VM.Estack().PushItem(stackitem.NewBigInteger(big.NewInt(int64(count)))) + ic.VM.Estack().PushItem(stackitem.NewBigInteger(uint256.NewInt(uint64(count)))) return nil } // GetAddressVersion returns the address version of the current protocol. func GetAddressVersion(ic *interop.Context) error { - ic.VM.Estack().PushItem(stackitem.NewBigInteger(big.NewInt(int64(address.NEO3Prefix)))) + ic.VM.Estack().PushItem(stackitem.NewBigInteger(uint256.NewInt(uint64(address.NEO3Prefix)))) return nil } // GetNetwork returns chain network number. func GetNetwork(ic *interop.Context) error { m := ic.Chain.GetConfig().Magic - ic.VM.Estack().PushItem(stackitem.NewBigInteger(big.NewInt(int64(m)))) + ic.VM.Estack().PushItem(stackitem.NewBigInteger(uint256.NewInt(uint64(m)))) return nil } @@ -108,7 +108,7 @@ func GetRandom(ic *interop.Context) error { if !ic.VM.AddGas(ic.BaseExecFee() * price) { return errors.New("gas limit exceeded") } - ic.VM.Estack().PushItem(stackitem.NewBigInteger(bigint.FromBytesUnsigned(res))) + ic.VM.Estack().PushItem(stackitem.NewBigInteger(bigint.Uint256FromBytesUnsigned(res))) return nil } diff --git a/pkg/core/interop/storage/find.go b/pkg/core/interop/storage/find.go index 62a59b688a..92afc6e7b6 100644 --- a/pkg/core/interop/storage/find.go +++ b/pkg/core/interop/storage/find.go @@ -6,6 +6,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/storage" + "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util/slice" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) @@ -93,7 +94,7 @@ func Find(ic *interop.Context) error { return fmt.Errorf("%T is not a storage,Context", stcInterface) } prefix := ic.VM.Estack().Pop().Bytes() - opts := ic.VM.Estack().Pop().BigInt().Int64() + opts := util.ToInt64(ic.VM.Estack().Pop().BigInt()) if opts&^FindAll != 0 { return fmt.Errorf("%w: unknown flag", errFindInvalidOptions) } diff --git a/pkg/core/interop/storage/storage_test.go b/pkg/core/interop/storage/storage_test.go index fec1820b29..0cf45cd8f0 100644 --- a/pkg/core/interop/storage/storage_test.go +++ b/pkg/core/interop/storage/storage_test.go @@ -2,9 +2,9 @@ package storage_test import ( "errors" - "math/big" "testing" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/pkg/config/limits" "github.com/nspcc-dev/neo-go/pkg/core" "github.com/nspcc-dev/neo-go/pkg/core/block" @@ -119,7 +119,7 @@ func TestFind(t *testing.T) { v, contractState, context, _ := createVMAndContractState(t) arr := []stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(42)), + stackitem.NewBigInteger(uint256.NewInt(42)), stackitem.NewByteArray([]byte("second")), stackitem.Null{}, } diff --git a/pkg/core/native/crypto.go b/pkg/core/native/crypto.go index 0228bc9ccc..ca28e1476e 100644 --- a/pkg/core/native/crypto.go +++ b/pkg/core/native/crypto.go @@ -123,14 +123,14 @@ func curveFromStackitem(si stackitem.Item) (elliptic.Curve, error) { if err != nil { return nil, err } - if !curve.IsInt64() { + if !curve.IsUint64() { return nil, errors.New("not an int64") } - c := curve.Int64() + c := curve.Uint64() switch c { - case int64(Secp256k1): + case uint64(Secp256k1): return secp256k1.S256(), nil - case int64(Secp256r1): + case uint64(Secp256r1): return elliptic.P256(), nil default: return nil, errors.New("unsupported curve type") diff --git a/pkg/core/native/designate.go b/pkg/core/native/designate.go index caf587ccf2..6514ed64f9 100644 --- a/pkg/core/native/designate.go +++ b/pkg/core/native/designate.go @@ -5,10 +5,10 @@ import ( "errors" "fmt" "math" - "math/big" "sort" "sync/atomic" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/pkg/core/dao" "github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/interop/runtime" @@ -385,8 +385,8 @@ func (s *Designate) DesignateAsRole(ic *interop.Context, r noderoles.Role, pubs } ic.AddNotification(s.Hash, DesignationEventName, stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(r))), - stackitem.NewBigInteger(big.NewInt(int64(ic.Block.Index))), + stackitem.NewBigInteger(uint256.NewInt(uint64(r))), + stackitem.NewBigInteger(uint256.NewInt(uint64(ic.Block.Index))), })) return nil } diff --git a/pkg/core/native/interop.go b/pkg/core/native/interop.go index 00f0f3c739..3957e67d4f 100644 --- a/pkg/core/native/interop.go +++ b/pkg/core/native/interop.go @@ -14,7 +14,7 @@ import ( // Call calls the specified native contract method. func Call(ic *interop.Context) error { - version := ic.VM.Estack().Pop().BigInt().Int64() + version := ic.VM.Estack().Pop().BigInt().Uint64() if version != 0 { return fmt.Errorf("native contract of version %d is not active", version) } diff --git a/pkg/core/native/ledger.go b/pkg/core/native/ledger.go index 51734c2260..870cb37cc6 100644 --- a/pkg/core/native/ledger.go +++ b/pkg/core/native/ledger.go @@ -3,8 +3,8 @@ package native import ( "fmt" "math" - "math/big" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/pkg/core/dao" "github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" @@ -245,7 +245,7 @@ func SignersToStackItem(signers []transaction.Signer) stackitem.Item { } res[i] = stackitem.NewArray([]stackitem.Item{ stackitem.NewByteArray(s.Account.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(s.Scopes))), + stackitem.NewBigInteger(uint256.NewInt(uint64(s.Scopes))), stackitem.NewArray(contracts), stackitem.NewArray(groups), stackitem.NewArray(rules), diff --git a/pkg/core/native/management.go b/pkg/core/native/management.go index c56159398d..a844fff0a8 100644 --- a/pkg/core/native/management.go +++ b/pkg/core/native/management.go @@ -7,7 +7,6 @@ import ( "errors" "fmt" "math" - "math/big" "unicode/utf8" "github.com/nspcc-dev/neo-go/pkg/core/dao" @@ -202,8 +201,8 @@ func (m *Management) getContractByID(ic *interop.Context, args []stackitem.Item) if err != nil { panic(err) } - id := idBig.Int64() - if !idBig.IsInt64() || id < math.MinInt32 || id > math.MaxInt32 { + id := util.ToInt64(idBig) + if !util.IsInt64(idBig) || id < math.MinInt32 || id > math.MaxInt32 { panic("id is not a correct int32") } ctr, err := GetContractByID(ic.DAO, int32(id)) @@ -508,7 +507,7 @@ func (m *Management) Destroy(d *dao.Simple, hash util.Uint160) error { } func (m *Management) getMinimumDeploymentFee(ic *interop.Context, args []stackitem.Item) stackitem.Item { - return stackitem.NewBigInteger(big.NewInt(m.minimumDeploymentFee(ic.DAO))) + return stackitem.NewBigIntegerFromInt64(m.minimumDeploymentFee(ic.DAO)) } // minimumDeploymentFee returns the minimum required fee for contract deploy. diff --git a/pkg/core/native/native_neo.go b/pkg/core/native/native_neo.go index 756f308ef1..5490d6bc7e 100644 --- a/pkg/core/native/native_neo.go +++ b/pkg/core/native/native_neo.go @@ -10,6 +10,7 @@ import ( "sort" "strings" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/core/dao" "github.com/nspcc-dev/neo-go/pkg/core/interop" @@ -543,12 +544,16 @@ func (n *NEO) unclaimedGas(ic *interop.Context, args []stackitem.Item) stackitem if err != nil { panic(err) } - return stackitem.NewBigInteger(gen) + g, overflow := uint256.FromBig(gen) + if overflow { + panic("overflow") + } + return stackitem.NewBigInteger(g) } func (n *NEO) getGASPerBlock(ic *interop.Context, _ []stackitem.Item) stackitem.Item { gas := n.GetGASPerBlock(ic.DAO, ic.Block.Index) - return stackitem.NewBigInteger(gas) + return stackitem.NewBigIntegerFromBig(gas) } func (n *NEO) getSortedGASRecordFromDAO(d *dao.Simple) gasRecord { @@ -617,7 +622,7 @@ func (n *NEO) SetGASPerBlock(ic *interop.Context, index uint32, gas *big.Int) er } func (n *NEO) getRegisterPrice(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - return stackitem.NewBigInteger(big.NewInt(n.getRegisterPriceInternal(ic.DAO))) + return stackitem.NewBigIntegerFromInt64(n.getRegisterPriceInternal(ic.DAO)) } func (n *NEO) getRegisterPriceInternal(d *dao.Simple) int64 { @@ -764,7 +769,7 @@ func (n *NEO) RegisterCandidateInternal(ic *interop.Context, pub *keys.PublicKey ic.AddNotification(n.Hash, "CandidateStateChanged", stackitem.NewArray([]stackitem.Item{ stackitem.NewByteArray(pub.Bytes()), stackitem.NewBool(c.Registered), - stackitem.NewBigInteger(&c.Votes), + stackitem.NewBigIntegerFromBig(&c.Votes), })) } return err @@ -804,7 +809,7 @@ func (n *NEO) UnregisterCandidateInternal(ic *interop.Context, pub *keys.PublicK ic.AddNotification(n.Hash, "CandidateStateChanged", stackitem.NewArray([]stackitem.Item{ stackitem.NewByteArray(pub.Bytes()), stackitem.NewBool(c.Registered), - stackitem.NewBigInteger(&c.Votes), + stackitem.NewBigIntegerFromBig(&c.Votes), })) } return err @@ -880,7 +885,7 @@ func (n *NEO) VoteInternal(ic *interop.Context, h util.Uint160, pub *keys.Public stackitem.NewByteArray(h.BytesBE()), keyToStackItem(oldVote), keyToStackItem(pub), - stackitem.NewBigInteger(&acc.Balance), + stackitem.NewBigIntegerFromBig(&acc.Balance), })) if newGas != nil { // Can be if it was already distributed in the same block. @@ -987,7 +992,7 @@ func (n *NEO) getCandidatesCall(ic *interop.Context, _ []stackitem.Item) stackit for i := range validators { arr[i] = stackitem.NewStruct([]stackitem.Item{ stackitem.NewByteArray([]byte(validators[i].Key)), - stackitem.NewBigInteger(validators[i].Votes), + stackitem.NewBigIntegerFromBig(validators[i].Votes), }) } return stackitem.NewArray(arr) @@ -1033,13 +1038,13 @@ func (n *NEO) getCandidateVoteCall(ic *interop.Context, args []stackitem.Item) s key := makeValidatorKey(pub) si := ic.DAO.GetStorageItem(n.ID, key) if si == nil { - return stackitem.NewBigInteger(big.NewInt(-1)) + return stackitem.NewBigIntegerFromInt64(-1) } c := new(candidate).FromBytes(si) if !c.Registered { - return stackitem.NewBigInteger(big.NewInt(-1)) + return stackitem.NewBigIntegerFromInt64(-1) } - return stackitem.NewBigInteger(&c.Votes) + return stackitem.NewBigIntegerFromBig(&c.Votes) } func (n *NEO) getAccountState(ic *interop.Context, args []stackitem.Item) stackitem.Item { diff --git a/pkg/core/native/native_neo_candidate.go b/pkg/core/native/native_neo_candidate.go index 068d6dde5a..6ae3b55f4f 100644 --- a/pkg/core/native/native_neo_candidate.go +++ b/pkg/core/native/native_neo_candidate.go @@ -3,6 +3,7 @@ package native import ( "math/big" + "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) @@ -24,7 +25,7 @@ func (c *candidate) FromBytes(data []byte) *candidate { func (c *candidate) ToStackItem() (stackitem.Item, error) { return stackitem.NewStruct([]stackitem.Item{ stackitem.NewBool(c.Registered), - stackitem.NewBigInteger(&c.Votes), + stackitem.NewBigIntegerFromBig(&c.Votes), }), nil } @@ -40,6 +41,6 @@ func (c *candidate) FromStackItem(item stackitem.Item) error { return err } c.Registered = reg - c.Votes = *vs + c.Votes = *util.ToBig(vs) return nil } diff --git a/pkg/core/native/native_nep17.go b/pkg/core/native/native_nep17.go index 6a2c42c02d..62a143afee 100644 --- a/pkg/core/native/native_nep17.go +++ b/pkg/core/native/native_nep17.go @@ -6,6 +6,7 @@ import ( "math" "math/big" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/pkg/core/dao" "github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/interop/contract" @@ -91,12 +92,16 @@ func (c *nep17TokenNative) Symbol(_ *interop.Context, _ []stackitem.Item) stacki } func (c *nep17TokenNative) Decimals(_ *interop.Context, _ []stackitem.Item) stackitem.Item { - return stackitem.NewBigInteger(big.NewInt(c.decimals)) + return stackitem.NewBigInteger(uint256.NewInt(uint64(c.decimals))) } func (c *nep17TokenNative) TotalSupply(ic *interop.Context, _ []stackitem.Item) stackitem.Item { _, supply := c.getTotalSupply(ic.DAO) - return stackitem.NewBigInteger(supply) + s, overflow := uint256.FromBig(supply) + if overflow { + panic("overflow") + } + return stackitem.NewBigInteger(s) } func (c *nep17TokenNative) getTotalSupply(d *dao.Simple) (state.StorageItem, *big.Int) { @@ -152,9 +157,13 @@ func (c *nep17TokenNative) postTransfer(ic *interop.Context, from, to *util.Uint if from != nil { fromArg = stackitem.NewByteArray((*from).BytesBE()) } + a, overflow := uint256.FromBig(amount) + if overflow { + panic("overflow") + } args := []stackitem.Item{ fromArg, - stackitem.NewBigInteger(amount), + stackitem.NewBigInteger(a), data, } if err := contract.CallFromNative(ic, c.Hash, cs, manifest.MethodOnNEP17Payment, args, false); err != nil { @@ -164,10 +173,14 @@ func (c *nep17TokenNative) postTransfer(ic *interop.Context, from, to *util.Uint } func (c *nep17TokenNative) emitTransfer(ic *interop.Context, from, to *util.Uint160, amount *big.Int) { + a, overflow := uint256.FromBig(amount) + if overflow { + panic("overflow") + } ic.AddNotification(c.Hash, "Transfer", stackitem.NewArray([]stackitem.Item{ addrToStackItem(from), addrToStackItem(to), - stackitem.NewBigInteger(amount), + stackitem.NewBigInteger(a), })) } @@ -245,7 +258,12 @@ func (c *nep17TokenNative) TransferInternal(ic *interop.Context, from, to util.U func (c *nep17TokenNative) balanceOf(ic *interop.Context, args []stackitem.Item) stackitem.Item { h := toUint160(args[0]) - return stackitem.NewBigInteger(c.balanceOfInternal(ic.DAO, h)) + balance := c.balanceOfInternal(ic.DAO, h) + b, overflow := uint256.FromBig(balance) + if overflow { + panic("overflow") + } + return stackitem.NewBigInteger(b) } func (c *nep17TokenNative) balanceOfInternal(d *dao.Simple, h util.Uint160) *big.Int { @@ -329,7 +347,7 @@ func toBigInt(s stackitem.Item) *big.Int { if err != nil { panic(err) } - return bi + return util.ToBig(bi) } func toUint160(s stackitem.Item) util.Uint160 { diff --git a/pkg/core/native/native_test/ledger_test.go b/pkg/core/native/native_test/ledger_test.go index 3af6d346a2..90623e6853 100644 --- a/pkg/core/native/native_test/ledger_test.go +++ b/pkg/core/native/native_test/ledger_test.go @@ -2,10 +2,10 @@ package native_test import ( "fmt" - "math/big" "strings" "testing" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/pkg/compiler" "github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" @@ -86,12 +86,12 @@ func TestLedger_GetTransaction(t *testing.T) { t.Run("success", func(t *testing.T) { ledgerInvoker.Invoke(t, []stackitem.Item{ stackitem.NewByteArray(tx.Hash().BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(tx.Version))), - stackitem.NewBigInteger(big.NewInt(int64(tx.Nonce))), + stackitem.NewBigInteger(uint256.NewInt(uint64(tx.Version))), + stackitem.NewBigInteger(uint256.NewInt(uint64(tx.Nonce))), stackitem.NewByteArray(tx.Sender().BytesBE()), - stackitem.NewBigInteger(big.NewInt(tx.SystemFee)), - stackitem.NewBigInteger(big.NewInt(tx.NetworkFee)), - stackitem.NewBigInteger(big.NewInt(int64(tx.ValidUntilBlock))), + stackitem.NewBigIntegerFromInt64(tx.SystemFee), + stackitem.NewBigIntegerFromInt64(tx.NetworkFee), + stackitem.NewBigInteger(uint256.NewInt(uint64(tx.ValidUntilBlock))), stackitem.NewByteArray(tx.Script), }, "getTransaction", tx.Hash()) }) @@ -153,14 +153,14 @@ func TestLedger_GetBlock(t *testing.T) { expected := []stackitem.Item{ stackitem.NewByteArray(b.Hash().BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(b.Version))), + stackitem.NewBigInteger(uint256.NewInt(uint64(b.Version))), stackitem.NewByteArray(b.PrevHash.BytesBE()), stackitem.NewByteArray(b.MerkleRoot.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(b.Timestamp))), - stackitem.NewBigInteger(big.NewInt(int64(b.Nonce))), - stackitem.NewBigInteger(big.NewInt(int64(b.Index))), + stackitem.NewBigInteger(uint256.NewInt(uint64(b.Timestamp))), + stackitem.NewBigInteger(uint256.NewInt(uint64(b.Nonce))), + stackitem.NewBigInteger(uint256.NewInt(uint64(b.Index))), stackitem.NewByteArray(b.NextConsensus.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(len(b.Transactions)))), + stackitem.NewBigInteger(uint256.NewInt(uint64(len(b.Transactions)))), } t.Run("good, by hash", func(t *testing.T) { ledgerInvoker.Invoke(t, expected, "getBlock", b.Hash()) @@ -192,7 +192,7 @@ func TestLedger_GetTransactionSigners(t *testing.T) { expected := stackitem.NewArray([]stackitem.Item{ stackitem.NewArray([]stackitem.Item{ stackitem.NewByteArray(s.Account.BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(s.Scopes))), + stackitem.NewBigInteger(uint256.NewInt(uint64(s.Scopes))), stackitem.NewArray([]stackitem.Item{}), stackitem.NewArray([]stackitem.Item{}), stackitem.NewArray([]stackitem.Item{}), diff --git a/pkg/core/native/native_test/neo_test.go b/pkg/core/native/native_test/neo_test.go index 011636ca92..9a368282e6 100644 --- a/pkg/core/native/native_test/neo_test.go +++ b/pkg/core/native/native_test/neo_test.go @@ -8,6 +8,7 @@ import ( "sort" "testing" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/internal/contracts" "github.com/nspcc-dev/neo-go/internal/random" "github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames" @@ -359,7 +360,7 @@ func TestNEO_TransferOnPayment(t *testing.T) { require.NoError(t, err) managementValidatorsInvoker.Invoke(t, si, "deploy", nefB, manifB) - const amount int64 = 2 + const amount uint64 = 2 h := neoValidatorsInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), cs.Hash, amount, nil) aer := e.GetTxExecResult(t, h) @@ -370,7 +371,7 @@ func TestNEO_TransferOnPayment(t *testing.T) { Item: stackitem.NewArray([]stackitem.Item{ stackitem.NewByteArray(neoValidatorsInvoker.Hash.BytesBE()), stackitem.NewByteArray(e.Validator.ScriptHash().BytesBE()), - stackitem.NewBigInteger(big.NewInt(amount)), + stackitem.NewBigInteger(uint256.NewInt(amount)), stackitem.Null{}, }), }) @@ -384,7 +385,7 @@ func TestNEO_TransferOnPayment(t *testing.T) { Item: stackitem.NewArray([]stackitem.Item{ stackitem.NewByteArray(e.NativeHash(t, nativenames.Neo).BytesBE()), stackitem.NewByteArray(e.Validator.ScriptHash().BytesBE()), - stackitem.NewBigInteger(big.NewInt(amount)), + stackitem.NewBigInteger(uint256.NewInt(amount)), stackitem.Null{}, }), }) @@ -394,7 +395,7 @@ func TestNEO_TransferOnPayment(t *testing.T) { Item: stackitem.NewArray([]stackitem.Item{ stackitem.NewByteArray(e.NativeHash(t, nativenames.Gas).BytesBE()), stackitem.Null{}, - stackitem.NewBigInteger(big.NewInt(1)), + stackitem.NewBigInteger(uint256.NewInt(1)), stackitem.Null{}, }), }) @@ -445,8 +446,8 @@ func TestNEO_TransferZeroWithZeroBalance(t *testing.T) { h := acc.Invoke(t, true, "transfer", accH, to, int64(0), nil) aer, err := e.Chain.GetAppExecResults(h, trigger.Application) require.NoError(t, err) - require.Equal(t, 1, len(aer[0].Events)) // roundtrip/transfer only, no GAS claim - require.Equal(t, stackitem.NewBigInteger(big.NewInt(0)), aer[0].Events[0].Item.Value().([]stackitem.Item)[2]) // amount is 0 + require.Equal(t, 1, len(aer[0].Events)) // roundtrip/transfer only, no GAS claim + require.Equal(t, stackitem.NewBigInteger(uint256.NewInt(0)), aer[0].Events[0].Item.Value().([]stackitem.Item)[2]) // amount is 0 // check balance wasn't changed and height was updated updatedBalance, updatedHeight := e.Chain.GetGoverningTokenBalance(accH) require.Equal(t, int64(0), updatedBalance.Int64()) @@ -478,8 +479,8 @@ func TestNEO_TransferZeroWithNonZeroBalance(t *testing.T) { aer, err := e.Chain.GetAppExecResults(h, trigger.Application) require.NoError(t, err) - require.Equal(t, 2, len(aer[0].Events)) // roundtrip + GAS claim - require.Equal(t, stackitem.NewBigInteger(big.NewInt(0)), aer[0].Events[0].Item.Value().([]stackitem.Item)[2]) // amount is 0 + require.Equal(t, 2, len(aer[0].Events)) // roundtrip + GAS claim + require.Equal(t, stackitem.NewBigInteger(uint256.NewInt(0)), aer[0].Events[0].Item.Value().([]stackitem.Item)[2]) // amount is 0 // check balance wasn't changed and height was updated updatedBalance, updatedHeight := e.Chain.GetGoverningTokenBalance(acc.ScriptHash()) require.Equal(t, initialBalance, updatedBalance) @@ -577,7 +578,7 @@ func TestNEO_GetCandidates(t *testing.T) { expected := make([]stackitem.Item, candidatesCount) for i := range expected { pub := candidates[i].(neotest.SingleSigner).Account().PublicKey().Bytes() - v := stackitem.NewBigInteger(big.NewInt(int64(candidatesCount-i+1) * 1000000)) + v := stackitem.NewBigInteger(uint256.NewInt(uint64(candidatesCount-i+1) * 1000000)) expected[i] = stackitem.NewStruct([]stackitem.Item{ stackitem.NewByteArray(pub), v, diff --git a/pkg/core/native/native_test/oracle_test.go b/pkg/core/native/native_test/oracle_test.go index f7ce224f3d..298a62e06c 100644 --- a/pkg/core/native/native_test/oracle_test.go +++ b/pkg/core/native/native_test/oracle_test.go @@ -3,11 +3,11 @@ package native_test import ( "encoding/json" "math" - "math/big" "path/filepath" "strings" "testing" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/internal/contracts" "github.com/nspcc-dev/neo-go/pkg/core/native" "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" @@ -127,7 +127,7 @@ func TestOracle_Request(t *testing.T) { require.Equal(t, stackitem.NewArray([]stackitem.Item{ stackitem.NewByteArray([]byte("url")), stackitem.NewByteArray(userData), - stackitem.NewBigInteger(big.NewInt(int64(tx.Attributes[0].Value.(*transaction.OracleResponse).Code))), + stackitem.NewBigInteger(uint256.NewInt(uint64(tx.Attributes[0].Value.(*transaction.OracleResponse).Code))), stackitem.NewByteArray(tx.Attributes[0].Value.(*transaction.OracleResponse).Result), }), actual) diff --git a/pkg/core/native/neo_types.go b/pkg/core/native/neo_types.go index 530f7b935f..1a0c9c343e 100644 --- a/pkg/core/native/neo_types.go +++ b/pkg/core/native/neo_types.go @@ -6,6 +6,7 @@ import ( "math/big" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) @@ -46,7 +47,7 @@ func (k keysWithVotes) toStackItem() stackitem.Item { for i := range k { arr[i] = stackitem.NewStruct([]stackitem.Item{ stackitem.NewByteArray([]byte(k[i].Key)), - stackitem.NewBigInteger(k[i].Votes), + stackitem.NewBigIntegerFromBig(k[i].Votes), }) } return stackitem.NewArray(arr) @@ -75,7 +76,7 @@ func (k *keysWithVotes) fromStackItem(item stackitem.Item) error { return err } kvs[i].Key = string(pub) - kvs[i].Votes = vs + kvs[i].Votes = util.ToBig(vs) } *k = kvs return nil diff --git a/pkg/core/native/notary.go b/pkg/core/native/notary.go index 93f2af7880..6f2c0f0cbc 100644 --- a/pkg/core/native/notary.go +++ b/pkg/core/native/notary.go @@ -6,6 +6,7 @@ import ( "math" "math/big" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/pkg/core/dao" "github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/interop/contract" @@ -315,7 +316,7 @@ func (n *Notary) withdraw(ic *interop.Context, args []stackitem.Item) stackitem. if err != nil { panic(fmt.Errorf("failed to get GAS contract state: %w", err)) } - transferArgs := []stackitem.Item{stackitem.NewByteArray(n.Hash.BytesBE()), stackitem.NewByteArray(to.BytesBE()), stackitem.NewBigInteger(deposit.Amount), stackitem.Null{}} + transferArgs := []stackitem.Item{stackitem.NewByteArray(n.Hash.BytesBE()), stackitem.NewByteArray(to.BytesBE()), stackitem.NewBigIntegerFromBig(deposit.Amount), stackitem.Null{}} err = contract.CallFromNative(ic, n.Hash, cs, "transfer", transferArgs, true) if err != nil { panic(fmt.Errorf("failed to transfer GAS from Notary account: %w", err)) @@ -330,7 +331,7 @@ func (n *Notary) withdraw(ic *interop.Context, args []stackitem.Item) stackitem. // balanceOf returns the deposited GAS amount for the specified address. func (n *Notary) balanceOf(ic *interop.Context, args []stackitem.Item) stackitem.Item { acc := toUint160(args[0]) - return stackitem.NewBigInteger(n.BalanceOf(ic.DAO, acc)) + return stackitem.NewBigIntegerFromBig(n.BalanceOf(ic.DAO, acc)) } // BalanceOf is an internal representation of `balanceOf` Notary method. @@ -408,7 +409,7 @@ func (n *Notary) GetNotaryNodes(d *dao.Simple) (keys.PublicKeys, error) { // getMaxNotValidBeforeDelta is a Notary contract method and returns the maximum NotValidBefore delta. func (n *Notary) getMaxNotValidBeforeDelta(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - return stackitem.NewBigInteger(big.NewInt(int64(n.GetMaxNotValidBeforeDelta(ic.DAO)))) + return stackitem.NewBigInteger(uint256.NewInt(uint64(n.GetMaxNotValidBeforeDelta(ic.DAO)))) } // GetMaxNotValidBeforeDelta is an internal representation of Notary getMaxNotValidBeforeDelta method. @@ -436,7 +437,7 @@ func (n *Notary) setMaxNotValidBeforeDelta(ic *interop.Context, args []stackitem // getNotaryServiceFeePerKey is a Notary contract method and returns a reward per notary request key for notary nodes. func (n *Notary) getNotaryServiceFeePerKey(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - return stackitem.NewBigInteger(big.NewInt(int64(n.GetNotaryServiceFeePerKey(ic.DAO)))) + return stackitem.NewBigInteger(uint256.NewInt(uint64(n.GetNotaryServiceFeePerKey(ic.DAO)))) } // GetNotaryServiceFeePerKey is an internal representation of Notary getNotaryServiceFeePerKey method. diff --git a/pkg/core/native/oracle.go b/pkg/core/native/oracle.go index 0189f4fd3e..217160f28e 100644 --- a/pkg/core/native/oracle.go +++ b/pkg/core/native/oracle.go @@ -9,6 +9,7 @@ import ( "strings" "sync/atomic" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/pkg/core/dao" "github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/interop/contract" @@ -346,7 +347,7 @@ func (o *Oracle) request(ic *interop.Context, args []stackitem.Item) stackitem.I if !ic.VM.AddGas(o.getPriceInternal(ic.DAO)) { panic("insufficient gas") } - if err := o.RequestInternal(ic, url, filter, cb, userData, gas); err != nil { + if err := o.RequestInternal(ic, url, filter, cb, userData, util.ToBig(gas)); err != nil { panic(err) } return stackitem.Null{} @@ -468,7 +469,7 @@ func (o *Oracle) verify(ic *interop.Context, _ []stackitem.Item) stackitem.Item } func (o *Oracle) getPrice(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - return stackitem.NewBigInteger(big.NewInt(o.getPriceInternal(ic.DAO))) + return stackitem.NewBigInteger(uint256.NewInt(uint64(o.getPriceInternal(ic.DAO)))) } func (o *Oracle) getPriceInternal(d *dao.Simple) int64 { diff --git a/pkg/core/native/oracle_types.go b/pkg/core/native/oracle_types.go index ae5c8c6cd9..5d48bd08f9 100644 --- a/pkg/core/native/oracle_types.go +++ b/pkg/core/native/oracle_types.go @@ -3,8 +3,8 @@ package native import ( "crypto/elliptic" "errors" - "math/big" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) @@ -19,7 +19,7 @@ type NodeList keys.PublicKeys func (l IDList) ToStackItem() (stackitem.Item, error) { arr := make([]stackitem.Item, len(l)) for i := range l { - arr[i] = stackitem.NewBigInteger(new(big.Int).SetUint64(l[i])) + arr[i] = stackitem.NewBigInteger(uint256.NewInt(l[i])) } return stackitem.NewArray(arr), nil } diff --git a/pkg/core/native/policy.go b/pkg/core/native/policy.go index 570dfc40a6..240f1dba9f 100644 --- a/pkg/core/native/policy.go +++ b/pkg/core/native/policy.go @@ -2,9 +2,9 @@ package native import ( "fmt" - "math/big" "sort" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/pkg/core/dao" "github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames" @@ -199,7 +199,7 @@ func (p *Policy) PostPersist(ic *interop.Context) error { // getFeePerByte is a Policy contract method that returns the required transaction's fee // per byte. func (p *Policy) getFeePerByte(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - return stackitem.NewBigInteger(big.NewInt(p.GetFeePerByteInternal(ic.DAO))) + return stackitem.NewBigInteger(uint256.NewInt(uint64(p.GetFeePerByteInternal(ic.DAO)))) } // GetFeePerByteInternal returns required transaction's fee per byte. @@ -215,7 +215,7 @@ func (p *Policy) GetMaxVerificationGas(dao *dao.Simple) int64 { } func (p *Policy) getExecFeeFactor(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - return stackitem.NewBigInteger(big.NewInt(int64(p.GetExecFeeFactorInternal(ic.DAO)))) + return stackitem.NewBigInteger(uint256.NewInt(uint64(p.GetExecFeeFactorInternal(ic.DAO)))) } // GetExecFeeFactorInternal returns current execution fee factor. @@ -267,7 +267,7 @@ func (p *Policy) isBlockedInternal(dao *dao.Simple, hash util.Uint160) (int, boo } func (p *Policy) getStoragePrice(ic *interop.Context, _ []stackitem.Item) stackitem.Item { - return stackitem.NewBigInteger(big.NewInt(p.GetStoragePriceInternal(ic.DAO))) + return stackitem.NewBigInteger(uint256.NewInt(uint64(p.GetStoragePriceInternal(ic.DAO)))) } // GetStoragePriceInternal returns current execution fee factor. diff --git a/pkg/core/native/std.go b/pkg/core/native/std.go index 733fd73382..a5425f9e78 100644 --- a/pkg/core/native/std.go +++ b/pkg/core/native/std.go @@ -247,7 +247,7 @@ func (s *Std) itoa(_ *interop.Context, args []stackitem.Item) stackitem.Item { func (s *Std) atoi10(_ *interop.Context, args []stackitem.Item) stackitem.Item { num := s.toLimitedString(args[0]) res := s.atoi10Aux(num) - return stackitem.NewBigInteger(res) + return stackitem.NewBigIntegerFromBig(res) } func (s *Std) atoi10Aux(num string) *big.Int { @@ -286,7 +286,7 @@ func (s *Std) atoi(_ *interop.Context, args []stackitem.Item) stackitem.Item { panic(ErrInvalidBase) } - return stackitem.NewBigInteger(bi) + return stackitem.NewBigIntegerFromBig(bi) } func (s *Std) base64Encode(_ *interop.Context, args []stackitem.Item) stackitem.Item { @@ -343,14 +343,14 @@ func (s *Std) base58CheckDecode(_ *interop.Context, args []stackitem.Item) stack func (s *Std) memoryCompare(_ *interop.Context, args []stackitem.Item) stackitem.Item { s1 := s.toLimitedBytes(args[0]) s2 := s.toLimitedBytes(args[1]) - return stackitem.NewBigInteger(big.NewInt(int64(bytes.Compare(s1, s2)))) + return stackitem.NewBigIntegerFromInt64(int64(bytes.Compare(s1, s2))) } func (s *Std) memorySearch2(_ *interop.Context, args []stackitem.Item) stackitem.Item { mem := s.toLimitedBytes(args[0]) val := s.toLimitedBytes(args[1]) index := s.memorySearchAux(mem, val, 0, false) - return stackitem.NewBigInteger(big.NewInt(int64(index))) + return stackitem.NewBigIntegerFromInt64(int64(index)) } func (s *Std) memorySearch3(_ *interop.Context, args []stackitem.Item) stackitem.Item { @@ -358,7 +358,7 @@ func (s *Std) memorySearch3(_ *interop.Context, args []stackitem.Item) stackitem val := s.toLimitedBytes(args[1]) start := toUint32(args[2]) index := s.memorySearchAux(mem, val, int(start), false) - return stackitem.NewBigInteger(big.NewInt(int64(index))) + return stackitem.NewBigIntegerFromInt64(int64(index)) } func (s *Std) memorySearch4(_ *interop.Context, args []stackitem.Item) stackitem.Item { @@ -371,7 +371,7 @@ func (s *Std) memorySearch4(_ *interop.Context, args []stackitem.Item) stackitem } index := s.memorySearchAux(mem, val, int(start), backward) - return stackitem.NewBigInteger(big.NewInt(int64(index))) + return stackitem.NewBigIntegerFromInt64(int64(index)) } func (s *Std) memorySearchAux(mem, val []byte, start int, backward bool) int { diff --git a/pkg/core/native/std_test.go b/pkg/core/native/std_test.go index 48b81b526b..cfbd3f9a4e 100644 --- a/pkg/core/native/std_test.go +++ b/pkg/core/native/std_test.go @@ -8,6 +8,7 @@ import ( "strings" "testing" + "github.com/holiman/uint256" "github.com/mr-tron/base58" "github.com/nspcc-dev/neo-go/pkg/core/dao" "github.com/nspcc-dev/neo-go/pkg/core/interop" @@ -323,7 +324,7 @@ func TestStdLibSerializeDeserialize(t *testing.T) { checkSerializeDeserialize(t, []byte{1, 2, 3}, stackitem.NewByteArray([]byte{1, 2, 3})) }) t.Run("Integer", func(t *testing.T) { - checkSerializeDeserialize(t, 48, stackitem.NewBigInteger(big.NewInt(48))) + checkSerializeDeserialize(t, 48, stackitem.NewBigInteger(uint256.NewInt(48))) }) t.Run("Array", func(t *testing.T) { arr := stackitem.NewArray([]stackitem.Item{ @@ -373,7 +374,7 @@ func TestStdLibSerializeDeserialize(t *testing.T) { }) }) t.Run("Deserialize unknown", func(t *testing.T) { - data, err := stackitem.Serialize(stackitem.NewBigInteger(big.NewInt(123))) + data, err := stackitem.Serialize(stackitem.NewBigInteger(uint256.NewInt(123))) require.NoError(t, err) data[0] = 0xFF diff --git a/pkg/core/state/deposit.go b/pkg/core/state/deposit.go index 3330031f7a..6db2ad799b 100644 --- a/pkg/core/state/deposit.go +++ b/pkg/core/state/deposit.go @@ -6,6 +6,7 @@ import ( "math" "math/big" + "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) @@ -19,7 +20,7 @@ type Deposit struct { // error. func (d *Deposit) ToStackItem() (stackitem.Item, error) { return stackitem.NewStruct([]stackitem.Item{ - stackitem.NewBigInteger(d.Amount), + stackitem.NewBigIntegerFromBig(d.Amount), stackitem.Make(d.Till), }), nil } @@ -45,7 +46,7 @@ func (d *Deposit) FromStackItem(it stackitem.Item) error { if !till.IsUint64() || tiu64 > math.MaxUint32 { return errors.New("wrong till value") } - d.Amount = amount + d.Amount = util.ToBig(amount) d.Till = uint32(tiu64) return nil } diff --git a/pkg/core/state/native_state.go b/pkg/core/state/native_state.go index 48eef1a0f9..ffba6ff10d 100644 --- a/pkg/core/state/native_state.go +++ b/pkg/core/state/native_state.go @@ -6,8 +6,10 @@ import ( "fmt" "math/big" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" + "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) @@ -72,7 +74,7 @@ func balanceFromBytes(b []byte, item stackitem.Convertible) error { // ToStackItem implements stackitem.Convertible. It never returns an error. func (s *NEP17Balance) ToStackItem() (stackitem.Item, error) { - return stackitem.NewStruct([]stackitem.Item{stackitem.NewBigInteger(&s.Balance)}), nil + return stackitem.NewStruct([]stackitem.Item{stackitem.NewBigIntegerFromBig(&s.Balance)}), nil } // FromStackItem implements stackitem.Convertible. @@ -88,7 +90,7 @@ func (s *NEP17Balance) FromStackItem(item stackitem.Item) error { if err != nil { return fmt.Errorf("invalid balance: %w", err) } - s.Balance = *balance + s.Balance = *util.ToBig(balance) return nil } @@ -122,8 +124,8 @@ func (s *NEOBalance) ToStackItem() (stackitem.Item, error) { voteItem = stackitem.Null{} } return stackitem.NewStruct([]stackitem.Item{ - stackitem.NewBigInteger(&s.Balance), - stackitem.NewBigInteger(big.NewInt(int64(s.BalanceHeight))), + stackitem.NewBigIntegerFromBig(&s.Balance), + stackitem.NewBigInteger(uint256.NewInt(uint64(s.BalanceHeight))), voteItem, }), nil } @@ -138,12 +140,12 @@ func (s *NEOBalance) FromStackItem(item stackitem.Item) error { if err != nil { return fmt.Errorf("invalid balance stackitem: %w", err) } - s.Balance = *balance + s.Balance = *util.ToBig(balance) h, err := structItem[1].TryInteger() if err != nil { return fmt.Errorf("invalid heigh stackitem") } - s.BalanceHeight = uint32(h.Int64()) + s.BalanceHeight = uint32(h.Uint64()) if _, ok := structItem[2].(stackitem.Null); ok { s.VoteTo = nil return nil diff --git a/pkg/core/state/oracle.go b/pkg/core/state/oracle.go index 6a3c29dcaf..10bdb5a9ab 100644 --- a/pkg/core/state/oracle.go +++ b/pkg/core/state/oracle.go @@ -2,9 +2,9 @@ package state import ( "errors" - "math/big" "unicode/utf8" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) @@ -29,7 +29,7 @@ func (o *OracleRequest) ToStackItem() (stackitem.Item, error) { } return stackitem.NewArray([]stackitem.Item{ stackitem.NewByteArray(o.OriginalTxID.BytesBE()), - stackitem.NewBigInteger(new(big.Int).SetUint64(o.GasForResponse)), + stackitem.NewBigInteger(uint256.NewInt(o.GasForResponse)), stackitem.Make(o.URL), filter, stackitem.NewByteArray(o.CallbackContract.BytesBE()), diff --git a/pkg/core/state/oracle_test.go b/pkg/core/state/oracle_test.go index 07e2ce1f60..8472d43586 100644 --- a/pkg/core/state/oracle_test.go +++ b/pkg/core/state/oracle_test.go @@ -1,9 +1,9 @@ package state import ( - "math/big" "testing" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/internal/random" "github.com/nspcc-dev/neo-go/internal/testserdes" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" @@ -37,7 +37,7 @@ func TestOracleRequestToFromSI(t *testing.T) { items := []stackitem.Item{ stackitem.NewByteArray(random.Uint256().BytesBE()), - stackitem.NewBigInteger(big.NewInt(123)), + stackitem.NewBigInteger(uint256.NewInt(123)), stackitem.Make("url"), stackitem.Null{}, stackitem.NewByteArray(random.Uint160().BytesBE()), diff --git a/pkg/core/transaction/transaction.go b/pkg/core/transaction/transaction.go index 3c201d042b..a563b5ec9d 100644 --- a/pkg/core/transaction/transaction.go +++ b/pkg/core/transaction/transaction.go @@ -6,9 +6,9 @@ import ( "errors" "fmt" "math" - "math/big" "math/rand" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/io" @@ -459,12 +459,12 @@ func (t *Transaction) HasSigner(hash util.Uint160) bool { func (t *Transaction) ToStackItem() stackitem.Item { return stackitem.NewArray([]stackitem.Item{ stackitem.NewByteArray(t.Hash().BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(t.Version))), - stackitem.NewBigInteger(big.NewInt(int64(t.Nonce))), + stackitem.NewBigInteger(uint256.NewInt(uint64(t.Version))), + stackitem.NewBigInteger(uint256.NewInt(uint64(t.Nonce))), stackitem.NewByteArray(t.Sender().BytesBE()), - stackitem.NewBigInteger(big.NewInt(int64(t.SystemFee))), - stackitem.NewBigInteger(big.NewInt(int64(t.NetworkFee))), - stackitem.NewBigInteger(big.NewInt(int64(t.ValidUntilBlock))), + stackitem.NewBigInteger(uint256.NewInt(uint64(t.SystemFee))), + stackitem.NewBigInteger(uint256.NewInt(uint64(t.NetworkFee))), + stackitem.NewBigInteger(uint256.NewInt(uint64(t.ValidUntilBlock))), stackitem.NewByteArray(t.Script), }) } diff --git a/pkg/core/transaction/witness_condition.go b/pkg/core/transaction/witness_condition.go index 65a73b09e4..9b38cbc2b8 100644 --- a/pkg/core/transaction/witness_condition.go +++ b/pkg/core/transaction/witness_condition.go @@ -3,8 +3,8 @@ package transaction import ( "encoding/json" "errors" - "math/big" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/util" @@ -633,7 +633,7 @@ func unmarshalConditionJSON(data []byte, maxDepth int) (WitnessCondition, error) func condToStackItem(typ WitnessConditionType, c interface{}) stackitem.Item { res := make([]stackitem.Item, 0, 2) - res = append(res, stackitem.NewBigInteger(big.NewInt(int64(typ)))) + res = append(res, stackitem.NewBigInteger(uint256.NewInt(uint64(typ)))) switch typ { case WitnessBoolean: res = append(res, stackitem.NewBool(c.(bool))) diff --git a/pkg/core/transaction/witness_rule.go b/pkg/core/transaction/witness_rule.go index 05661f1963..8054b69e83 100644 --- a/pkg/core/transaction/witness_rule.go +++ b/pkg/core/transaction/witness_rule.go @@ -3,8 +3,8 @@ package transaction import ( "encoding/json" "errors" - "math/big" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) @@ -90,7 +90,7 @@ func (w *WitnessRule) UnmarshalJSON(data []byte) error { // ToStackItem implements Convertible interface. func (w *WitnessRule) ToStackItem() stackitem.Item { return stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(int64(w.Action))), + stackitem.NewBigInteger(uint256.NewInt(uint64(w.Action))), w.Condition.ToStackItem(), }) } diff --git a/pkg/encoding/bigint/bigint.go b/pkg/encoding/bigint/bigint.go index 8274bb6842..7b5a93a1f6 100644 --- a/pkg/encoding/bigint/bigint.go +++ b/pkg/encoding/bigint/bigint.go @@ -5,6 +5,7 @@ import ( "math/big" "math/bits" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/pkg/util/slice" ) @@ -23,6 +24,14 @@ func FromBytesUnsigned(data []byte) *big.Int { return new(big.Int).SetBytes(bs) } +// Uint256FromBytesUnsigned converts data in little-endian format to an unsigned integer. +func Uint256FromBytesUnsigned(data []byte) *uint256.Int { + bs := slice.CopyReverse(data) + return new(uint256.Int).SetBytes(bs) +} + +// FromBytes converts data in little-endian format to +// an integer. // FromBytes converts data in little-endian format to // an integer. func FromBytes(data []byte) *big.Int { @@ -78,6 +87,34 @@ func FromBytes(data []byte) *big.Int { return n.SetBits(ws) } +func Uint256FromBytes(data []byte) *uint256.Int { + n := new(uint256.Int) + size := len(data) + if size == 0 { + if data == nil { + panic("nil slice provided to `FromBytes`") + } + return uint256.NewInt(0) + } + isNeg := data[len(data)-1]&0x80 != 0 + slice.Reverse(data) + if !isNeg { + n.SetBytes(data) + } else { + carry := true + for i := len(data) - 1; i >= 0; i-- { + if carry { + data[i]-- + carry = data[i] == math.MaxUint8 + } + data[i] = ^data[i] + } + n.SetBytes(data) + n.Neg(n) + } + return n +} + // getEffectiveSize returns the minimal number of bytes required // to represent a number (two's complement for negatives). func getEffectiveSize(buf []byte, isNeg bool) int { @@ -105,6 +142,8 @@ func ToBytes(n *big.Int) []byte { return ToPreallocatedBytes(n, []byte{}) } +// ToPreallocatedBytes converts an integer to a slice in little-endian format using the given +// byte array for conversion result. // ToPreallocatedBytes converts an integer to a slice in little-endian format using the given // byte array for conversion result. func ToPreallocatedBytes(n *big.Int, data []byte) []byte { @@ -158,3 +197,35 @@ func ToPreallocatedBytes(n *big.Int, data []byte) []byte { return data } + +func Uint256ToBytes(n *uint256.Int) []byte { + if n.Sign() == 0 { + return []byte{} + } + fill := true + var filler byte + b := n.Bytes() + if n.Sign() < 0 { + var sig int + for ; sig < len(b); sig++ { + if b[sig] < 0xff { + if b[sig] >= 0x80 { + fill = false + } + break + } + } + b = b[sig:] + filler = 0xff + } else { + filler = 0 + if b[0] < 0x80 { + fill = false + } + } + slice.Reverse(b) + if fill { + b = append(b, filler) + } + return b +} diff --git a/pkg/neorpc/result/invoke_test.go b/pkg/neorpc/result/invoke_test.go index 64a145740b..928e48988d 100644 --- a/pkg/neorpc/result/invoke_test.go +++ b/pkg/neorpc/result/invoke_test.go @@ -4,10 +4,10 @@ import ( "encoding/base64" "encoding/json" "errors" - "math/big" "testing" "github.com/google/uuid" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" @@ -28,7 +28,7 @@ func TestInvoke_MarshalJSON(t *testing.T) { State: "HALT", GasConsumed: 237626000, Script: []byte{10}, - Stack: []stackitem.Item{stackitem.NewBigInteger(big.NewInt(1))}, + Stack: []stackitem.Item{stackitem.NewBigInteger(uint256.NewInt(1))}, FaultException: "", Notifications: []state.NotificationEvent{}, Transaction: tx, @@ -66,7 +66,7 @@ func TestAppExecToInvocation(t *testing.T) { Trigger: trigger.Application, VMState: vmstate.Fault, GasConsumed: 123, - Stack: []stackitem.Item{stackitem.NewBigInteger(big.NewInt(123))}, + Stack: []stackitem.Item{stackitem.NewBigInteger(uint256.NewInt(123))}, Events: []state.NotificationEvent{{ ScriptHash: util.Uint160{3, 2, 1}, Name: "Notification", diff --git a/pkg/rpcclient/neo/neo.go b/pkg/rpcclient/neo/neo.go index b9b17ed336..a15f119b77 100644 --- a/pkg/rpcclient/neo/neo.go +++ b/pkg/rpcclient/neo/neo.go @@ -211,11 +211,11 @@ func itemsToValidators(arr []stackitem.Item) ([]result.Validator, error) { if err != nil { return nil, fmt.Errorf("item #%d has wrong votes: %w", i, err) } - if !votes.IsInt64() { + if !util.IsInt64(votes) { return nil, fmt.Errorf("item #%d has too big number of votes", i) } res[i].PublicKey = *k - res[i].Votes = votes.Int64() + res[i].Votes = util.ToInt64(votes) } return res, nil } diff --git a/pkg/rpcclient/rpc_test.go b/pkg/rpcclient/rpc_test.go index 815798f4b3..4690c0bc5c 100644 --- a/pkg/rpcclient/rpc_test.go +++ b/pkg/rpcclient/rpc_test.go @@ -16,6 +16,7 @@ import ( "time" "github.com/gorilla/websocket" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/internal/testserdes" "github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/core/block" @@ -131,7 +132,7 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ Trigger: trigger.Application, VMState: vmstate.Halt, GasConsumed: 1, - Stack: []stackitem.Item{stackitem.NewBigInteger(big.NewInt(1))}, + Stack: []stackitem.Item{stackitem.NewBigInteger(uint256.NewInt(1))}, Events: []state.NotificationEvent{}, }, }, diff --git a/pkg/rpcclient/unwrap/unwrap.go b/pkg/rpcclient/unwrap/unwrap.go index f4c2243082..330159f64d 100644 --- a/pkg/rpcclient/unwrap/unwrap.go +++ b/pkg/rpcclient/unwrap/unwrap.go @@ -38,7 +38,11 @@ func BigInt(r *result.Invoke, err error) (*big.Int, error) { if err != nil { return nil, err } - return itm.TryInteger() + n, err := itm.TryInteger() + if err != nil { + return nil, err + } + return util.ToBig(n), nil } // Bool expects correct execution (HALT state) with a single stack item @@ -62,10 +66,10 @@ func Int64(r *result.Invoke, err error) (int64, error) { if err != nil { return 0, err } - if !i.IsInt64() { + if !util.IsInt64(i) { return 0, errors.New("int64 overflow") } - return i.Int64(), nil + return util.ToInt64(i), nil } // LimitedInt64 is similar to Int64 except it allows to set minimum and maximum @@ -227,7 +231,7 @@ func ArrayOfBigInts(r *result.Invoke, err error) ([]*big.Int, error) { if err != nil { return nil, fmt.Errorf("element %d is not an integer: %w", i, err) } - res[i] = v + res[i] = util.ToBig(v) } return res, nil } diff --git a/pkg/services/rpcsrv/client_test.go b/pkg/services/rpcsrv/client_test.go index e0a0752782..148941de7d 100644 --- a/pkg/services/rpcsrv/client_test.go +++ b/pkg/services/rpcsrv/client_test.go @@ -18,6 +18,7 @@ import ( "github.com/google/uuid" "github.com/gorilla/websocket" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/internal/testchain" "github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/core" @@ -1623,7 +1624,7 @@ func TestClient_IteratorSessions(t *testing.T) { const storageItemsCount = 255 expected := make([][]byte, storageItemsCount) for i := 0; i < storageItemsCount; i++ { - expected[i] = stackitem.NewBigInteger(big.NewInt(int64(i))).Bytes() + expected[i] = stackitem.NewBigInteger(uint256.NewInt(uint64(i))).Bytes() } sort.Slice(expected, func(i, j int) bool { if len(expected[i]) != len(expected[j]) { @@ -1823,7 +1824,7 @@ func TestClient_InvokeAndPackIteratorResults(t *testing.T) { const storageItemsCount = 255 expected := make([][]byte, storageItemsCount) for i := 0; i < storageItemsCount; i++ { - expected[i] = stackitem.NewBigInteger(big.NewInt(int64(i))).Bytes() + expected[i] = stackitem.NewBigInteger(uint256.NewInt(uint64(i))).Bytes() } sort.Slice(expected, func(i, j int) bool { if len(expected[i]) != len(expected[j]) { @@ -1909,7 +1910,7 @@ func TestClient_Iterator_SessionConfigVariations(t *testing.T) { // Fill in expected stackitems set during the first test. expected = make([][]byte, storageItemsCount) for i := 0; i < storageItemsCount; i++ { - expected[i] = stackitem.NewBigInteger(big.NewInt(int64(i))).Bytes() + expected[i] = stackitem.NewBigInteger(uint256.NewInt(uint64(i))).Bytes() } sort.Slice(expected, func(i, j int) bool { if len(expected[i]) != len(expected[j]) { diff --git a/pkg/services/rpcsrv/server.go b/pkg/services/rpcsrv/server.go index e2a5d2a58d..772b67844a 100644 --- a/pkg/services/rpcsrv/server.go +++ b/pkg/services/rpcsrv/server.go @@ -896,10 +896,10 @@ func (s *Server) getNEP11Tokens(h util.Uint160, acc util.Uint160, bw *io.BufBinW if err != nil { return nil, "", 0, fmt.Errorf("`decimals` return value error: %w", err) } - if !dec.IsInt64() || dec.Sign() == -1 || dec.Int64() > math.MaxInt32 { + if !util.IsInt64(dec) || dec.Sign() == -1 || util.ToInt64(dec) > math.MaxInt32 { return nil, "", 0, errors.New("`decimals` returned a bad integer") } - return vals, sym, int(dec.Int64()), nil + return vals, sym, int(util.ToInt64(dec)), nil } func (s *Server) getNEP11Balances(ps params.Params) (interface{}, *neorpc.Error) { @@ -1137,10 +1137,10 @@ func (s *Server) getNEP17TokenBalance(h util.Uint160, acc util.Uint160, bw *io.B if err != nil { return nil, "", 0, fmt.Errorf("`decimals` return value error: %w", err) } - if !dec.IsInt64() || dec.Sign() == -1 || dec.Int64() > math.MaxInt32 { + if !util.IsInt64(dec) || dec.Sign() == -1 || util.ToInt64(dec) > math.MaxInt32 { return nil, "", 0, errors.New("`decimals` returned a bad integer") } - return res, sym, int(dec.Int64()), nil + return util.ToBig(res), sym, int(util.ToInt64(dec)), nil } func (s *Server) getNEP11DTokenBalance(h util.Uint160, acc util.Uint160, id []byte, bw *io.BufBinWriter) (*big.Int, error) { @@ -1153,7 +1153,7 @@ func (s *Server) getNEP11DTokenBalance(h util.Uint160, acc util.Uint160, id []by if err != nil { return nil, fmt.Errorf("unexpected `balanceOf` result type: %w", err) } - return res, nil + return util.ToBig(res), nil } func getTimestampsAndLimit(ps params.Params, index int) (uint64, uint64, int, int, error) { diff --git a/pkg/smartcontract/manifest/event_test.go b/pkg/smartcontract/manifest/event_test.go index 65b32183a9..af230376a6 100644 --- a/pkg/smartcontract/manifest/event_test.go +++ b/pkg/smartcontract/manifest/event_test.go @@ -1,7 +1,6 @@ package manifest import ( - "math/big" "testing" "github.com/nspcc-dev/neo-go/pkg/smartcontract" @@ -42,7 +41,7 @@ func TestEvent_ToStackItemFromStackItem(t *testing.T) { stackitem.NewArray([]stackitem.Item{ stackitem.NewStruct([]stackitem.Item{ stackitem.NewByteArray([]byte(m.Parameters[0].Name)), - stackitem.NewBigInteger(big.NewInt(int64(m.Parameters[0].Type))), + stackitem.NewBigIntegerFromInt64(int64(m.Parameters[0].Type)), }), }), }) diff --git a/pkg/smartcontract/manifest/manifest_test.go b/pkg/smartcontract/manifest/manifest_test.go index 82fcc13b4b..2a5e4ee4cd 100644 --- a/pkg/smartcontract/manifest/manifest_test.go +++ b/pkg/smartcontract/manifest/manifest_test.go @@ -2,9 +2,9 @@ package manifest import ( "encoding/json" - "math/big" "testing" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/internal/random" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/smartcontract" @@ -368,11 +368,11 @@ func TestABI_ToStackItemFromStackItem(t *testing.T) { stackitem.NewArray([]stackitem.Item{ stackitem.NewStruct([]stackitem.Item{ stackitem.NewByteArray([]byte("p1")), - stackitem.NewBigInteger(big.NewInt(int64(smartcontract.BoolType))), + stackitem.NewBigInteger(uint256.NewInt(uint64(smartcontract.BoolType))), }), }), - stackitem.NewBigInteger(big.NewInt(int64(smartcontract.StringType))), - stackitem.NewBigInteger(big.NewInt(int64(5))), + stackitem.NewBigInteger(uint256.NewInt(uint64(smartcontract.StringType))), + stackitem.NewBigInteger(uint256.NewInt(uint64(5))), stackitem.NewBool(true), }), }), @@ -382,7 +382,7 @@ func TestABI_ToStackItemFromStackItem(t *testing.T) { stackitem.NewArray([]stackitem.Item{ stackitem.NewStruct([]stackitem.Item{ stackitem.NewByteArray([]byte("p1")), - stackitem.NewBigInteger(big.NewInt(int64(smartcontract.BoolType))), + stackitem.NewBigInteger(uint256.NewInt(uint64(smartcontract.BoolType))), }), }), }), diff --git a/pkg/smartcontract/manifest/method.go b/pkg/smartcontract/manifest/method.go index 85f512fd0c..c2c67391be 100644 --- a/pkg/smartcontract/manifest/method.go +++ b/pkg/smartcontract/manifest/method.go @@ -4,6 +4,7 @@ import ( "errors" "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) @@ -76,7 +77,7 @@ func (m *Method) FromStackItem(item stackitem.Item) error { if err != nil { return err } - m.ReturnType, err = smartcontract.ConvertToParamType(int(rTyp.Int64())) + m.ReturnType, err = smartcontract.ConvertToParamType(int(util.ToInt64(rTyp))) if err != nil { return err } @@ -84,7 +85,7 @@ func (m *Method) FromStackItem(item stackitem.Item) error { if err != nil { return err } - m.Offset = int(offset.Int64()) + m.Offset = int(util.ToInt64(offset)) safe, err := method[4].TryBool() if err != nil { return err diff --git a/pkg/smartcontract/manifest/method_test.go b/pkg/smartcontract/manifest/method_test.go index 2f48eb7972..0e379809b4 100644 --- a/pkg/smartcontract/manifest/method_test.go +++ b/pkg/smartcontract/manifest/method_test.go @@ -1,9 +1,9 @@ package manifest import ( - "math/big" "testing" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" @@ -44,11 +44,11 @@ func TestMethod_ToStackItemFromStackItem(t *testing.T) { stackitem.NewArray([]stackitem.Item{ stackitem.NewStruct([]stackitem.Item{ stackitem.NewByteArray([]byte(m.Parameters[0].Name)), - stackitem.NewBigInteger(big.NewInt(int64(m.Parameters[0].Type))), + stackitem.NewBigIntegerFromInt64(int64(m.Parameters[0].Type)), }), }), - stackitem.NewBigInteger(big.NewInt(int64(m.ReturnType))), - stackitem.NewBigInteger(big.NewInt(int64(m.Offset))), + stackitem.NewBigIntegerFromInt64(int64(m.ReturnType)), + stackitem.NewBigIntegerFromInt64(int64(m.Offset)), stackitem.NewBool(m.Safe), }) CheckToFromStackItem(t, m, expected) @@ -62,8 +62,8 @@ func TestMethod_FromStackItemErrors(t *testing.T) { "invalid parameters type": stackitem.NewStruct([]stackitem.Item{stackitem.NewByteArray([]byte{}), stackitem.Null{}, stackitem.Null{}, stackitem.Null{}, stackitem.Null{}}), "invalid parameter": stackitem.NewStruct([]stackitem.Item{stackitem.NewByteArray([]byte{}), stackitem.NewArray([]stackitem.Item{stackitem.NewStruct([]stackitem.Item{})}), stackitem.Null{}, stackitem.Null{}, stackitem.Null{}}), "invalid return type": stackitem.NewStruct([]stackitem.Item{stackitem.NewByteArray([]byte{}), stackitem.NewArray([]stackitem.Item{}), stackitem.Null{}, stackitem.Null{}, stackitem.Null{}}), - "invalid offset": stackitem.NewStruct([]stackitem.Item{stackitem.NewByteArray([]byte{}), stackitem.NewArray([]stackitem.Item{}), stackitem.NewBigInteger(big.NewInt(1)), stackitem.NewInterop(nil), stackitem.Null{}}), - "invalid safe": stackitem.NewStruct([]stackitem.Item{stackitem.NewByteArray([]byte{}), stackitem.NewArray([]stackitem.Item{}), stackitem.NewBigInteger(big.NewInt(1)), stackitem.NewBigInteger(big.NewInt(5)), stackitem.NewInterop(nil)}), + "invalid offset": stackitem.NewStruct([]stackitem.Item{stackitem.NewByteArray([]byte{}), stackitem.NewArray([]stackitem.Item{}), stackitem.NewBigInteger(uint256.NewInt(1)), stackitem.NewInterop(nil), stackitem.Null{}}), + "invalid safe": stackitem.NewStruct([]stackitem.Item{stackitem.NewByteArray([]byte{}), stackitem.NewArray([]stackitem.Item{}), stackitem.NewBigInteger(uint256.NewInt(1)), stackitem.NewBigInteger(uint256.NewInt(5)), stackitem.NewInterop(nil)}), } for name, errCase := range errCases { t.Run(name, func(t *testing.T) { diff --git a/pkg/smartcontract/manifest/parameter.go b/pkg/smartcontract/manifest/parameter.go index 3e74245222..5f8d18f972 100644 --- a/pkg/smartcontract/manifest/parameter.go +++ b/pkg/smartcontract/manifest/parameter.go @@ -6,6 +6,7 @@ import ( "sort" "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) @@ -64,7 +65,7 @@ func (p *Parameter) FromStackItem(item stackitem.Item) error { if err != nil { return err } - p.Type, err = smartcontract.ConvertToParamType(int(typ.Int64())) + p.Type, err = smartcontract.ConvertToParamType(int(util.ToInt64(typ))) if err != nil { return err } diff --git a/pkg/smartcontract/manifest/parameter_test.go b/pkg/smartcontract/manifest/parameter_test.go index 9826e5f2a0..7080ecf357 100644 --- a/pkg/smartcontract/manifest/parameter_test.go +++ b/pkg/smartcontract/manifest/parameter_test.go @@ -1,7 +1,6 @@ package manifest import ( - "math/big" "testing" "github.com/nspcc-dev/neo-go/pkg/smartcontract" @@ -39,7 +38,7 @@ func TestParameter_ToStackItemFromStackItem(t *testing.T) { } expected := stackitem.NewStruct([]stackitem.Item{ stackitem.NewByteArray([]byte(p.Name)), - stackitem.NewBigInteger(big.NewInt(int64(p.Type))), + stackitem.NewBigIntegerFromInt64(int64(p.Type)), }) CheckToFromStackItem(t, p, expected) } @@ -50,7 +49,7 @@ func TestParameter_FromStackItemErrors(t *testing.T) { "invalid length": stackitem.NewStruct([]stackitem.Item{}), "invalid name type": stackitem.NewStruct([]stackitem.Item{stackitem.NewInterop(nil), stackitem.Null{}}), "invalid type type": stackitem.NewStruct([]stackitem.Item{stackitem.NewByteArray([]byte{}), stackitem.Null{}}), - "invalid type value": stackitem.NewStruct([]stackitem.Item{stackitem.NewByteArray([]byte{}), stackitem.NewBigInteger(big.NewInt(-100500))}), + "invalid type value": stackitem.NewStruct([]stackitem.Item{stackitem.NewByteArray([]byte{}), stackitem.NewBigIntegerFromInt64(-100500)}), } for name, errCase := range errCases { t.Run(name, func(t *testing.T) { diff --git a/pkg/smartcontract/parameter_test.go b/pkg/smartcontract/parameter_test.go index 295ed8f932..0b7ebd6640 100644 --- a/pkg/smartcontract/parameter_test.go +++ b/pkg/smartcontract/parameter_test.go @@ -9,6 +9,7 @@ import ( "strings" "testing" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" @@ -463,7 +464,7 @@ func TestExpandParameterToEmitableToStackitem(t *testing.T) { { In: Parameter{Type: IntegerType, Value: big.NewInt(123)}, Expected: big.NewInt(123), - ExpectedStackitem: stackitem.NewBigInteger(big.NewInt(123)), + ExpectedStackitem: stackitem.NewBigInteger(uint256.NewInt(123)), }, { In: Parameter{Type: ByteArrayType, Value: []byte{1, 2, 3}}, @@ -522,7 +523,7 @@ func TestExpandParameterToEmitableToStackitem(t *testing.T) { }}, Expected: []interface{}{big.NewInt(123), []byte{1, 2, 3}, []interface{}{true}}, ExpectedStackitem: stackitem.NewArray([]stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(123)), + stackitem.NewBigInteger(uint256.NewInt(123)), stackitem.NewByteArray([]byte{1, 2, 3}), stackitem.NewArray([]stackitem.Item{ stackitem.NewBool(true), diff --git a/pkg/util/bigint.go b/pkg/util/bigint.go new file mode 100644 index 0000000000..13c2d23245 --- /dev/null +++ b/pkg/util/bigint.go @@ -0,0 +1,38 @@ +package util + +import ( + "math" + "math/big" + + "github.com/holiman/uint256" +) + +var ( + minInt64 = new(uint256.Int).Neg(uint256.NewInt(math.MaxInt64 + 1)) + maxInt64 = new(uint256.Int).SetUint64(math.MaxInt64) +) + +func ToBig(x *uint256.Int) *big.Int { + if x.Sign() == 0 { + return big.NewInt(0) + } + if x.Sign() > 0 { + return x.ToBig() + } + b := new(uint256.Int).Neg(x).ToBig() + return b.Neg(b) +} + +func IsInt64(x *uint256.Int) bool { + return !(x.Sgt(maxInt64) || x.Slt(minInt64)) +} + +func ToInt64(x *uint256.Int) int64 { + var v int64 + if x.Sign() < 0 { + v -= int64(new(uint256.Int).Neg(x).Uint64()) + } else { + v = int64(x.Uint64()) + } + return int64(v) +} diff --git a/pkg/util/bigint_test.go b/pkg/util/bigint_test.go new file mode 100644 index 0000000000..f4011b2955 --- /dev/null +++ b/pkg/util/bigint_test.go @@ -0,0 +1,64 @@ +package util + +import ( + "math" + "math/big" + "testing" + + "github.com/holiman/uint256" + "github.com/stretchr/testify/assert" +) + +var testCases = []int64{ + 0, + 1, + -1, + 2, + -2, + 127, + -127, + 128, + -128, + 129, + -129, + 255, + -255, + 256, + -256, + 123456789, + -123456789, + -6777216, + 6777216, +} + +func TestToBig(t *testing.T) { + for _, tc := range testCases { + x, _ := uint256.FromBig(big.NewInt(tc)) + assert.Equal(t, big.NewInt(tc), ToBig(x)) + } +} + +func TestToInt64(t *testing.T) { + min, _ := uint256.FromBig(big.NewInt(math.MinInt64)) + max, _ := uint256.FromBig(big.NewInt(math.MaxInt64)) + x := ToInt64(min) + assert.Equal(t, int64(math.MinInt64), x) + x = ToInt64(max) + assert.Equal(t, int64(math.MaxInt64), x) + + v := uint256.NewInt(uint64(math.MaxInt64) + 1) + ok := IsInt64(v) + assert.False(t, ok) + v = new(uint256.Int).Neg(uint256.NewInt(uint64(math.MaxInt64) + 2)) + ok = IsInt64(v) + assert.False(t, ok) +} + +func BenchmarkToInt64_1(b *testing.B) { + min, _ := uint256.FromBig(big.NewInt(math.MinInt64)) + max, _ := uint256.FromBig(big.NewInt(math.MaxInt64)) + for i := 0; i < b.N; i++ { + ToInt64(min) + ToInt64(max) + } +} diff --git a/pkg/vm/emit/emit_test.go b/pkg/vm/emit/emit_test.go index a585161b48..5a034c8318 100644 --- a/pkg/vm/emit/emit_test.go +++ b/pkg/vm/emit/emit_test.go @@ -95,7 +95,7 @@ func TestEmitBigInt(t *testing.T) { bi.Sub(bi, big.NewInt(1)) // sanity check - require.NotPanics(t, func() { stackitem.NewBigInteger(bi) }) + require.NotPanics(t, func() { stackitem.NewBigIntegerFromBig(bi) }) BigInt(buf.BinWriter, bi) require.NoError(t, buf.Err) @@ -114,7 +114,7 @@ func TestEmitBigInt(t *testing.T) { bi.Lsh(bi, 255) // sanity check - require.NotPanics(t, func() { stackitem.NewBigInteger(bi) }) + require.NotPanics(t, func() { stackitem.NewBigIntegerFromBig(bi) }) BigInt(buf.BinWriter, bi) require.NoError(t, buf.Err) @@ -130,7 +130,7 @@ func TestEmitBigInt(t *testing.T) { bi.Lsh(bi, 255) // sanity check - require.Panics(t, func() { stackitem.NewBigInteger(bi) }) + require.Panics(t, func() { stackitem.NewBigIntegerFromBig(bi) }) BigInt(buf.BinWriter, bi) require.Error(t, buf.Err) @@ -150,7 +150,7 @@ func TestEmitBigInt(t *testing.T) { bi.Sub(bi, big.NewInt(1)) // sanity check - require.Panics(t, func() { stackitem.NewBigInteger(bi) }) + require.Panics(t, func() { stackitem.NewBigIntegerFromBig(bi) }) BigInt(buf.BinWriter, bi) require.Error(t, buf.Err) diff --git a/pkg/vm/exception.go b/pkg/vm/exception.go index 61c509931f..1003445a1c 100644 --- a/pkg/vm/exception.go +++ b/pkg/vm/exception.go @@ -2,8 +2,8 @@ package vm import ( "errors" - "math/big" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) @@ -65,7 +65,7 @@ func (c *exceptionHandlingContext) TryBytes() ([]byte, error) { } // TryInteger implements the stackitem.Item interface. -func (c *exceptionHandlingContext) TryInteger() (*big.Int, error) { +func (c *exceptionHandlingContext) TryInteger() (*uint256.Int, error) { return nil, errors.New("can't convert exceptionHandlingContext to Integer") } diff --git a/pkg/vm/json_test.go b/pkg/vm/json_test.go index ada80b4999..fbcb631f3a 100644 --- a/pkg/vm/json_test.go +++ b/pkg/vm/json_test.go @@ -14,6 +14,7 @@ import ( "strings" "testing" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" @@ -292,7 +293,7 @@ func (v *vmUTStackItem) toStackItem() stackitem.Item { case typeBoolean: return stackitem.NewBool(v.Value.(bool)) case typeInteger: - return stackitem.NewBigInteger(v.Value.(*big.Int)) + return stackitem.NewBigInteger(v.Value.(*uint256.Int)) case typeStruct: items := v.Value.([]vmUTStackItem) result := make([]stackitem.Item, len(items)) @@ -331,7 +332,7 @@ func execStep(t *testing.T, v *VM, step vmUTStep) { func jsonStringToInteger(s string) stackitem.Item { b, err := decodeHex(s) if err == nil { - return stackitem.NewBigInteger(new(big.Int).SetBytes(b)) + return stackitem.NewBigInteger(new(uint256.Int).SetBytes(b)) } return nil } diff --git a/pkg/vm/slot_test.go b/pkg/vm/slot_test.go index ec10a7ffc8..d39d5c6dc6 100644 --- a/pkg/vm/slot_test.go +++ b/pkg/vm/slot_test.go @@ -1,9 +1,9 @@ package vm import ( - "math/big" "testing" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/stretchr/testify/require" ) @@ -21,7 +21,7 @@ func TestSlot_Get(t *testing.T) { item := s.Get(2) require.Equal(t, stackitem.Null{}, item) - s.Set(1, stackitem.NewBigInteger(big.NewInt(42)), rc) - require.Equal(t, stackitem.NewBigInteger(big.NewInt(42)), s.Get(1)) + s.Set(1, stackitem.NewBigInteger(uint256.NewInt(42)), rc) + require.Equal(t, stackitem.NewBigInteger(uint256.NewInt(42)), s.Get(1)) require.Equal(t, 3, int(*rc)) } diff --git a/pkg/vm/stack.go b/pkg/vm/stack.go index 9e609766ee..a2ea215555 100644 --- a/pkg/vm/stack.go +++ b/pkg/vm/stack.go @@ -4,8 +4,9 @@ import ( "encoding/json" "errors" "fmt" - "math/big" + "github.com/holiman/uint256" + "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) @@ -38,7 +39,7 @@ func (e Element) Value() interface{} { // BigInt attempts to get the underlying value of the element as a big integer. // It will panic if the assertion has failed, which will be caught by the VM. -func (e Element) BigInt() *big.Int { +func (e Element) BigInt() *uint256.Int { val, err := e.value.TryInteger() if err != nil { panic(err) @@ -343,7 +344,10 @@ func (s *Stack) PopSigElements() ([][]byte, error) { elems[k] = b } default: - num = int(item.BigInt().Int64()) + if !util.IsInt64(item.BigInt()) { + return nil, fmt.Errorf("bad element count %s", item.value.String()) + } + num = int(util.ToInt64(item.BigInt())) if num < 1 || num > s.Len() { return nil, fmt.Errorf("wrong number of elements: %d", num) } diff --git a/pkg/vm/stack_test.go b/pkg/vm/stack_test.go index e596a5eefa..b192a12d12 100644 --- a/pkg/vm/stack_test.go +++ b/pkg/vm/stack_test.go @@ -4,6 +4,7 @@ import ( "math/big" "testing" + "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -215,7 +216,7 @@ func TestPushVal(t *testing.T) { // integer s.PushVal(2) elem := s.Pop() - assert.Equal(t, int64(2), elem.BigInt().Int64()) + assert.Equal(t, int64(2), util.ToInt64(elem.BigInt())) // byteArray s.PushVal([]byte("foo")) @@ -256,8 +257,8 @@ func TestSwapElemValues(t *testing.T) { s.PushVal(4) assert.NoError(t, s.Swap(0, 1)) - assert.Equal(t, int64(2), s.Pop().BigInt().Int64()) - assert.Equal(t, int64(4), s.Pop().BigInt().Int64()) + assert.Equal(t, int64(2), util.ToInt64(s.Pop().BigInt())) + assert.Equal(t, int64(4), util.ToInt64(s.Pop().BigInt())) s.PushVal(1) s.PushVal(2) @@ -265,10 +266,10 @@ func TestSwapElemValues(t *testing.T) { s.PushVal(4) assert.NoError(t, s.Swap(1, 3)) - assert.Equal(t, int64(4), s.Pop().BigInt().Int64()) - assert.Equal(t, int64(1), s.Pop().BigInt().Int64()) - assert.Equal(t, int64(2), s.Pop().BigInt().Int64()) - assert.Equal(t, int64(3), s.Pop().BigInt().Int64()) + assert.Equal(t, int64(4), util.ToInt64(s.Pop().BigInt())) + assert.Equal(t, int64(1), util.ToInt64(s.Pop().BigInt())) + assert.Equal(t, int64(2), util.ToInt64(s.Pop().BigInt())) + assert.Equal(t, int64(3), util.ToInt64(s.Pop().BigInt())) s.PushVal(1) s.PushVal(2) @@ -281,10 +282,10 @@ func TestSwapElemValues(t *testing.T) { assert.Error(t, s.Swap(5, 0)) assert.NoError(t, s.Swap(1, 1)) - assert.Equal(t, int64(4), s.Pop().BigInt().Int64()) - assert.Equal(t, int64(3), s.Pop().BigInt().Int64()) - assert.Equal(t, int64(2), s.Pop().BigInt().Int64()) - assert.Equal(t, int64(1), s.Pop().BigInt().Int64()) + assert.Equal(t, int64(4), util.ToInt64(s.Pop().BigInt())) + assert.Equal(t, int64(3), util.ToInt64(s.Pop().BigInt())) + assert.Equal(t, int64(2), util.ToInt64(s.Pop().BigInt())) + assert.Equal(t, int64(1), util.ToInt64(s.Pop().BigInt())) } func TestRoll(t *testing.T) { @@ -296,10 +297,10 @@ func TestRoll(t *testing.T) { s.PushVal(4) assert.NoError(t, s.Roll(2)) - assert.Equal(t, int64(2), s.Pop().BigInt().Int64()) - assert.Equal(t, int64(4), s.Pop().BigInt().Int64()) - assert.Equal(t, int64(3), s.Pop().BigInt().Int64()) - assert.Equal(t, int64(1), s.Pop().BigInt().Int64()) + assert.Equal(t, int64(2), util.ToInt64(s.Pop().BigInt())) + assert.Equal(t, int64(4), util.ToInt64(s.Pop().BigInt())) + assert.Equal(t, int64(3), util.ToInt64(s.Pop().BigInt())) + assert.Equal(t, int64(1), util.ToInt64(s.Pop().BigInt())) s.PushVal(1) s.PushVal(2) @@ -307,10 +308,10 @@ func TestRoll(t *testing.T) { s.PushVal(4) assert.NoError(t, s.Roll(3)) - assert.Equal(t, int64(1), s.Pop().BigInt().Int64()) - assert.Equal(t, int64(4), s.Pop().BigInt().Int64()) - assert.Equal(t, int64(3), s.Pop().BigInt().Int64()) - assert.Equal(t, int64(2), s.Pop().BigInt().Int64()) + assert.Equal(t, int64(1), util.ToInt64(s.Pop().BigInt())) + assert.Equal(t, int64(4), util.ToInt64(s.Pop().BigInt())) + assert.Equal(t, int64(3), util.ToInt64(s.Pop().BigInt())) + assert.Equal(t, int64(2), util.ToInt64(s.Pop().BigInt())) s.PushVal(1) s.PushVal(2) @@ -321,10 +322,10 @@ func TestRoll(t *testing.T) { assert.Error(t, s.Roll(4)) assert.NoError(t, s.Roll(0)) - assert.Equal(t, int64(4), s.Pop().BigInt().Int64()) - assert.Equal(t, int64(3), s.Pop().BigInt().Int64()) - assert.Equal(t, int64(2), s.Pop().BigInt().Int64()) - assert.Equal(t, int64(1), s.Pop().BigInt().Int64()) + assert.Equal(t, int64(4), util.ToInt64(s.Pop().BigInt())) + assert.Equal(t, int64(3), util.ToInt64(s.Pop().BigInt())) + assert.Equal(t, int64(2), util.ToInt64(s.Pop().BigInt())) + assert.Equal(t, int64(1), util.ToInt64(s.Pop().BigInt())) } func TestInsertAt(t *testing.T) { @@ -338,12 +339,12 @@ func TestInsertAt(t *testing.T) { e := s.Dup(1) // it's `4` s.InsertAt(e, 3) - assert.Equal(t, int64(5), s.Peek(0).BigInt().Int64()) - assert.Equal(t, int64(4), s.Peek(1).BigInt().Int64()) - assert.Equal(t, int64(3), s.Peek(2).BigInt().Int64()) - assert.Equal(t, int64(4), s.Peek(3).BigInt().Int64()) - assert.Equal(t, int64(2), s.Peek(4).BigInt().Int64()) - assert.Equal(t, int64(1), s.Peek(5).BigInt().Int64()) + assert.Equal(t, int64(5), util.ToInt64(s.Peek(0).BigInt())) + assert.Equal(t, int64(4), util.ToInt64(s.Peek(1).BigInt())) + assert.Equal(t, int64(3), util.ToInt64(s.Peek(2).BigInt())) + assert.Equal(t, int64(4), util.ToInt64(s.Peek(3).BigInt())) + assert.Equal(t, int64(2), util.ToInt64(s.Peek(4).BigInt())) + assert.Equal(t, int64(1), util.ToInt64(s.Peek(5).BigInt())) } func TestPopSigElements(t *testing.T) { diff --git a/pkg/vm/stackitem/item.go b/pkg/vm/stackitem/item.go index 6e645bf280..dc6e165aa9 100644 --- a/pkg/vm/stackitem/item.go +++ b/pkg/vm/stackitem/item.go @@ -11,6 +11,7 @@ import ( "reflect" "unicode/utf8" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "github.com/nspcc-dev/neo-go/pkg/util" @@ -45,7 +46,7 @@ type Item interface { // byte slice, it's returned as is without copying. TryBytes() ([]byte, error) // TryInteger converts Item to an integer. - TryInteger() (*big.Int, error) + TryInteger() (*uint256.Int, error) // Equals checks if 2 StackItems are equal. Equals(s Item) bool // Type returns stack item type. @@ -91,17 +92,19 @@ func mkInvConversion(from Item, to Type) error { func Make(v interface{}) Item { switch val := v.(type) { case int: - return (*BigInteger)(big.NewInt(int64(val))) + b, _ := uint256.FromBig(big.NewInt(int64(val))) + return NewBigInteger(b) case int64: - return (*BigInteger)(big.NewInt(val)) + b, _ := uint256.FromBig(big.NewInt(val)) + return NewBigInteger(b) case uint8: - return (*BigInteger)(big.NewInt(int64(val))) + return NewBigInteger(uint256.NewInt(uint64(val))) case uint16: - return (*BigInteger)(big.NewInt(int64(val))) + return NewBigInteger(uint256.NewInt(uint64(val))) case uint32: - return (*BigInteger)(big.NewInt(int64(val))) + return NewBigInteger(uint256.NewInt(uint64(val))) case uint64: - return (*BigInteger)(new(big.Int).SetUint64(val)) + return NewBigInteger(uint256.NewInt(uint64(val))) case []byte: return NewByteArray(val) case string: @@ -113,7 +116,14 @@ func Make(v interface{}) Item { value: val, } case *big.Int: - return NewBigInteger(val) + b, overflow := uint256.FromBig(val) + if overflow { + panic(fmt.Sprintf( + "big int overflow: %v", + val, + )) + } + return NewBigInteger(b) case Item: return val case []int: @@ -173,7 +183,7 @@ func convertPrimitive(item Item, typ Type) (Item, error) { if err != nil { return nil, err } - return NewBigInteger(bi), nil + return (*BigInteger)(bi), nil case ByteArrayT, BufferT: b, err := item.TryBytes() if err != nil { @@ -264,7 +274,7 @@ func (i *Struct) TryBytes() ([]byte, error) { } // TryInteger implements the Item interface. -func (i *Struct) TryInteger() (*big.Int, error) { +func (i *Struct) TryInteger() (*uint256.Int, error) { return nil, mkInvConversion(i, IntegerT) } @@ -394,7 +404,7 @@ func (i Null) TryBytes() ([]byte, error) { } // TryInteger implements the Item interface. -func (i Null) TryInteger() (*big.Int, error) { +func (i Null) TryInteger() (*uint256.Int, error) { return nil, mkInvConversion(i, IntegerT) } @@ -416,16 +426,25 @@ func (i Null) Convert(typ Type) (Item, error) { } // BigInteger represents a big integer on the stack. -type BigInteger big.Int +type BigInteger uint256.Int // NewBigInteger returns an new BigInteger object. -func NewBigInteger(value *big.Int) *BigInteger { - if err := CheckIntegerSize(value); err != nil { - panic(err) - } +func NewBigInteger(value *uint256.Int) *BigInteger { return (*BigInteger)(value) } +func NewBigIntegerFromInt64(value int64) *BigInteger { + return NewBigIntegerFromBig(big.NewInt(value)) +} + +func NewBigIntegerFromBig(value *big.Int) *BigInteger { + b, overflow := uint256.FromBig(value) + if overflow { + panic("BigInter overflow") + } + return (*BigInteger)(b) +} + // CheckIntegerSize checks that the value size doesn't exceed the VM limit for Interer. func CheckIntegerSize(value *big.Int) error { // There are 2 cases when `BitLen` differs from the actual size: @@ -447,13 +466,13 @@ func CheckIntegerSize(value *big.Int) error { } // Big casts i to the big.Int type. -func (i *BigInteger) Big() *big.Int { - return (*big.Int)(i) +func (i *BigInteger) Big() *uint256.Int { + return (*uint256.Int)(i) } // Bytes converts i to a slice of bytes. func (i *BigInteger) Bytes() []byte { - return bigint.ToBytes(i.Big()) + return bigint.Uint256ToBytes(i.Big()) } // TryBool implements the Item interface. @@ -467,7 +486,7 @@ func (i *BigInteger) TryBytes() ([]byte, error) { } // TryInteger implements the Item interface. -func (i *BigInteger) TryInteger() (*big.Int, error) { +func (i *BigInteger) TryInteger() (*uint256.Int, error) { return i.Big(), nil } @@ -493,8 +512,8 @@ func (i *BigInteger) String() string { // Dup implements the Item interface. func (i *BigInteger) Dup() Item { - n := new(big.Int) - return (*BigInteger)(n.Set(i.Big())) + n := new(uint256.Int) + return (*BigInteger)(n.Set((*uint256.Int)(i))) } // Type implements the Item interface. @@ -554,11 +573,11 @@ func (i Bool) TryBytes() ([]byte, error) { } // TryInteger implements the Item interface. -func (i Bool) TryInteger() (*big.Int, error) { +func (i Bool) TryInteger() (*uint256.Int, error) { if i { - return big.NewInt(1), nil + return uint256.NewInt(1), nil } - return big.NewInt(0), nil + return uint256.NewInt(0), nil } // Equals implements the Item interface. @@ -621,11 +640,11 @@ func (i ByteArray) TryBytes() ([]byte, error) { } // TryInteger implements the Item interface. -func (i ByteArray) TryInteger() (*big.Int, error) { +func (i ByteArray) TryInteger() (*uint256.Int, error) { if len(i) > MaxBigIntegerSizeBits/8 { return nil, errTooBigInteger } - return bigint.FromBytes(i), nil + return uint256.NewInt(0).SetBytes(i), nil } // Equals implements the Item interface. @@ -749,7 +768,7 @@ func (i *Array) TryBytes() ([]byte, error) { } // TryInteger implements the Item interface. -func (i *Array) TryInteger() (*big.Int, error) { +func (i *Array) TryInteger() (*uint256.Int, error) { return nil, mkInvConversion(i, IntegerT) } @@ -844,7 +863,7 @@ func (i *Map) TryBytes() ([]byte, error) { } // TryInteger implements the Item interface. -func (i *Map) TryInteger() (*big.Int, error) { +func (i *Map) TryInteger() (*uint256.Int, error) { return nil, mkInvConversion(i, IntegerT) } @@ -972,7 +991,7 @@ func (i *Interop) TryBytes() ([]byte, error) { } // TryInteger implements the Item interface. -func (i *Interop) TryInteger() (*big.Int, error) { +func (i *Interop) TryInteger() (*uint256.Int, error) { return nil, mkInvConversion(i, IntegerT) } @@ -1065,7 +1084,7 @@ func (p *Pointer) TryBytes() ([]byte, error) { } // TryInteger implements the Item interface. -func (p *Pointer) TryInteger() (*big.Int, error) { +func (p *Pointer) TryInteger() (*uint256.Int, error) { return nil, mkInvConversion(p, IntegerT) } @@ -1134,7 +1153,7 @@ func (i *Buffer) TryBytes() ([]byte, error) { } // TryInteger implements the Item interface. -func (i *Buffer) TryInteger() (*big.Int, error) { +func (i *Buffer) TryInteger() (*uint256.Int, error) { return nil, mkInvConversion(i, IntegerT) } @@ -1169,7 +1188,7 @@ func (i *Buffer) Convert(typ Type) (Item, error) { if len(*i) > MaxBigIntegerSizeBits/8 { return nil, errTooBigInteger } - return NewBigInteger(bigint.FromBytes(*i)), nil + return NewBigInteger(bigint.Uint256FromBytes(*i)), nil default: return nil, mkInvConversion(i, typ) } @@ -1223,7 +1242,7 @@ func deepCopy(item Item, seen map[Item]Item, asImmutable bool) Item { m.MarkAsReadOnly() return m case *BigInteger: - bi := new(big.Int).Set(it.Big()) + bi := new(uint256.Int).Set(it.Big()) return (*BigInteger)(bi) case *ByteArray: return NewByteArray(slice.Copy(*it)) diff --git a/pkg/vm/stackitem/item_test.go b/pkg/vm/stackitem/item_test.go index 210f0a83d1..d0c3c831f3 100644 --- a/pkg/vm/stackitem/item_test.go +++ b/pkg/vm/stackitem/item_test.go @@ -4,6 +4,7 @@ import ( "math/big" "testing" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -15,35 +16,35 @@ var makeStackItemTestCases = []struct { }{ { input: int64(3), - result: (*BigInteger)(big.NewInt(3)), + result: (*BigInteger)(uint256.NewInt(3)), }, { input: int16(3), - result: (*BigInteger)(big.NewInt(3)), + result: (*BigInteger)(uint256.NewInt(3)), }, { input: 3, - result: (*BigInteger)(big.NewInt(3)), + result: (*BigInteger)(uint256.NewInt(3)), }, { input: uint8(3), - result: (*BigInteger)(big.NewInt(3)), + result: (*BigInteger)(uint256.NewInt(3)), }, { input: uint16(3), - result: (*BigInteger)(big.NewInt(3)), + result: (*BigInteger)(uint256.NewInt(3)), }, { input: uint32(3), - result: (*BigInteger)(big.NewInt(3)), + result: (*BigInteger)(uint256.NewInt(3)), }, { input: uint64(3), - result: (*BigInteger)(big.NewInt(3)), + result: (*BigInteger)(uint256.NewInt(3)), }, { input: big.NewInt(3), - result: (*BigInteger)(big.NewInt(3)), + result: (*BigInteger)(uint256.NewInt(3)), }, { input: []byte{1, 2, 3, 4}, @@ -70,12 +71,12 @@ var makeStackItemTestCases = []struct { result: Bool(false), }, { - input: []Item{(*BigInteger)(big.NewInt(3)), NewByteArray([]byte{1, 2, 3})}, - result: &Array{value: []Item{(*BigInteger)(big.NewInt(3)), NewByteArray([]byte{1, 2, 3})}}, + input: []Item{(*BigInteger)(uint256.NewInt(3)), NewByteArray([]byte{1, 2, 3})}, + result: &Array{value: []Item{(*BigInteger)(uint256.NewInt(3)), NewByteArray([]byte{1, 2, 3})}}, }, { input: []int{1, 2, 3}, - result: &Array{value: []Item{(*BigInteger)(big.NewInt(1)), (*BigInteger)(big.NewInt(2)), (*BigInteger)(big.NewInt(3))}}, + result: &Array{value: []Item{(*BigInteger)(uint256.NewInt(1)), (*BigInteger)(uint256.NewInt(2)), (*BigInteger)(uint256.NewInt(3))}}, }, { input: nil, @@ -109,7 +110,7 @@ var stringerTestCases = []struct { result: "Struct", }, { - input: NewBigInteger(big.NewInt(3)), + input: NewBigInteger(uint256.NewInt(3)), result: "BigInteger", }, { @@ -158,53 +159,53 @@ var equalsTestCases = map[string][]struct { }, { item1: NewStruct(nil), - item2: NewBigInteger(big.NewInt(1)), + item2: NewBigInteger(uint256.NewInt(1)), result: false, }, { item1: NewStruct(nil), - item2: NewStruct([]Item{NewBigInteger(big.NewInt(1))}), + item2: NewStruct([]Item{NewBigInteger(uint256.NewInt(1))}), result: false, }, { - item1: NewStruct([]Item{NewBigInteger(big.NewInt(1))}), - item2: NewStruct([]Item{NewBigInteger(big.NewInt(2))}), + item1: NewStruct([]Item{NewBigInteger(uint256.NewInt(1))}), + item2: NewStruct([]Item{NewBigInteger(uint256.NewInt(2))}), result: false, }, { - item1: NewStruct([]Item{NewBigInteger(big.NewInt(1))}), - item2: NewStruct([]Item{NewBigInteger(big.NewInt(1))}), + item1: NewStruct([]Item{NewBigInteger(uint256.NewInt(1))}), + item2: NewStruct([]Item{NewBigInteger(uint256.NewInt(1))}), result: true, }, { - item1: NewStruct([]Item{NewBigInteger(big.NewInt(1)), NewStruct([]Item{})}), - item2: NewStruct([]Item{NewBigInteger(big.NewInt(1)), NewStruct([]Item{})}), + item1: NewStruct([]Item{NewBigInteger(uint256.NewInt(1)), NewStruct([]Item{})}), + item2: NewStruct([]Item{NewBigInteger(uint256.NewInt(1)), NewStruct([]Item{})}), result: true, }, }, "bigint": { { - item1: NewBigInteger(big.NewInt(2)), + item1: NewBigInteger(uint256.NewInt(2)), item2: nil, result: false, }, { - item1: NewBigInteger(big.NewInt(2)), - item2: NewBigInteger(big.NewInt(2)), + item1: NewBigInteger(uint256.NewInt(2)), + item2: NewBigInteger(uint256.NewInt(2)), result: true, }, { - item1: NewBigInteger(big.NewInt(2)), + item1: NewBigInteger(uint256.NewInt(2)), item2: NewBool(false), result: false, }, { - item1: NewBigInteger(big.NewInt(0)), + item1: NewBigInteger(uint256.NewInt(0)), item2: NewBool(false), result: false, }, { - item1: NewBigInteger(big.NewInt(2)), + item1: NewBigInteger(uint256.NewInt(2)), item2: Make(int32(2)), result: true, }, @@ -222,7 +223,7 @@ var equalsTestCases = map[string][]struct { }, { item1: NewBool(true), - item2: NewBigInteger(big.NewInt(1)), + item2: NewBigInteger(uint256.NewInt(1)), result: false, }, { @@ -249,7 +250,7 @@ var equalsTestCases = map[string][]struct { }, { item1: NewByteArray([]byte{1}), - item2: NewBigInteger(big.NewInt(1)), + item2: NewBigInteger(uint256.NewInt(1)), result: false, }, { @@ -285,18 +286,18 @@ var equalsTestCases = map[string][]struct { result: false, }, { - item1: NewArray([]Item{(*BigInteger)(big.NewInt(1)), (*BigInteger)(big.NewInt(2)), (*BigInteger)(big.NewInt(3))}), - item2: NewArray([]Item{(*BigInteger)(big.NewInt(1)), (*BigInteger)(big.NewInt(2)), (*BigInteger)(big.NewInt(3))}), + item1: NewArray([]Item{(*BigInteger)(uint256.NewInt(1)), (*BigInteger)(uint256.NewInt(2)), (*BigInteger)(uint256.NewInt(3))}), + item2: NewArray([]Item{(*BigInteger)(uint256.NewInt(1)), (*BigInteger)(uint256.NewInt(2)), (*BigInteger)(uint256.NewInt(3))}), result: false, }, { - item1: NewArray([]Item{(*BigInteger)(big.NewInt(1))}), - item2: NewBigInteger(big.NewInt(1)), + item1: NewArray([]Item{(*BigInteger)(uint256.NewInt(1))}), + item2: NewBigInteger(uint256.NewInt(1)), result: false, }, { - item1: NewArray([]Item{(*BigInteger)(big.NewInt(1)), (*BigInteger)(big.NewInt(2)), (*BigInteger)(big.NewInt(3))}), - item2: NewArray([]Item{(*BigInteger)(big.NewInt(1)), (*BigInteger)(big.NewInt(2)), (*BigInteger)(big.NewInt(4))}), + item1: NewArray([]Item{(*BigInteger)(uint256.NewInt(1)), (*BigInteger)(uint256.NewInt(2)), (*BigInteger)(uint256.NewInt(3))}), + item2: NewArray([]Item{(*BigInteger)(uint256.NewInt(1)), (*BigInteger)(uint256.NewInt(2)), (*BigInteger)(uint256.NewInt(4))}), result: false, }, }, @@ -312,13 +313,13 @@ var equalsTestCases = map[string][]struct { result: false, }, { - item1: &Map{value: []MapElement{{NewByteArray([]byte("first")), NewBigInteger(big.NewInt(1))}, {NewBool(true), NewByteArray([]byte{2})}}}, - item2: &Map{value: []MapElement{{NewByteArray([]byte("first")), NewBigInteger(big.NewInt(1))}, {NewBool(true), NewByteArray([]byte{2})}}}, + item1: &Map{value: []MapElement{{NewByteArray([]byte("first")), NewBigInteger(uint256.NewInt(1))}, {NewBool(true), NewByteArray([]byte{2})}}}, + item2: &Map{value: []MapElement{{NewByteArray([]byte("first")), NewBigInteger(uint256.NewInt(1))}, {NewBool(true), NewByteArray([]byte{2})}}}, result: false, }, { - item1: &Map{value: []MapElement{{NewByteArray([]byte("first")), NewBigInteger(big.NewInt(1))}, {NewBool(true), NewByteArray([]byte{2})}}}, - item2: &Map{value: []MapElement{{NewByteArray([]byte("first")), NewBigInteger(big.NewInt(1))}, {NewBool(true), NewByteArray([]byte{3})}}}, + item1: &Map{value: []MapElement{{NewByteArray([]byte("first")), NewBigInteger(uint256.NewInt(1))}, {NewBool(true), NewByteArray([]byte{2})}}}, + item2: &Map{value: []MapElement{{NewByteArray([]byte("first")), NewBigInteger(uint256.NewInt(1))}, {NewBool(true), NewByteArray([]byte{3})}}}, result: false, }, }, @@ -366,7 +367,7 @@ var equalsTestCases = map[string][]struct { }, { item1: NewPointer(0, []byte{}), - item2: NewBigInteger(big.NewInt(0)), + item2: NewBigInteger(uint256.NewInt(0)), result: false, }, }, @@ -429,7 +430,7 @@ var marshalJSONTestCases = []struct { result []byte }{ { - input: NewBigInteger(big.NewInt(2)), + input: NewBigInteger(uint256.NewInt(2)), result: []byte(`2`), }, { @@ -445,7 +446,7 @@ var marshalJSONTestCases = []struct { result: []byte(`"010203"`), }, { - input: &Array{value: []Item{(*BigInteger)(big.NewInt(3)), NewByteArray([]byte{1, 2, 3})}}, + input: &Array{value: []Item{(*BigInteger)(uint256.NewInt(3)), NewByteArray([]byte{1, 2, 3})}}, result: []byte(`[3,"010203"]`), }, { @@ -487,7 +488,7 @@ func TestNewVeryBigInteger(t *testing.T) { assert.True(t, len(bs)*8 <= MaxBigIntegerSizeBits) } else { assert.True(t, len(bs)*8 > MaxBigIntegerSizeBits) - assert.Panics(t, func() { NewBigInteger(v) }) + assert.Panics(t, func() { NewBigIntegerFromBig(v) }) } } @@ -525,7 +526,7 @@ func TestDeepCopy(t *testing.T) { name string item Item }{ - {"Integer", NewBigInteger(big.NewInt(1))}, + {"Integer", NewBigInteger(uint256.NewInt(1))}, {"ByteArray", NewByteArray([]byte{1, 2, 3})}, {"Buffer", NewBuffer([]byte{1, 2, 3})}, {"Bool", NewBool(true)}, @@ -576,7 +577,7 @@ func TestDeepCopy(t *testing.T) { t.Run("Map", func(t *testing.T) { m := NewMapWithValue(make([]MapElement, 2)) m.value[0] = MapElement{Key: NewBool(true), Value: m} - m.value[1] = MapElement{Key: NewBigInteger(big.NewInt(1)), Value: NewByteArray([]byte{1, 2, 3})} + m.value[1] = MapElement{Key: NewBigInteger(uint256.NewInt(1)), Value: NewByteArray([]byte{1, 2, 3})} actual := DeepCopy(m, false) m.isReadOnly = true // tiny hack for test to be able to compare object references. diff --git a/pkg/vm/stackitem/json.go b/pkg/vm/stackitem/json.go index 10902e62c0..883629384a 100644 --- a/pkg/vm/stackitem/json.go +++ b/pkg/vm/stackitem/json.go @@ -10,6 +10,8 @@ import ( "math/big" "strconv" "strings" + + "github.com/holiman/uint256" ) // decoder is a wrapper around json.Decoder helping to mimic C# json decoder behavior. @@ -112,7 +114,7 @@ func toJSON(data []byte, seen map[Item]sliceNoPointer, item Item) ([]byte, error data = append(data, '}') seen[item] = sliceNoPointer{start, len(data)} case *BigInteger: - if it.Big().CmpAbs(big.NewInt(MaxAllowedInteger)) == 1 { + if uint256.NewInt(0).Abs(it.Big()).Cmp(uint256.NewInt(MaxAllowedInteger)) == 1 { return nil, fmt.Errorf("%w (MaxAllowedInteger)", ErrInvalidValue) } data = append(data, it.Big().String()...) @@ -228,7 +230,7 @@ func (d *decoder) decode() (Item, error) { if !ok { return nil, fmt.Errorf("%w (integer)", ErrInvalidValue) } - return NewBigInteger(num), nil + return NewBigIntegerFromBig(num), nil case bool: return NewBool(t), nil default: @@ -443,7 +445,7 @@ func FromJSONWithTypes(data []byte) (Item, error) { if !ok { return nil, mkErrValue(errors.New("not an integer")) } - return NewBigInteger(val), nil + return NewBigIntegerFromBig(val), nil case ByteArrayT, BufferT: var s string if err := json.Unmarshal(raw.Value, &s); err != nil { diff --git a/pkg/vm/stackitem/json_test.go b/pkg/vm/stackitem/json_test.go index c68db70f03..594ac38f44 100644 --- a/pkg/vm/stackitem/json_test.go +++ b/pkg/vm/stackitem/json_test.go @@ -2,9 +2,9 @@ package stackitem import ( "errors" - "math/big" "testing" + "github.com/holiman/uint256" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -47,7 +47,7 @@ func TestFromToJSON(t *testing.T) { t.Run("Array", func(t *testing.T) { t.Run("Empty", getTestDecodeFunc(`[]`, NewArray([]Item{}))) t.Run("Simple", getTestDecodeFunc((`[1,"test",true,null]`), - NewArray([]Item{NewBigInteger(big.NewInt(1)), NewByteArray([]byte("test")), NewBool(true), Null{}}))) + NewArray([]Item{NewBigInteger(uint256.NewInt(1)), NewByteArray([]byte("test")), NewBool(true), Null{}}))) t.Run("Nested", getTestDecodeFunc(`[[],[{},null]]`, NewArray([]Item{NewArray([]Item{}), NewArray([]Item{NewMap(), Null{}})}))) t.Run("ManyElements", func(t *testing.T) { @@ -61,7 +61,7 @@ func TestFromToJSON(t *testing.T) { }) t.Run("Map", func(t *testing.T) { small := NewMap() - small.Add(NewByteArray([]byte("a")), NewBigInteger(big.NewInt(3))) + small.Add(NewByteArray([]byte("a")), NewBigInteger(uint256.NewInt(3))) large := NewMap() large.Add(NewByteArray([]byte("3")), small) large.Add(NewByteArray([]byte("arr")), NewArray([]Item{NewByteArray([]byte("test"))})) @@ -89,7 +89,7 @@ func TestFromToJSON(t *testing.T) { t.Run("InvalidMapValue", getTestDecodeFunc(`{"a":{]}`, nil)) t.Run("AfterArray", getTestDecodeFunc(`[]XX`, nil)) t.Run("EncodeBigInteger", func(t *testing.T) { - item := NewBigInteger(big.NewInt(MaxAllowedInteger + 1)) + item := NewBigInteger(uint256.NewInt(MaxAllowedInteger + 1)) _, err := ToJSON(item) require.Error(t, err) }) @@ -224,14 +224,14 @@ func TestToJSONWithTypes(t *testing.T) { result string }{ {"Null", Null{}, `{"type":"Any"}`}, - {"Integer", NewBigInteger(big.NewInt(42)), `{"type":"Integer","value":"42"}`}, + {"Integer", NewBigInteger(uint256.NewInt(42)), `{"type":"Integer","value":"42"}`}, {"ByteString", NewByteArray([]byte{1, 2, 3}), `{"type":"ByteString","value":"AQID"}`}, {"Buffer", NewBuffer([]byte{1, 2, 3}), `{"type":"Buffer","value":"AQID"}`}, {"BoolTrue", NewBool(true), `{"type":"Boolean","value":true}`}, {"BoolFalse", NewBool(false), `{"type":"Boolean","value":false}`}, {"Struct", NewStruct([]Item{Make(11)}), `{"type":"Struct","value":[{"type":"Integer","value":"11"}]}`}, - {"Map", NewMapWithValue([]MapElement{{Key: NewBigInteger(big.NewInt(42)), Value: NewBool(false)}}), + {"Map", NewMapWithValue([]MapElement{{Key: NewBigInteger(uint256.NewInt(42)), Value: NewBool(false)}}), `{"type":"Map","value":[{"key":{"type":"Integer","value":"42"},` + `"value":{"type":"Boolean","value":false}}]}`}, {"Interop", NewInterop(nil), @@ -337,7 +337,7 @@ func TestToJSONWithTypesBadCases(t *testing.T) { // until the necessary branch is covered #ididthemath. arr := NewArray([]Item{ NewByteArray(bigBuf[:MaxSize/4*3-70]), - NewBigInteger(big.NewInt(1234)), + NewBigInteger(uint256.NewInt(1234)), }) _, err := ToJSONWithTypes(arr) require.True(t, errors.Is(err, errTooBigSize), "got: %v", err) diff --git a/pkg/vm/stackitem/serialization.go b/pkg/vm/stackitem/serialization.go index baab7fcd68..bda46a446e 100644 --- a/pkg/vm/stackitem/serialization.go +++ b/pkg/vm/stackitem/serialization.go @@ -3,7 +3,6 @@ package stackitem import ( "errors" "fmt" - "math/big" "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "github.com/nspcc-dev/neo-go/pkg/io" @@ -160,7 +159,7 @@ func (w *SerializationContext) serialize(item Item) error { w.data = append(w.data, byte(IntegerT)) ln := len(w.data) w.data = append(w.data, 0) - data := bigint.ToPreallocatedBytes((*big.Int)(t), w.data[len(w.data):]) + data := bigint.Uint256ToBytes(t.Big()) w.data[ln] = byte(len(data)) w.data = append(w.data, data...) case *Interop: @@ -300,7 +299,7 @@ func (r *deserContext) decodeBinary() Item { return NewBool(b) case IntegerT: data := r.ReadVarBytes(bigint.MaxBytesLen) - num := bigint.FromBytes(data) + num := bigint.Uint256FromBytes(data) return NewBigInteger(num) case ArrayT, StructT: size := int(r.ReadVarUint()) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 950dfed7ed..23f656b2bd 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -13,6 +13,7 @@ import ( "text/tabwriter" "unicode/utf8" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" @@ -92,10 +93,12 @@ type VM struct { } var ( - bigMinusOne = big.NewInt(-1) - bigZero = big.NewInt(0) - bigOne = big.NewInt(1) - bigTwo = big.NewInt(2) + bigMinusOne = new(uint256.Int).Neg(uint256.NewInt(1)) + bigZero = uint256.NewInt(0) + bigOne = uint256.NewInt(1) + bigTwo = uint256.NewInt(2) + minInt32, _ = uint256.FromBig(big.NewInt(math.MinInt32)) + maxInt32, _ = uint256.FromBig(big.NewInt(math.MaxInt32)) ) // New returns a new VM object ready to load AVM bytecode scripts. @@ -596,7 +599,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro } if op <= opcode.PUSHINT256 { - v.estack.PushItem(stackitem.NewBigInteger(bigint.FromBytes(parameter))) + v.estack.PushItem(stackitem.NewBigInteger(bigint.Uint256FromBytes(parameter))) return } @@ -607,7 +610,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro opcode.PUSH12, opcode.PUSH13, opcode.PUSH14, opcode.PUSH15, opcode.PUSH16: val := int(op) - int(opcode.PUSH0) - v.estack.PushItem(stackitem.NewBigInteger(big.NewInt(int64(val)))) + v.estack.PushItem(stackitem.NewBigInteger(uint256.NewInt(uint64(val)))) case opcode.PUSHDATA1, opcode.PUSHDATA2, opcode.PUSHDATA4: v.estack.PushItem(stackitem.NewByteArray(parameter)) @@ -796,7 +799,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro v.estack.PushItem(stackitem.NewBuffer(res)) case opcode.DEPTH: - v.estack.PushItem(stackitem.NewBigInteger(big.NewInt(int64(v.estack.Len())))) + v.estack.PushItem(stackitem.NewBigInteger(uint256.NewInt(uint64(v.estack.Len())))) case opcode.DROP: if v.estack.Len() < 1 { @@ -885,22 +888,22 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro // Bit operations. case opcode.INVERT: i := v.estack.Pop().BigInt() - v.estack.PushItem(stackitem.NewBigInteger(new(big.Int).Not(i))) + v.estack.PushItem(stackitem.NewBigInteger(new(uint256.Int).Not(i))) case opcode.AND: b := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt() - v.estack.PushItem(stackitem.NewBigInteger(new(big.Int).And(b, a))) + v.estack.PushItem(stackitem.NewBigInteger(new(uint256.Int).And(b, a))) case opcode.OR: b := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt() - v.estack.PushItem(stackitem.NewBigInteger(new(big.Int).Or(b, a))) + v.estack.PushItem(stackitem.NewBigInteger(new(uint256.Int).Or(b, a))) case opcode.XOR: b := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt() - v.estack.PushItem(stackitem.NewBigInteger(new(big.Int).Xor(b, a))) + v.estack.PushItem(stackitem.NewBigInteger(new(uint256.Int).Xor(b, a))) case opcode.EQUAL, opcode.NOTEQUAL: if v.estack.Len() < 2 { @@ -914,58 +917,58 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro // Numeric operations. case opcode.SIGN: x := v.estack.Pop().BigInt() - v.estack.PushItem(stackitem.NewBigInteger(big.NewInt(int64(x.Sign())))) + v.estack.PushItem(stackitem.NewBigIntegerFromInt64(int64(x.Sign()))) case opcode.ABS: x := v.estack.Pop().BigInt() - v.estack.PushItem(stackitem.NewBigInteger(new(big.Int).Abs(x))) + v.estack.PushItem(stackitem.NewBigInteger(new(uint256.Int).Abs(x))) case opcode.NEGATE: x := v.estack.Pop().BigInt() - v.estack.PushItem(stackitem.NewBigInteger(new(big.Int).Neg(x))) + v.estack.PushItem(stackitem.NewBigInteger(new(uint256.Int).Neg(x))) case opcode.INC: x := v.estack.Pop().BigInt() - a := new(big.Int).Add(x, bigOne) + a := new(uint256.Int).Add(x, bigOne) v.estack.PushItem(stackitem.NewBigInteger(a)) case opcode.DEC: x := v.estack.Pop().BigInt() - a := new(big.Int).Sub(x, bigOne) + a := new(uint256.Int).Sub(x, bigOne) v.estack.PushItem(stackitem.NewBigInteger(a)) case opcode.ADD: a := v.estack.Pop().BigInt() b := v.estack.Pop().BigInt() - c := new(big.Int).Add(a, b) + c := new(uint256.Int).Add(a, b) v.estack.PushItem(stackitem.NewBigInteger(c)) case opcode.SUB: b := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt() - c := new(big.Int).Sub(a, b) + c := new(uint256.Int).Sub(a, b) v.estack.PushItem(stackitem.NewBigInteger(c)) case opcode.MUL: a := v.estack.Pop().BigInt() b := v.estack.Pop().BigInt() - c := new(big.Int).Mul(a, b) + c := new(uint256.Int).Mul(a, b) v.estack.PushItem(stackitem.NewBigInteger(c)) case opcode.DIV: b := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt() - v.estack.PushItem(stackitem.NewBigInteger(new(big.Int).Quo(a, b))) + v.estack.PushItem(stackitem.NewBigInteger(new(uint256.Int).Div(a, b))) case opcode.MOD: b := v.estack.Pop().BigInt() a := v.estack.Pop().BigInt() - v.estack.PushItem(stackitem.NewBigInteger(new(big.Int).Rem(a, b))) + v.estack.PushItem(stackitem.NewBigInteger(mod(a, b))) case opcode.POW: exp := v.estack.Pop().BigInt() @@ -973,7 +976,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro if ei := exp.Uint64(); !exp.IsUint64() || ei > maxSHLArg { panic("invalid exponent") } - v.estack.PushItem(stackitem.NewBigInteger(new(big.Int).Exp(a, exp, nil))) + v.estack.PushItem(stackitem.NewBigInteger(new(uint256.Int).Exp(a, exp))) case opcode.SQRT: a := v.estack.Pop().BigInt() @@ -981,7 +984,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro panic("negative value") } - v.estack.PushItem(stackitem.NewBigInteger(new(big.Int).Sqrt(a))) + v.estack.PushItem(stackitem.NewBigInteger(sqrt(a))) case opcode.MODMUL: modulus := v.estack.Pop().BigInt() @@ -991,14 +994,14 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro x2 := v.estack.Pop().BigInt() x1 := v.estack.Pop().BigInt() - res := new(big.Int).Mul(x1, x2) - v.estack.PushItem(stackitem.NewBigInteger(res.Mod(res, modulus))) + res := new(uint256.Int).MulMod(x1, x2, modulus) + v.estack.PushItem(stackitem.NewBigInteger(res)) case opcode.MODPOW: modulus := v.estack.Pop().BigInt() exponent := v.estack.Pop().BigInt() base := v.estack.Pop().BigInt() - res := new(big.Int) + res := new(uint256.Int) switch exponent.Cmp(bigMinusOne) { case -1: panic("exponent should be >= -1") @@ -1009,14 +1012,16 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro if modulus.Cmp(bigTwo) < 0 { panic("invalid modulus") } - if res.ModInverse(base, modulus) == nil { + res = modInverse(base, modulus) + if res == nil { panic("base and modulus are not relatively prime") } case 1: if modulus.Sign() == 0 { panic("zero modulus") // https://docs.microsoft.com/en-us/dotnet/api/system.numerics.biginteger.modpow?view=net-6.0#exceptions } - res.Exp(base, exponent, modulus) + res.Exp(base, exponent) + res.Mod(res, modulus) } v.estack.PushItem(stackitem.NewBigInteger(res)) @@ -1030,7 +1035,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro } a := v.estack.Pop().BigInt() - var item big.Int + var item uint256.Int if op == opcode.SHL { item.Lsh(a, uint(b)) } else { @@ -1216,7 +1221,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro v.estack.PushItem(arr[i]) } } - v.estack.PushItem(stackitem.NewBigInteger(big.NewInt(int64(l)))) + v.estack.PushItem(stackitem.NewBigIntegerFromInt64(int64(l))) case opcode.PICKITEM: key := v.estack.Pop() @@ -1252,7 +1257,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro return } item := arr[index] - v.estack.PushItem(stackitem.NewBigInteger(big.NewInt(int64(item)))) + v.estack.PushItem(stackitem.NewBigIntegerFromInt64(int64(item))) } case opcode.SETITEM: @@ -1424,7 +1429,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro default: res = len(elem.Bytes()) } - v.estack.PushItem(stackitem.NewBigInteger(big.NewInt(int64(res)))) + v.estack.PushItem(stackitem.NewBigIntegerFromInt64(int64(res))) case opcode.JMP, opcode.JMPL, opcode.JMPIF, opcode.JMPIFL, opcode.JMPIFNOT, opcode.JMPIFNOTL, opcode.JMPEQ, opcode.JMPEQL, opcode.JMPNE, opcode.JMPNEL, @@ -1673,7 +1678,7 @@ func getTryParams(op opcode.Opcode, p []byte) ([]byte, []byte) { } // getJumpCondition performs opcode specific comparison of a and b. -func getJumpCondition(op opcode.Opcode, a, b *big.Int) bool { +func getJumpCondition(op opcode.Opcode, a, b *uint256.Int) bool { cmp := a.Cmp(b) switch op { case opcode.JMPEQ, opcode.JMPEQL: @@ -1931,7 +1936,7 @@ func makeArrayOfType(n int, typ stackitem.Type) []stackitem.Item { case stackitem.BooleanT: items[i] = stackitem.NewBool(false) case stackitem.IntegerT: - items[i] = stackitem.NewBigInteger(big.NewInt(0)) + items[i] = stackitem.NewBigInteger(uint256.NewInt(0)) case stackitem.ByteArrayT: items[i] = stackitem.NewByteArray([]byte{}) default: @@ -1983,13 +1988,73 @@ func (v *VM) GetCurrentScriptHash() util.Uint160 { } // toInt converts an item to a 32-bit int. -func toInt(i *big.Int) int { - if !i.IsInt64() { +func toInt(i *uint256.Int) int { + if i.Sgt(maxInt32) || i.Slt(minInt32) { panic("not an int32") } - n := i.Int64() - if n < math.MinInt32 || n > math.MaxInt32 { - panic("not an int32") + var v int64 + if i.Sign() < 0 { + v -= int64(new(uint256.Int).Neg(i).Uint64()) + } else { + v = int64(i.Uint64()) + } + return int(v) +} + +// sqrt calculate square root of uint256.Int. +func sqrt(value *uint256.Int) *uint256.Int { + if value.Sign() < 0 { + panic("square root of negative number") + } + if value.Sign() == 0 { + return uint256.NewInt(0) + } + if value.Lt(uint256.NewInt(4)) { + return uint256.NewInt(1) + } + x := new(uint256.Int).Lsh(uint256.NewInt(1), uint(value.BitLen()+1)/2) + for n := 0; ; n++ { + a := new(uint256.Int).Div(value, x) + a.Add(a, x) + a.Rsh(a, 1) + if a.Cmp(x) >= 0 { + if n&1 == 0 { + return x + } + return a + } + x = a + } +} + +// mod calculate mod of uint256.Int and ganrantee the result is positive. +func mod(value, modulus *uint256.Int) *uint256.Int { + x := new(uint256.Int).SMod(value, modulus) + if x.Sign() < 0 { + x.Add(x, modulus) + } + return x +} + +// modInverse calculate multiplicative inverse of value in modulus. +func modInverse(value, modulus *uint256.Int) *uint256.Int { + x, y := new(uint256.Int), new(uint256.Int) + g := exgcd(value, modulus, x, y) + if g.Cmp(uint256.NewInt(1)) != 0 { + return nil + } + return x +} + +func exgcd(a, b, x, y *uint256.Int) *uint256.Int { + if b.Sign() == 0 { + x.SetUint64(1) + y.SetUint64(0) + return a } - return int(n) + r := exgcd(b, mod(a, b), y, x) + t := new(uint256.Int).Div(a, b) + t.Mul(t, x) + y.Sub(y, t) + return r } diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index d78ab22c38..a1ecd4533b 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -12,12 +12,14 @@ import ( "strings" "testing" + "github.com/holiman/uint256" "github.com/nspcc-dev/neo-go/internal/random" "github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames" "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" + "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" @@ -46,7 +48,7 @@ func TestInteropHook(t *testing.T) { v.Load(buf.Bytes()) runVM(t, v) assert.Equal(t, 1, v.estack.Len()) - assert.Equal(t, big.NewInt(1), v.estack.Pop().value.Value()) + assert.Equal(t, uint256.NewInt(1), v.estack.Pop().value.Value()) } func TestVM_SetPriceGetter(t *testing.T) { @@ -196,7 +198,7 @@ func testISTYPE(t *testing.T, result bool, typ stackitem.Type, item stackitem.It func TestISTYPE(t *testing.T) { t.Run("Integer", func(t *testing.T) { - testISTYPE(t, true, stackitem.IntegerT, stackitem.NewBigInteger(big.NewInt(42))) + testISTYPE(t, true, stackitem.IntegerT, stackitem.NewBigInteger(uint256.NewInt(42))) testISTYPE(t, false, stackitem.IntegerT, stackitem.NewByteArray([]byte{})) }) t.Run("Boolean", func(t *testing.T) { @@ -205,7 +207,7 @@ func TestISTYPE(t *testing.T) { }) t.Run("ByteArray", func(t *testing.T) { testISTYPE(t, true, stackitem.ByteArrayT, stackitem.NewByteArray([]byte{})) - testISTYPE(t, false, stackitem.ByteArrayT, stackitem.NewBigInteger(big.NewInt(42))) + testISTYPE(t, false, stackitem.ByteArrayT, stackitem.NewBigInteger(uint256.NewInt(42))) }) t.Run("Array", func(t *testing.T) { testISTYPE(t, true, stackitem.ArrayT, stackitem.NewArray([]stackitem.Item{})) @@ -237,7 +239,7 @@ func TestCONVERT(t *testing.T) { item, res stackitem.Item } arr := []stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(7)), + stackitem.NewBigInteger(uint256.NewInt(7)), stackitem.NewByteArray([]byte{4, 8, 15}), } m := stackitem.NewMap() @@ -253,7 +255,7 @@ func TestCONVERT(t *testing.T) { } trueCases := []stackitem.Item{ - stackitem.NewBool(true), stackitem.NewBigInteger(big.NewInt(11)), stackitem.NewByteArray([]byte{1, 2, 3}), + stackitem.NewBool(true), stackitem.NewBigInteger(uint256.NewInt(11)), stackitem.NewByteArray([]byte{1, 2, 3}), stackitem.NewArray(arr), stackitem.NewArray(nil), stackitem.NewStruct(arr), stackitem.NewStruct(nil), stackitem.NewMap(), m, stackitem.NewInterop(struct{}{}), @@ -264,7 +266,7 @@ func TestCONVERT(t *testing.T) { } falseCases := []stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(0)), stackitem.NewByteArray([]byte{0, 0}), stackitem.NewBool(false), + stackitem.NewBigInteger(uint256.NewInt(0)), stackitem.NewByteArray([]byte{0, 0}), stackitem.NewBool(false), } for i := range falseCases { testBool(falseCases[i], stackitem.NewBool(false)) @@ -282,8 +284,8 @@ func TestCONVERT(t *testing.T) { }) t.Run("primitive -> Integer/ByteArray", func(t *testing.T) { - n := big.NewInt(42) - b := bigint.ToBytes(n) + n := uint256.NewInt(42) + b := bigint.Uint256ToBytes(n) itemInt := stackitem.NewBigInteger(n) itemBytes := stackitem.NewByteArray(b) @@ -292,8 +294,8 @@ func TestCONVERT(t *testing.T) { stackitem.IntegerT: { {itemInt, itemInt}, {itemBytes, itemInt}, - {stackitem.NewBool(true), stackitem.NewBigInteger(big.NewInt(1))}, - {stackitem.NewBool(false), stackitem.NewBigInteger(big.NewInt(0))}, + {stackitem.NewBool(true), stackitem.NewBigInteger(uint256.NewInt(1))}, + {stackitem.NewBool(false), stackitem.NewBigInteger(uint256.NewInt(0))}, }, stackitem.ByteArrayT: { {itemInt, itemBytes}, @@ -335,7 +337,7 @@ func TestCONVERT(t *testing.T) { t.Run("->Any", func(t *testing.T) { items := []stackitem.Item{ - stackitem.NewBigInteger(big.NewInt(1)), stackitem.NewByteArray([]byte{1}), stackitem.NewBool(true), + stackitem.NewBigInteger(uint256.NewInt(1)), stackitem.NewByteArray([]byte{1}), stackitem.NewBool(true), stackitem.NewArray(arr), stackitem.NewStruct(arr), m, stackitem.NewInterop(struct{}{}), } @@ -438,7 +440,7 @@ func TestPushm1to16(t *testing.T) { elem := vm.estack.Pop() val := i - int(opcode.PUSH1) + 1 - assert.Equal(t, elem.BigInt().Int64(), int64(val)) + assert.Equal(t, util.ToInt64(elem.BigInt()), int64(val)) } } @@ -546,7 +548,7 @@ func testJMP(t *testing.T, op opcode.Opcode, res interface{}, items ...interface return } runVM(t, v) - require.EqualValues(t, res, v.estack.Pop().BigInt().Int64()) + require.EqualValues(t, res, util.ToInt64(v.estack.Pop().BigInt())) } func TestJMPs(t *testing.T) { @@ -644,10 +646,18 @@ func TestNOT(t *testing.T) { t.Run("Buffer1", getTestFuncForVM(prog, false, stackitem.NewBuffer([]byte{1}))) } +func uint256FromInt64(val int64) *uint256.Int { + n, overflow := uint256.FromBig(big.NewInt(val)) + if overflow { + panic("overflow") + } + return n +} + // getBigInt returns 2^a+b. -func getBigInt(a, b int64) *big.Int { - p := new(big.Int).Exp(big.NewInt(2), big.NewInt(a), nil) - p.Add(p, big.NewInt(b)) +func getBigInt(a, b int64) *uint256.Int { + p := new(uint256.Int).Exp(uint256.NewInt(2), uint256FromInt64(a)) + p.Add(p, uint256FromInt64(b)) return p } @@ -710,7 +720,7 @@ func TestArithNegativeArguments(t *testing.T) { func TestSUBBigResult(t *testing.T) { prog := makeProgram(opcode.SUB) bi := getBigInt(stackitem.MaxBigIntegerSizeBits-1, -1) - runWithArgs(t, prog, new(big.Int).Sub(big.NewInt(-1), bi), -1, bi) + runWithArgs(t, prog, new(uint256.Int).Sub(uint256FromInt64(-1), bi), -1, bi) runWithArgs(t, prog, nil, -2, bi) } @@ -817,7 +827,7 @@ func TestDepth(t *testing.T) { vm.estack.PushVal(2) vm.estack.PushVal(3) runVM(t, vm) - assert.Equal(t, int64(3), vm.estack.Pop().BigInt().Int64()) + assert.Equal(t, int64(3), util.ToInt64(vm.estack.Pop().BigInt())) } func TestEQUALTrue(t *testing.T) { @@ -998,7 +1008,7 @@ func TestINCBigResult(t *testing.T) { require.NoError(t, vm.Step()) require.False(t, vm.HasFailed()) require.Equal(t, 1, vm.estack.Len()) - require.Equal(t, new(big.Int).Add(x, big.NewInt(1)), vm.estack.Top().BigInt()) + require.Equal(t, new(uint256.Int).Add(x, uint256.NewInt(1)), vm.estack.Top().BigInt()) checkVMFailed(t, vm) } @@ -1013,7 +1023,7 @@ func TestDECBigResult(t *testing.T) { require.NoError(t, vm.Step()) require.False(t, vm.HasFailed()) require.Equal(t, 1, vm.estack.Len()) - require.Equal(t, new(big.Int).Sub(x, big.NewInt(1)), vm.estack.Top().BigInt()) + require.Equal(t, new(uint256.Int).Sub(x, uint256.NewInt(1)), vm.estack.Top().BigInt()) checkVMFailed(t, vm) } @@ -1128,9 +1138,9 @@ func TestTRY(t *testing.T) { v := load(outer) runVM(t, v) require.Equal(t, 3, v.Estack().Len()) - require.Equal(t, big.NewInt(4), v.Estack().Pop().Value()) // outer FINALLY - require.Equal(t, big.NewInt(2), v.Estack().Pop().Value()) // inner FINALLY - require.Equal(t, big.NewInt(23), v.Estack().Pop().Value()) // inner THROW + CATCH + require.Equal(t, uint256.NewInt(4), v.Estack().Pop().Value()) // outer FINALLY + require.Equal(t, uint256.NewInt(2), v.Estack().Pop().Value()) // inner FINALLY + require.Equal(t, uint256.NewInt(23), v.Estack().Pop().Value()) // inner THROW + CATCH }) } @@ -1201,7 +1211,7 @@ func TestNEWARRAYIssue437(t *testing.T) { func TestNEWARRAYT(t *testing.T) { testCases := map[stackitem.Type]stackitem.Item{ stackitem.BooleanT: stackitem.NewBool(false), - stackitem.IntegerT: stackitem.NewBigInteger(big.NewInt(0)), + stackitem.IntegerT: stackitem.NewBigInteger(uint256.NewInt(0)), stackitem.ByteArrayT: stackitem.NewByteArray([]byte{}), stackitem.ArrayT: stackitem.Null{}, 0xFF: nil, @@ -1312,9 +1322,9 @@ func TestPICKITEMDupArray(t *testing.T) { vm.estack.PushVal([]stackitem.Item{stackitem.Make(-1)}) runVM(t, vm) assert.Equal(t, 2, vm.estack.Len()) - assert.Equal(t, int64(1), vm.estack.Pop().BigInt().Int64()) + assert.Equal(t, int64(1), util.ToInt64(vm.estack.Pop().BigInt())) items := vm.estack.Pop().Value().([]stackitem.Item) - assert.Equal(t, big.NewInt(-1), items[0].Value()) + assert.Equal(t, uint256FromInt64(-1), items[0].Value()) } func TestPICKITEMDupMap(t *testing.T) { @@ -1325,11 +1335,11 @@ func TestPICKITEMDupMap(t *testing.T) { vm.estack.Push(Element{value: m}) runVM(t, vm) assert.Equal(t, 2, vm.estack.Len()) - assert.Equal(t, int64(1), vm.estack.Pop().BigInt().Int64()) + assert.Equal(t, int64(1), util.ToInt64(vm.estack.Pop().BigInt())) items := vm.estack.Pop().Value().([]stackitem.MapElement) assert.Equal(t, 1, len(items)) - assert.Equal(t, big.NewInt(42), items[0].Key.Value()) - assert.Equal(t, big.NewInt(-1), items[0].Value.Value()) + assert.Equal(t, uint256.NewInt(42), items[0].Key.Value()) + assert.Equal(t, uint256FromInt64(-1), items[0].Value.Value()) } func TestPICKITEMMap(t *testing.T) { @@ -1405,8 +1415,8 @@ func TestSETITEMBigMapBad(t *testing.T) { func TestSETITEMMapStackLimit(t *testing.T) { size := MaxStackSize/2 - 4 m := stackitem.NewMap() - m.Add(stackitem.NewBigInteger(big.NewInt(1)), stackitem.NewArray(makeArrayOfType(size, stackitem.BooleanT))) - m.Add(stackitem.NewBigInteger(big.NewInt(2)), stackitem.NewArray(makeArrayOfType(size, stackitem.BooleanT))) + m.Add(stackitem.NewBigInteger(uint256.NewInt(1)), stackitem.NewArray(makeArrayOfType(size, stackitem.BooleanT))) + m.Add(stackitem.NewBigInteger(uint256.NewInt(2)), stackitem.NewArray(makeArrayOfType(size, stackitem.BooleanT))) prog := makeProgram( opcode.DUP, opcode.PUSH1, opcode.PUSH1, opcode.SETITEM, @@ -1594,7 +1604,7 @@ func TestSimpleCall(t *testing.T) { result := 12 vm := load(buf.Bytes()) runVM(t, vm) - assert.Equal(t, result, int(vm.estack.Pop().BigInt().Int64())) + assert.Equal(t, result, int(util.ToInt64(vm.estack.Pop().BigInt()))) } func TestNZ(t *testing.T) { @@ -1622,7 +1632,7 @@ func TestPICKgood(t *testing.T) { vm.estack.PushVal(5) vm.estack.PushVal(3) runVM(t, vm) - assert.Equal(t, int64(result), vm.estack.Pop().BigInt().Int64()) + assert.Equal(t, int64(result), util.ToInt64(vm.estack.Pop().BigInt())) } func TestPICKDup(t *testing.T) { @@ -1634,10 +1644,10 @@ func TestPICKDup(t *testing.T) { vm := load(prog) runVM(t, vm) assert.Equal(t, 4, vm.estack.Len()) - assert.Equal(t, int64(1), vm.estack.Pop().BigInt().Int64()) - assert.Equal(t, int64(1), vm.estack.Pop().BigInt().Int64()) - assert.Equal(t, int64(0), vm.estack.Pop().BigInt().Int64()) - assert.Equal(t, int64(-1), vm.estack.Pop().BigInt().Int64()) + assert.Equal(t, int64(1), util.ToInt64(vm.estack.Pop().BigInt())) + assert.Equal(t, int64(1), util.ToInt64(vm.estack.Pop().BigInt())) + assert.Equal(t, int64(0), util.ToInt64(vm.estack.Pop().BigInt())) + assert.Equal(t, int64(-1), util.ToInt64(vm.estack.Pop().BigInt())) } func TestROTBad(t *testing.T) { @@ -1668,9 +1678,9 @@ func TestROLLBad2(t *testing.T) { runWithArgs(t, prog, nil, 1, 2, 3, 3) } -func maxu64Plus(x int64) *big.Int { - bi := new(big.Int).SetUint64(math.MaxUint64) - bi.Add(bi, big.NewInt(2)) +func maxu64Plus(x int64) *uint256.Int { + bi := uint256.NewInt(math.MaxUint64) + bi.Add(bi, uint256.NewInt(2)) return bi } @@ -1739,9 +1749,9 @@ func TestTUCKgood(t *testing.T) { vm.estack.PushVal(42) vm.estack.PushVal(34) runVM(t, vm) - assert.Equal(t, int64(34), vm.estack.Peek(0).BigInt().Int64()) - assert.Equal(t, int64(42), vm.estack.Peek(1).BigInt().Int64()) - assert.Equal(t, int64(34), vm.estack.Peek(2).BigInt().Int64()) + assert.Equal(t, int64(34), util.ToInt64(vm.estack.Peek(0).BigInt())) + assert.Equal(t, int64(42), util.ToInt64(vm.estack.Peek(1).BigInt())) + assert.Equal(t, int64(34), util.ToInt64(vm.estack.Peek(2).BigInt())) } func TestTUCKgood2(t *testing.T) { @@ -1751,10 +1761,10 @@ func TestTUCKgood2(t *testing.T) { vm.estack.PushVal(42) vm.estack.PushVal(34) runVM(t, vm) - assert.Equal(t, int64(34), vm.estack.Peek(0).BigInt().Int64()) - assert.Equal(t, int64(42), vm.estack.Peek(1).BigInt().Int64()) - assert.Equal(t, int64(34), vm.estack.Peek(2).BigInt().Int64()) - assert.Equal(t, int64(11), vm.estack.Peek(3).BigInt().Int64()) + assert.Equal(t, int64(34), util.ToInt64(vm.estack.Peek(0).BigInt())) + assert.Equal(t, int64(42), util.ToInt64(vm.estack.Peek(1).BigInt())) + assert.Equal(t, int64(34), util.ToInt64(vm.estack.Peek(2).BigInt())) + assert.Equal(t, int64(11), util.ToInt64(vm.estack.Peek(3).BigInt())) } func TestOVER(t *testing.T) { @@ -1769,9 +1779,9 @@ func TestOVERgood(t *testing.T) { vm.estack.PushVal(42) vm.estack.PushVal(34) runVM(t, vm) - assert.Equal(t, int64(42), vm.estack.Peek(0).BigInt().Int64()) - assert.Equal(t, int64(34), vm.estack.Peek(1).BigInt().Int64()) - assert.Equal(t, int64(42), vm.estack.Peek(2).BigInt().Int64()) + assert.Equal(t, int64(42), util.ToInt64(vm.estack.Peek(0).BigInt())) + assert.Equal(t, int64(34), util.ToInt64(vm.estack.Peek(1).BigInt())) + assert.Equal(t, int64(42), util.ToInt64(vm.estack.Peek(2).BigInt())) assert.Equal(t, 3, vm.estack.Len()) } @@ -1787,7 +1797,7 @@ func TestOVERDup(t *testing.T) { runVM(t, vm) assert.Equal(t, 3, vm.estack.Len()) assert.Equal(t, []byte{0x01, 0x02}, vm.estack.Pop().Bytes()) - assert.Equal(t, int64(1), vm.estack.Pop().BigInt().Int64()) + assert.Equal(t, int64(1), util.ToInt64(vm.estack.Pop().BigInt())) assert.Equal(t, []byte{0x01, 0x00}, vm.estack.Pop().Bytes()) } @@ -1831,8 +1841,8 @@ func TestXDROPgood(t *testing.T) { vm.estack.PushVal(2) runVM(t, vm) assert.Equal(t, 2, vm.estack.Len()) - assert.Equal(t, int64(2), vm.estack.Peek(0).BigInt().Int64()) - assert.Equal(t, int64(1), vm.estack.Peek(1).BigInt().Int64()) + assert.Equal(t, int64(2), util.ToInt64(vm.estack.Peek(0).BigInt())) + assert.Equal(t, int64(1), util.ToInt64(vm.estack.Peek(1).BigInt())) } func TestCLEAR(t *testing.T) { @@ -1859,7 +1869,7 @@ func TestINVERTgood2(t *testing.T) { vm := load(prog) vm.estack.PushVal(-1) runVM(t, vm) - assert.Equal(t, int64(0), vm.estack.Peek(0).BigInt().Int64()) + assert.Equal(t, int64(0), util.ToInt64(vm.estack.Peek(0).BigInt())) } func TestINVERTgood3(t *testing.T) { @@ -1867,21 +1877,21 @@ func TestINVERTgood3(t *testing.T) { vm := load(prog) vm.estack.PushVal(0x69) runVM(t, vm) - assert.Equal(t, int64(-0x6A), vm.estack.Peek(0).BigInt().Int64()) + assert.Equal(t, int64(-0x6A), util.ToInt64(vm.estack.Peek(0).BigInt())) } func TestINVERTWithConversion1(t *testing.T) { prog := makeProgram(opcode.PUSHDATA2, 0, 0, opcode.INVERT) vm := load(prog) runVM(t, vm) - assert.Equal(t, int64(-1), vm.estack.Peek(0).BigInt().Int64()) + assert.Equal(t, int64(-1), util.ToInt64(vm.estack.Peek(0).BigInt())) } func TestINVERTWithConversion2(t *testing.T) { prog := makeProgram(opcode.PUSH0, opcode.PUSH1, opcode.NUMEQUAL, opcode.INVERT) vm := load(prog) runVM(t, vm) - assert.Equal(t, int64(-1), vm.estack.Peek(0).BigInt().Int64()) + assert.Equal(t, int64(-1), util.ToInt64(vm.estack.Peek(0).BigInt())) } func TestCAT(t *testing.T) { @@ -1970,10 +1980,10 @@ func TestPACKGood(t *testing.T) { a := vm.estack.Peek(0).Array() assert.Equal(t, len(elements), len(a)) for i := 0; i < len(elements); i++ { - e := a[i].Value().(*big.Int) - assert.Equal(t, int64(elements[i]), e.Int64()) + e := a[i].Value().(*uint256.Int) + assert.Equal(t, int64(elements[i]), util.ToInt64(e)) } - assert.Equal(t, int64(1), vm.estack.Peek(1).BigInt().Int64()) + assert.Equal(t, int64(1), util.ToInt64(vm.estack.Peek(1).BigInt())) }) } } @@ -1992,13 +2002,13 @@ func TestPACK_UNPACK_MaxSize(t *testing.T) { // check reference counter = 1+1+1024 assert.Equal(t, 1+1+len(elements), int(vm.refs)) assert.Equal(t, 1+1+len(elements), vm.estack.Len()) // canary + length + elements - assert.Equal(t, int64(len(elements)), vm.estack.Peek(0).Value().(*big.Int).Int64()) + assert.Equal(t, int64(len(elements)), util.ToInt64(vm.estack.Peek(0).Value().(*uint256.Int))) for i := 0; i < len(elements); i++ { - e, ok := vm.estack.Peek(i + 1).Value().(*big.Int) + e, ok := vm.estack.Peek(i + 1).Value().(*uint256.Int) assert.True(t, ok) - assert.Equal(t, int64(elements[i]), e.Int64()) + assert.Equal(t, int64(elements[i]), util.ToInt64(e)) } - assert.Equal(t, int64(1), vm.estack.Peek(1+len(elements)).BigInt().Int64()) + assert.Equal(t, int64(1), util.ToInt64(vm.estack.Peek(1+len(elements)).BigInt())) } func TestPACK_UNPACK_PACK_MaxSize(t *testing.T) { @@ -2018,10 +2028,10 @@ func TestPACK_UNPACK_PACK_MaxSize(t *testing.T) { a := vm.estack.Peek(0).Array() assert.Equal(t, len(elements), len(a)) for i := 0; i < len(elements); i++ { - e := a[i].Value().(*big.Int) - assert.Equal(t, int64(elements[i]), e.Int64()) + e := a[i].Value().(*uint256.Int) + assert.Equal(t, int64(elements[i]), util.ToInt64(e)) } - assert.Equal(t, int64(1), vm.estack.Peek(1).BigInt().Int64()) + assert.Equal(t, int64(1), util.ToInt64(vm.estack.Peek(1).BigInt())) } func TestPACKMAP_UNPACK_PACKMAP_MaxSize(t *testing.T) { @@ -2043,12 +2053,12 @@ func TestPACKMAP_UNPACK_PACKMAP_MaxSize(t *testing.T) { m := vm.estack.Peek(0).value.(*stackitem.Map).Value().([]stackitem.MapElement) assert.Equal(t, len(elements), len(m)) for i := 0; i < len(elements); i++ { - k := m[i].Key.Value().(*big.Int) - v := m[i].Value.Value().(*big.Int) - assert.Equal(t, int64(elements[i]), k.Int64()) - assert.Equal(t, int64(elements[i])*2, v.Int64()) + k := m[i].Key.Value().(*uint256.Int) + v := m[i].Value.Value().(*uint256.Int) + assert.Equal(t, int64(elements[i]), util.ToInt64(k)) + assert.Equal(t, int64(elements[i])*2, util.ToInt64(v)) } - assert.Equal(t, int64(-1), vm.estack.Peek(1).BigInt().Int64()) + assert.Equal(t, int64(-1), util.ToInt64(vm.estack.Peek(1).BigInt())) } func TestPACKMAPBadKey(t *testing.T) { @@ -2074,11 +2084,11 @@ func TestUNPACKGood(t *testing.T) { vm.estack.PushVal(elements) runVM(t, vm) assert.Equal(t, 5, vm.estack.Len()) - assert.Equal(t, int64(len(elements)), vm.estack.Peek(0).BigInt().Int64()) + assert.Equal(t, int64(len(elements)), util.ToInt64(vm.estack.Peek(0).BigInt())) for k, v := range elements { - assert.Equal(t, int64(v), vm.estack.Peek(k+1).BigInt().Int64()) + assert.Equal(t, int64(v), util.ToInt64(vm.estack.Peek(k+1).BigInt())) } - assert.Equal(t, int64(1), vm.estack.Peek(len(elements)+1).BigInt().Int64()) + assert.Equal(t, int64(1), util.ToInt64(vm.estack.Peek(len(elements)+1).BigInt())) } func TestREVERSEITEMS(t *testing.T) { @@ -2127,8 +2137,8 @@ func TestREVERSEITEMSGoodOneElem(t *testing.T) { assert.Equal(t, 2, vm.estack.Len()) a := vm.estack.Peek(0).Array() assert.Equal(t, len(elements), len(a)) - e := a[0].Value().(*big.Int) - assert.Equal(t, int64(elements[0]), e.Int64()) + e := a[0].Value().(*uint256.Int) + assert.Equal(t, int64(elements[0]), util.ToInt64(e)) } func TestREVERSEITEMSGoodStruct(t *testing.T) { @@ -2152,10 +2162,10 @@ func TestREVERSEITEMSGoodStruct(t *testing.T) { a := vm.estack.Peek(0).Array() assert.Equal(t, len(elements), len(a)) for k, v := range elements { - e := a[len(a)-1-k].Value().(*big.Int) - assert.Equal(t, int64(v), e.Int64()) + e := a[len(a)-1-k].Value().(*uint256.Int) + assert.Equal(t, int64(v), util.ToInt64(e)) } - assert.Equal(t, int64(1), vm.estack.Peek(1).BigInt().Int64()) + assert.Equal(t, int64(1), util.ToInt64(vm.estack.Peek(1).BigInt())) } } @@ -2174,10 +2184,10 @@ func TestREVERSEITEMSGood(t *testing.T) { a := vm.estack.Peek(0).Array() assert.Equal(t, len(elements), len(a)) for k, v := range elements { - e := a[len(a)-1-k].Value().(*big.Int) - assert.Equal(t, int64(v), e.Int64()) + e := a[len(a)-1-k].Value().(*uint256.Int) + assert.Equal(t, int64(v), util.ToInt64(e)) } - assert.Equal(t, int64(1), vm.estack.Peek(1).BigInt().Int64()) + assert.Equal(t, int64(1), util.ToInt64(vm.estack.Peek(1).BigInt())) } } @@ -2226,14 +2236,14 @@ func testCLEARITEMS(t *testing.T, item stackitem.Item) { runVM(t, v) require.Equal(t, 2, v.estack.Len()) require.EqualValues(t, 2, int(v.refs)) // empty collection + it's size - require.EqualValues(t, 0, v.estack.Pop().BigInt().Int64()) + require.EqualValues(t, 0, util.ToInt64(v.estack.Pop().BigInt())) } func TestCLEARITEMS(t *testing.T) { - arr := []stackitem.Item{stackitem.NewBigInteger(big.NewInt(1)), stackitem.NewByteArray([]byte{1})} + arr := []stackitem.Item{stackitem.NewBigInteger(uint256.NewInt(1)), stackitem.NewByteArray([]byte{1})} m := stackitem.NewMap() - m.Add(stackitem.NewBigInteger(big.NewInt(1)), stackitem.NewByteArray([]byte{})) - m.Add(stackitem.NewByteArray([]byte{42}), stackitem.NewBigInteger(big.NewInt(2))) + m.Add(stackitem.NewBigInteger(uint256.NewInt(1)), stackitem.NewByteArray([]byte{})) + m.Add(stackitem.NewByteArray([]byte{42}), stackitem.NewBigInteger(uint256.NewInt(2))) testCases := map[string]stackitem.Item{ "empty Array": stackitem.NewArray([]stackitem.Item{}), @@ -2291,8 +2301,8 @@ func TestSWAPGood(t *testing.T) { vm.estack.PushVal(4) runVM(t, vm) assert.Equal(t, 2, vm.estack.Len()) - assert.Equal(t, int64(2), vm.estack.Pop().BigInt().Int64()) - assert.Equal(t, int64(4), vm.estack.Pop().BigInt().Int64()) + assert.Equal(t, int64(2), util.ToInt64(vm.estack.Pop().BigInt())) + assert.Equal(t, int64(4), util.ToInt64(vm.estack.Pop().BigInt())) } func TestSWAP(t *testing.T) { @@ -2307,8 +2317,8 @@ func TestDupInt(t *testing.T) { vm.estack.PushVal(-1) runVM(t, vm) assert.Equal(t, 2, vm.estack.Len()) - assert.Equal(t, int64(1), vm.estack.Pop().BigInt().Int64()) - assert.Equal(t, int64(-1), vm.estack.Pop().BigInt().Int64()) + assert.Equal(t, int64(1), util.ToInt64(vm.estack.Pop().BigInt())) + assert.Equal(t, int64(-1), util.ToInt64(vm.estack.Pop().BigInt())) } func TestNegateCopy(t *testing.T) { @@ -2319,8 +2329,8 @@ func TestNegateCopy(t *testing.T) { v.estack.PushVal(bi) runVM(t, v) assert.Equal(t, 2, v.estack.Len()) - assert.Equal(t, int64(1), v.estack.Pop().BigInt().Int64()) - assert.Equal(t, int64(-1), v.estack.Pop().BigInt().Int64()) + assert.Equal(t, int64(1), util.ToInt64(v.estack.Pop().BigInt())) + assert.Equal(t, int64(-1), util.ToInt64(v.estack.Pop().BigInt())) } func TestDupByteArray(t *testing.T) { @@ -2361,7 +2371,7 @@ var opcodesTestCases = map[opcode.Opcode][]struct { args: []interface{}{1, 1}, expected: int64(1), actual: func(vm *VM) interface{} { - return vm.estack.Pop().BigInt().Int64() + return util.ToInt64(vm.estack.Pop().BigInt()) }, }, { @@ -2369,7 +2379,7 @@ var opcodesTestCases = map[opcode.Opcode][]struct { args: []interface{}{1, 0}, expected: int64(0), actual: func(vm *VM) interface{} { - return vm.estack.Pop().BigInt().Int64() + return util.ToInt64(vm.estack.Pop().BigInt()) }, }, { @@ -2377,7 +2387,7 @@ var opcodesTestCases = map[opcode.Opcode][]struct { args: []interface{}{0, 1}, expected: int64(0), actual: func(vm *VM) interface{} { - return vm.estack.Pop().BigInt().Int64() + return util.ToInt64(vm.estack.Pop().BigInt()) }, }, { @@ -2385,7 +2395,7 @@ var opcodesTestCases = map[opcode.Opcode][]struct { args: []interface{}{0, 0}, expected: int64(0), actual: func(vm *VM) interface{} { - return vm.estack.Pop().BigInt().Int64() + return util.ToInt64(vm.estack.Pop().BigInt()) }, }, { @@ -2406,7 +2416,7 @@ var opcodesTestCases = map[opcode.Opcode][]struct { args: []interface{}{1, 1}, expected: int64(1), actual: func(vm *VM) interface{} { - return vm.estack.Pop().BigInt().Int64() + return util.ToInt64(vm.estack.Pop().BigInt()) }, }, { @@ -2414,7 +2424,7 @@ var opcodesTestCases = map[opcode.Opcode][]struct { args: []interface{}{0, 0}, expected: int64(0), actual: func(vm *VM) interface{} { - return vm.estack.Pop().BigInt().Int64() + return util.ToInt64(vm.estack.Pop().BigInt()) }, }, { @@ -2422,7 +2432,7 @@ var opcodesTestCases = map[opcode.Opcode][]struct { args: []interface{}{0, 1}, expected: int64(1), actual: func(vm *VM) interface{} { - return vm.estack.Pop().BigInt().Int64() + return util.ToInt64(vm.estack.Pop().BigInt()) }, }, { @@ -2430,7 +2440,7 @@ var opcodesTestCases = map[opcode.Opcode][]struct { args: []interface{}{1, 0}, expected: int64(1), actual: func(vm *VM) interface{} { - return vm.estack.Pop().BigInt().Int64() + return util.ToInt64(vm.estack.Pop().BigInt()) }, }, { @@ -2451,7 +2461,7 @@ var opcodesTestCases = map[opcode.Opcode][]struct { args: []interface{}{1, 1}, expected: int64(0), actual: func(vm *VM) interface{} { - return vm.estack.Pop().BigInt().Int64() + return util.ToInt64(vm.estack.Pop().BigInt()) }, }, { @@ -2459,7 +2469,7 @@ var opcodesTestCases = map[opcode.Opcode][]struct { args: []interface{}{0, 0}, expected: int64(0), actual: func(vm *VM) interface{} { - return vm.estack.Pop().BigInt().Int64() + return util.ToInt64(vm.estack.Pop().BigInt()) }, }, { @@ -2467,7 +2477,7 @@ var opcodesTestCases = map[opcode.Opcode][]struct { args: []interface{}{0, 1}, expected: int64(1), actual: func(vm *VM) interface{} { - return vm.estack.Pop().BigInt().Int64() + return util.ToInt64(vm.estack.Pop().BigInt()) }, }, { @@ -2475,7 +2485,7 @@ var opcodesTestCases = map[opcode.Opcode][]struct { args: []interface{}{1, 0}, expected: int64(1), actual: func(vm *VM) interface{} { - return vm.estack.Pop().BigInt().Int64() + return util.ToInt64(vm.estack.Pop().BigInt()) }, }, { @@ -2530,7 +2540,7 @@ var opcodesTestCases = map[opcode.Opcode][]struct { args: []interface{}{3, 5}, expected: int64(3), actual: func(vm *VM) interface{} { - return vm.estack.Pop().BigInt().Int64() + return util.ToInt64(vm.estack.Pop().BigInt()) }, }, { @@ -2538,7 +2548,7 @@ var opcodesTestCases = map[opcode.Opcode][]struct { args: []interface{}{5, 3}, expected: int64(3), actual: func(vm *VM) interface{} { - return vm.estack.Pop().BigInt().Int64() + return util.ToInt64(vm.estack.Pop().BigInt()) }, }, { @@ -2546,7 +2556,7 @@ var opcodesTestCases = map[opcode.Opcode][]struct { args: []interface{}{3, 3}, expected: int64(3), actual: func(vm *VM) interface{} { - return vm.estack.Pop().BigInt().Int64() + return util.ToInt64(vm.estack.Pop().BigInt()) }, }, }, @@ -2556,7 +2566,7 @@ var opcodesTestCases = map[opcode.Opcode][]struct { args: []interface{}{3, 5}, expected: int64(5), actual: func(vm *VM) interface{} { - return vm.estack.Pop().BigInt().Int64() + return util.ToInt64(vm.estack.Pop().BigInt()) }, }, { @@ -2564,7 +2574,7 @@ var opcodesTestCases = map[opcode.Opcode][]struct { args: []interface{}{5, 3}, expected: int64(5), actual: func(vm *VM) interface{} { - return vm.estack.Pop().BigInt().Int64() + return util.ToInt64(vm.estack.Pop().BigInt()) }, }, { @@ -2572,7 +2582,7 @@ var opcodesTestCases = map[opcode.Opcode][]struct { args: []interface{}{3, 3}, expected: int64(3), actual: func(vm *VM) interface{} { - return vm.estack.Pop().BigInt().Int64() + return util.ToInt64(vm.estack.Pop().BigInt()) }, }, }, @@ -2608,7 +2618,7 @@ var opcodesTestCases = map[opcode.Opcode][]struct { args: []interface{}{3}, expected: int64(-3), actual: func(vm *VM) interface{} { - return vm.estack.Pop().BigInt().Int64() + return util.ToInt64(vm.estack.Pop().BigInt()) }, }, { @@ -2616,7 +2626,7 @@ var opcodesTestCases = map[opcode.Opcode][]struct { args: []interface{}{-3}, expected: int64(3), actual: func(vm *VM) interface{} { - return vm.estack.Pop().BigInt().Int64() + return util.ToInt64(vm.estack.Pop().BigInt()) }, }, { @@ -2624,7 +2634,7 @@ var opcodesTestCases = map[opcode.Opcode][]struct { args: []interface{}{0}, expected: int64(0), actual: func(vm *VM) interface{} { - return vm.estack.Pop().BigInt().Int64() + return util.ToInt64(vm.estack.Pop().BigInt()) }, }, }, @@ -2792,3 +2802,76 @@ func newTestVM() *VM { v.GasLimit = -1 return v } + +func TestSqrt(t *testing.T) { + cases := []struct { + expect uint64 + number uint64 + }{ + {1, 1}, + {1, 2}, + {1, 3}, + {2, 4}, + {3, 9}, + {3, 13}, + {4, 16}, + {9, 81}, + {2603, 6777216}, + {11111, 123456789}, + {180, 32639}, + {2890, 8355711}, + {46249, 2139062143}, + {11839999, 140185576636287}, + {3031039747, 9187201950435737471}, + {3031039747, 9187201947893824009}, + } + for _, tc := range cases { + assert.Equal(t, uint256.NewInt(tc.expect), sqrt(uint256.NewInt(tc.number)), "sqrt failed", tc.number) + } +} + +func BenchmarkSqrt_1(b *testing.B) { + for i := 0; i < b.N; i++ { + sqrt(uint256.NewInt(9187201950435737471)) + } +} + +func BenchmarkSqrt_2(b *testing.B) { + for i := 0; i < b.N; i++ { + new(big.Int).Sqrt(big.NewInt(9187201950435737471)) + } +} + +func TestModInverse(t *testing.T) { + cases := []struct { + expect uint64 + number uint64 + modulus uint64 + }{ + {52, 19, 141}, + } + for _, tc := range cases { + assert.Equal(t, uint256.NewInt(tc.expect), modInverse(uint256.NewInt(tc.number), uint256.NewInt(tc.modulus)), "modinverse failed", tc.number) + } +} + +func BenchmarkModInverse(b *testing.B) { + for i := 0; i < b.N; i++ { + modInverse(uint256.NewInt(19), uint256.NewInt(141)) + } +} + +func TestToInt(t *testing.T) { + min, _ := uint256.FromBig(big.NewInt(math.MinInt32)) + max, _ := uint256.FromBig(big.NewInt(math.MaxInt32)) + x := toInt(min) + assert.Equal(t, math.MinInt32, x) + x = toInt(max) + assert.Equal(t, math.MaxInt32, x) + b, _ := uint256.FromBig(big.NewInt(math.MinInt32 / 2)) + x = toInt(b) + assert.Equal(t, math.MinInt32/2, x) + b, _ = uint256.FromBig(big.NewInt(math.MaxInt32 / 2)) + x = toInt(b) + assert.Equal(t, math.MaxInt32/2, x) +}