From 588f3fbbd3dfb82cf49c7f7976cb28ba78b063cf Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Sun, 18 Jul 2021 12:39:31 +0300 Subject: [PATCH 1/4] state: drop State from NEOBalance and NEP17Balance We're in the `state` package already. --- cli/wallet/validator.go | 2 +- pkg/core/native/native_gas.go | 4 ++-- pkg/core/native/native_neo.go | 12 +++++----- pkg/core/state/native_state.go | 42 +++++++++++++++++----------------- 4 files changed, 30 insertions(+), 30 deletions(-) diff --git a/cli/wallet/validator.go b/cli/wallet/validator.go index 468e4a259..cee1ffaf4 100644 --- a/cli/wallet/validator.go +++ b/cli/wallet/validator.go @@ -252,7 +252,7 @@ func getAccountState(ctx *cli.Context) error { if len(res.Stack) == 0 { return cli.NewExitError("result stack is empty", 1) } - st := new(state.NEOBalanceState) + st := new(state.NEOBalance) err = st.FromStackItem(res.Stack[0]) if err != nil { return cli.NewExitError(fmt.Errorf("failed to convert account state from stackitem: %w", err), 1) diff --git a/pkg/core/native/native_gas.go b/pkg/core/native/native_gas.go index f0e0bed15..7167706d1 100644 --- a/pkg/core/native/native_gas.go +++ b/pkg/core/native/native_gas.go @@ -53,7 +53,7 @@ func newGAS() *GAS { } func (g *GAS) increaseBalance(_ *interop.Context, _ util.Uint160, si *state.StorageItem, amount *big.Int) error { - acc, err := state.NEP17BalanceStateFromBytes(*si) + acc, err := state.NEP17BalanceFromBytes(*si) if err != nil { return err } @@ -72,7 +72,7 @@ func (g *GAS) increaseBalance(_ *interop.Context, _ util.Uint160, si *state.Stor } func (g *GAS) balanceFromBytes(si *state.StorageItem) (*big.Int, error) { - acc, err := state.NEP17BalanceStateFromBytes(*si) + acc, err := state.NEP17BalanceFromBytes(*si) if err != nil { return nil, err } diff --git a/pkg/core/native/native_neo.go b/pkg/core/native/native_neo.go index 3726d9063..6f5fb1e9e 100644 --- a/pkg/core/native/native_neo.go +++ b/pkg/core/native/native_neo.go @@ -392,7 +392,7 @@ func (n *NEO) getGASPerVote(d dao.DAO, key []byte, index ...uint32) []big.Int { } func (n *NEO) increaseBalance(ic *interop.Context, h util.Uint160, si *state.StorageItem, amount *big.Int) error { - acc, err := state.NEOBalanceStateFromBytes(*si) + acc, err := state.NEOBalanceFromBytes(*si) if err != nil { return err } @@ -424,14 +424,14 @@ func (n *NEO) increaseBalance(ic *interop.Context, h util.Uint160, si *state.Sto } func (n *NEO) balanceFromBytes(si *state.StorageItem) (*big.Int, error) { - acc, err := state.NEOBalanceStateFromBytes(*si) + acc, err := state.NEOBalanceFromBytes(*si) if err != nil { return nil, err } return &acc.Balance, err } -func (n *NEO) distributeGas(ic *interop.Context, h util.Uint160, acc *state.NEOBalanceState) error { +func (n *NEO) distributeGas(ic *interop.Context, h util.Uint160, acc *state.NEOBalance) error { if ic.Block == nil || ic.Block.Index == 0 { return nil } @@ -609,7 +609,7 @@ func (n *NEO) CalculateBonus(d dao.DAO, acc util.Uint160, end uint32) (*big.Int, if si == nil { return nil, storage.ErrKeyNotFound } - st, err := state.NEOBalanceStateFromBytes(si) + st, err := state.NEOBalanceFromBytes(si) if err != nil { return nil, err } @@ -752,7 +752,7 @@ func (n *NEO) VoteInternal(ic *interop.Context, h util.Uint160, pub *keys.Public if si == nil { return errors.New("invalid account") } - acc, err := state.NEOBalanceStateFromBytes(si) + acc, err := state.NEOBalanceFromBytes(si) if err != nil { return err } @@ -802,7 +802,7 @@ func (n *NEO) VoteInternal(ic *interop.Context, h util.Uint160, pub *keys.Public // ModifyAccountVotes modifies votes of the specified account by value (can be negative). // typ specifies if this modify is occurring during transfer or vote (with old or new validator). -func (n *NEO) ModifyAccountVotes(acc *state.NEOBalanceState, d dao.DAO, value *big.Int, isNewVote bool) error { +func (n *NEO) ModifyAccountVotes(acc *state.NEOBalance, d dao.DAO, value *big.Int, isNewVote bool) error { n.votesChanged.Store(true) if acc.VoteTo != nil { key := makeValidatorKey(acc.VoteTo) diff --git a/pkg/core/state/native_state.go b/pkg/core/state/native_state.go index a4f652e3f..44e1adb50 100644 --- a/pkg/core/state/native_state.go +++ b/pkg/core/state/native_state.go @@ -10,21 +10,21 @@ import ( "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) -// NEP17BalanceState represents balance state of a NEP17-token. -type NEP17BalanceState struct { +// NEP17Balance represents balance state of a NEP17-token. +type NEP17Balance struct { Balance big.Int } -// NEOBalanceState represents balance state of a NEO-token. -type NEOBalanceState struct { - NEP17BalanceState +// NEOBalance represents balance state of a NEO-token. +type NEOBalance struct { + NEP17Balance BalanceHeight uint32 VoteTo *keys.PublicKey } -// NEP17BalanceStateFromBytes converts serialized NEP17BalanceState to structure. -func NEP17BalanceStateFromBytes(b []byte) (*NEP17BalanceState, error) { - balance := new(NEP17BalanceState) +// NEP17BalanceFromBytes converts serialized NEP17Balance to structure. +func NEP17BalanceFromBytes(b []byte) (*NEP17Balance, error) { + balance := new(NEP17Balance) err := balanceFromBytes(b, balance) if err != nil { return nil, err @@ -32,8 +32,8 @@ func NEP17BalanceStateFromBytes(b []byte) (*NEP17BalanceState, error) { return balance, nil } -// Bytes returns serialized NEP17BalanceState. -func (s *NEP17BalanceState) Bytes() []byte { +// Bytes returns serialized NEP17Balance. +func (s *NEP17Balance) Bytes() []byte { return balanceToBytes(s) } @@ -53,12 +53,12 @@ func balanceToBytes(item stackitem.Convertible) []byte { } // ToStackItem implements stackitem.Convertible. It never returns an error. -func (s *NEP17BalanceState) ToStackItem() (stackitem.Item, error) { +func (s *NEP17Balance) ToStackItem() (stackitem.Item, error) { return stackitem.NewStruct([]stackitem.Item{stackitem.NewBigInteger(&s.Balance)}), nil } // FromStackItem implements stackitem.Convertible. -func (s *NEP17BalanceState) FromStackItem(item stackitem.Item) error { +func (s *NEP17Balance) FromStackItem(item stackitem.Item) error { items, ok := item.Value().([]stackitem.Item) if !ok { return errors.New("not a struct") @@ -74,9 +74,9 @@ func (s *NEP17BalanceState) FromStackItem(item stackitem.Item) error { return nil } -// NEOBalanceStateFromBytes converts serialized NEOBalanceState to structure. -func NEOBalanceStateFromBytes(b []byte) (*NEOBalanceState, error) { - balance := new(NEOBalanceState) +// NEOBalanceFromBytes converts serialized NEOBalance to structure. +func NEOBalanceFromBytes(b []byte) (*NEOBalance, error) { + balance := new(NEOBalance) err := balanceFromBytes(b, balance) if err != nil { return nil, err @@ -84,14 +84,14 @@ func NEOBalanceStateFromBytes(b []byte) (*NEOBalanceState, error) { return balance, nil } -// Bytes returns serialized NEOBalanceState. -func (s *NEOBalanceState) Bytes() []byte { +// Bytes returns serialized NEOBalance. +func (s *NEOBalance) Bytes() []byte { return balanceToBytes(s) } // ToStackItem implements stackitem.Convertible interface. It never returns an error. -func (s *NEOBalanceState) ToStackItem() (stackitem.Item, error) { - resItem, _ := s.NEP17BalanceState.ToStackItem() +func (s *NEOBalance) ToStackItem() (stackitem.Item, error) { + resItem, _ := s.NEP17Balance.ToStackItem() result := resItem.(*stackitem.Struct) result.Append(stackitem.NewBigInteger(big.NewInt(int64(s.BalanceHeight)))) if s.VoteTo != nil { @@ -102,8 +102,8 @@ func (s *NEOBalanceState) ToStackItem() (stackitem.Item, error) { return result, nil } -// FromStackItem converts stackitem.Item to NEOBalanceState. -func (s *NEOBalanceState) FromStackItem(item stackitem.Item) error { +// FromStackItem converts stackitem.Item to NEOBalance. +func (s *NEOBalance) FromStackItem(item stackitem.Item) error { structItem, ok := item.Value().([]stackitem.Item) if !ok || len(structItem) < 3 { return errors.New("invalid stackitem length") From 100e97d77299d3c7f1fb8f062a9e2872aca6c75a Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Sun, 18 Jul 2021 15:55:37 +0300 Subject: [PATCH 2/4] util: move ArrayReverse into package of its own Leave just uint160 and uint256 types in util. --- pkg/encoding/bigint/bigint.go | 4 ++-- pkg/encoding/bigint/bigint_test.go | 4 ++-- pkg/util/array.go | 10 ---------- pkg/util/slice/array.go | 14 ++++++++++++++ pkg/util/{ => slice}/array_test.go | 6 +++--- pkg/util/uint160.go | 3 ++- pkg/util/uint256.go | 5 +++-- pkg/vm/cli/cli.go | 3 ++- 8 files changed, 28 insertions(+), 21 deletions(-) delete mode 100644 pkg/util/array.go create mode 100644 pkg/util/slice/array.go rename pkg/util/{ => slice}/array_test.go (88%) diff --git a/pkg/encoding/bigint/bigint.go b/pkg/encoding/bigint/bigint.go index aa189ca0a..b2ab067b0 100644 --- a/pkg/encoding/bigint/bigint.go +++ b/pkg/encoding/bigint/bigint.go @@ -5,7 +5,7 @@ import ( "math/big" "math/bits" - "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/util/slice" ) const ( @@ -17,7 +17,7 @@ const ( // FromBytesUnsigned converts data in little-endian format to an unsigned integer. func FromBytesUnsigned(data []byte) *big.Int { - bs := util.ArrayReverse(data) + bs := slice.CopyReverse(data) return new(big.Int).SetBytes(bs) } diff --git a/pkg/encoding/bigint/bigint_test.go b/pkg/encoding/bigint/bigint_test.go index 287582670..79c277546 100644 --- a/pkg/encoding/bigint/bigint_test.go +++ b/pkg/encoding/bigint/bigint_test.go @@ -5,7 +5,7 @@ import ( "math/big" "testing" - "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/util/slice" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -204,7 +204,7 @@ func TestVeryBigInts(t *testing.T) { num, ok := new(big.Int).SetString(tc.numStr, 10) assert.True(t, ok) - result := FromBytes(util.ArrayReverse(tc.buf)) + result := FromBytes(slice.CopyReverse(tc.buf)) assert.Equal(t, num, result, "error while converting %s from bytes", tc.numStr) } } diff --git a/pkg/util/array.go b/pkg/util/array.go deleted file mode 100644 index 4fd4589ed..000000000 --- a/pkg/util/array.go +++ /dev/null @@ -1,10 +0,0 @@ -package util - -// ArrayReverse returns a reversed version of the given byte slice. -func ArrayReverse(b []byte) []byte { - dest := make([]byte, len(b)) - for i, j := 0, len(b)-1; i <= j; i, j = i+1, j-1 { - dest[i], dest[j] = b[j], b[i] - } - return dest -} diff --git a/pkg/util/slice/array.go b/pkg/util/slice/array.go new file mode 100644 index 000000000..50e3f5d6c --- /dev/null +++ b/pkg/util/slice/array.go @@ -0,0 +1,14 @@ +/* +Package slice contains byte slice helpers. +*/ +package slice + +// CopyReverse returns a new byte slice containing reversed version of the +// original. +func CopyReverse(b []byte) []byte { + dest := make([]byte, len(b)) + for i, j := 0, len(b)-1; i <= j; i, j = i+1, j-1 { + dest[i], dest[j] = b[j], b[i] + } + return dest +} diff --git a/pkg/util/array_test.go b/pkg/util/slice/array_test.go similarity index 88% rename from pkg/util/array_test.go rename to pkg/util/slice/array_test.go index 4f44ac5df..7ba88dec9 100644 --- a/pkg/util/array_test.go +++ b/pkg/util/slice/array_test.go @@ -1,4 +1,4 @@ -package util +package slice import ( "testing" @@ -28,12 +28,12 @@ var testCases = []struct { }, } -func TestArrayReverse(t *testing.T) { +func TestCopyReverse(t *testing.T) { for _, tc := range testCases { arg := make([]byte, len(tc.arr)) copy(arg, tc.arr) - have := ArrayReverse(arg) + have := CopyReverse(arg) require.Equal(t, tc.rev, have) // test that argument was copied diff --git a/pkg/util/uint160.go b/pkg/util/uint160.go index 0eddf3fc2..991183e0f 100644 --- a/pkg/util/uint160.go +++ b/pkg/util/uint160.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/nspcc-dev/neo-go/pkg/io" + "github.com/nspcc-dev/neo-go/pkg/util/slice" ) // Uint160Size is the size of Uint160 in bytes. @@ -74,7 +75,7 @@ func (u Uint160) BytesBE() []byte { // BytesLE returns a little-endian byte representation of u. func (u Uint160) BytesLE() []byte { - return ArrayReverse(u.BytesBE()) + return slice.CopyReverse(u.BytesBE()) } // String implements the stringer interface. diff --git a/pkg/util/uint256.go b/pkg/util/uint256.go index ff764d4da..f3560b126 100644 --- a/pkg/util/uint256.go +++ b/pkg/util/uint256.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/nspcc-dev/neo-go/pkg/io" + "github.com/nspcc-dev/neo-go/pkg/util/slice" ) // Uint256Size is the size of Uint256 in bytes. @@ -54,7 +55,7 @@ func Uint256DecodeBytesBE(b []byte) (u Uint256, err error) { // Uint256DecodeBytesLE attempts to decode the given string (in LE representation) into an Uint256. func Uint256DecodeBytesLE(b []byte) (u Uint256, err error) { - b = ArrayReverse(b) + b = slice.CopyReverse(b) return Uint256DecodeBytesBE(b) } @@ -71,7 +72,7 @@ func (u Uint256) Reverse() Uint256 { // BytesLE return a little-endian byte representation of u. func (u Uint256) BytesLE() []byte { - return ArrayReverse(u.BytesBE()) + return slice.CopyReverse(u.BytesBE()) } // Equals returns true if both Uint256 values are the same. diff --git a/pkg/vm/cli/cli.go b/pkg/vm/cli/cli.go index 48dcc5b8f..cdc903f76 100644 --- a/pkg/vm/cli/cli.go +++ b/pkg/vm/cli/cli.go @@ -21,6 +21,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "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" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "gopkg.in/abiosoft/ishell.v2" @@ -565,7 +566,7 @@ func Parse(args []string) (string, error) { } buf.WriteString(fmt.Sprintf("Hex to String\t%s\n", fmt.Sprintf("%q", string(rawStr)))) buf.WriteString(fmt.Sprintf("Hex to Integer\t%s\n", bigint.FromBytes(rawStr))) - buf.WriteString(fmt.Sprintf("Swap Endianness\t%s\n", hex.EncodeToString(util.ArrayReverse(rawStr)))) + buf.WriteString(fmt.Sprintf("Swap Endianness\t%s\n", hex.EncodeToString(slice.CopyReverse(rawStr)))) } if addr, err := address.StringToUint160(arg); err == nil { buf.WriteString(fmt.Sprintf("Address to BE ScriptHash\t%s\n", addr)) From a54e3516d160c9b7292b23b907215ec69198f299 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Sun, 18 Jul 2021 16:08:23 +0300 Subject: [PATCH 3/4] slice: add Reverse function, deduplicate code a bit --- pkg/core/native/std.go | 12 +++--------- pkg/util/slice/array.go | 15 ++++++++++++--- pkg/util/slice/array_test.go | 3 +++ pkg/vm/vm.go | 7 +++---- 4 files changed, 21 insertions(+), 16 deletions(-) diff --git a/pkg/core/native/std.go b/pkg/core/native/std.go index f2e614b0a..2155883b5 100644 --- a/pkg/core/native/std.go +++ b/pkg/core/native/std.go @@ -16,6 +16,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" + "github.com/nspcc-dev/neo-go/pkg/util/slice" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) @@ -232,7 +233,7 @@ func (s *Std) itoa(_ *interop.Context, args []stackitem.Item) stackitem.Item { break } bs := bigint.ToBytes(num) - reverse(bs) + slice.Reverse(bs) str = hex.EncodeToString(bs) if pad := bs[0] & 0xF8; pad == 0 || pad == 0xF8 { str = str[1:] @@ -280,7 +281,7 @@ func (s *Std) atoi(_ *interop.Context, args []stackitem.Item) stackitem.Item { if changed && bs[0]&0x8 != 0 { bs[0] |= 0xF0 } - reverse(bs) + slice.Reverse(bs) bi = bigint.FromBytes(bs) default: panic(ErrInvalidBase) @@ -289,13 +290,6 @@ func (s *Std) atoi(_ *interop.Context, args []stackitem.Item) stackitem.Item { return stackitem.NewBigInteger(bi) } -func reverse(b []byte) { - l := len(b) - for i := 0; i < l/2; i++ { - b[i], b[l-i-1] = b[l-i-1], b[i] - } -} - func (s *Std) base64Encode(_ *interop.Context, args []stackitem.Item) stackitem.Item { src := s.toLimitedBytes(args[0]) result := base64.StdEncoding.EncodeToString(src) diff --git a/pkg/util/slice/array.go b/pkg/util/slice/array.go index 50e3f5d6c..3ade1a37f 100644 --- a/pkg/util/slice/array.go +++ b/pkg/util/slice/array.go @@ -7,8 +7,17 @@ package slice // original. func CopyReverse(b []byte) []byte { dest := make([]byte, len(b)) - for i, j := 0, len(b)-1; i <= j; i, j = i+1, j-1 { - dest[i], dest[j] = b[j], b[i] - } + reverse(dest, b) return dest } + +// Reverse does in-place reversing of byte slice. +func Reverse(b []byte) { + reverse(b, b) +} + +func reverse(dst []byte, src []byte) { + for i, j := 0, len(src)-1; i <= j; i, j = i+1, j-1 { + dst[i], dst[j] = src[j], src[i] + } +} diff --git a/pkg/util/slice/array_test.go b/pkg/util/slice/array_test.go index 7ba88dec9..5106b6f2f 100644 --- a/pkg/util/slice/array_test.go +++ b/pkg/util/slice/array_test.go @@ -41,5 +41,8 @@ func TestCopyReverse(t *testing.T) { have[i] = ^have[i] } require.Equal(t, tc.arr, arg) + + Reverse(arg) + require.Equal(t, tc.rev, arg) } } diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 9d996d7f8..106d5fb90 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -21,6 +21,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/smartcontract/nef" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "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/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) @@ -1167,10 +1168,8 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro a[i], a[j] = a[j], a[i] } case *stackitem.Buffer: - slice := t.Value().([]byte) - for i, j := 0, t.Len()-1; i < j; i, j = i+1, j-1 { - slice[i], slice[j] = slice[j], slice[i] - } + b := t.Value().([]byte) + slice.Reverse(b) default: panic(fmt.Sprintf("invalid item type %s", t)) } From 19717dd9a8bf283f04f44e5a09678dffb18893e2 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Sun, 18 Jul 2021 16:32:10 +0300 Subject: [PATCH 4/4] slice: introduce common Copy helper It's a bit more convenient to use. --- cli/wallet/legacy_test.go | 16 ++++++---------- pkg/core/dao/dao.go | 9 ++++----- pkg/core/mpt/helpers.go | 7 ------- pkg/core/mpt/proof.go | 7 ++++--- pkg/core/mpt/trie.go | 5 +++-- pkg/core/native/oracle.go | 5 ++--- pkg/core/storage/badgerdb_store.go | 7 +++---- pkg/core/storage/boltdb_store.go | 5 ++--- pkg/core/storage/memory_store.go | 5 +++-- pkg/core/storage/storeandbatch_test.go | 7 +++---- pkg/services/oracle/oracle.go | 8 +++----- pkg/util/slice/array.go | 7 +++++++ pkg/util/slice/array_test.go | 7 +++++-- pkg/vm/stackitem/item.go | 21 ++++++--------------- 14 files changed, 51 insertions(+), 65 deletions(-) diff --git a/cli/wallet/legacy_test.go b/cli/wallet/legacy_test.go index 7195bc81a..8a80ac00c 100644 --- a/cli/wallet/legacy_test.go +++ b/cli/wallet/legacy_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/nspcc-dev/neo-go/pkg/util/slice" "github.com/stretchr/testify/require" ) @@ -35,32 +36,27 @@ func TestParseMultisigContract(t *testing.T) { testParseMultisigContract(t, s, 1, pub) }) t.Run("bad, no check multisig", func(t *testing.T) { - sBad := make([]byte, len(s)) - copy(sBad, s) + sBad := slice.Copy(s) sBad[len(sBad)-1] ^= 0xFF testParseMultisigContract(t, sBad, 0) }) t.Run("bad, invalid number of keys", func(t *testing.T) { - sBad := make([]byte, len(s)) - copy(sBad, s) + sBad := slice.Copy(s) sBad[len(sBad)-2] = opPush1 + 1 testParseMultisigContract(t, sBad, 0) }) t.Run("bad, invalid first instruction", func(t *testing.T) { - sBad := make([]byte, len(s)) - copy(sBad, s) + sBad := slice.Copy(s) sBad[0] = 0xFF testParseMultisigContract(t, sBad, 0) }) t.Run("bad, invalid public key", func(t *testing.T) { - sBad := make([]byte, len(s)) - copy(sBad, s) + sBad := slice.Copy(s) sBad[2] = 0xFF testParseMultisigContract(t, sBad, 0) }) t.Run("bad, many sigs", func(t *testing.T) { - sBad := make([]byte, len(s)) - copy(sBad, s) + sBad := slice.Copy(s) sBad[0] = opPush1 + 1 testParseMultisigContract(t, sBad, 0) }) diff --git a/pkg/core/dao/dao.go b/pkg/core/dao/dao.go index 12e4ecfe7..96df67a27 100644 --- a/pkg/core/dao/dao.go +++ b/pkg/core/dao/dao.go @@ -15,6 +15,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/util/slice" ) // HasTransaction errors. @@ -317,11 +318,9 @@ func (dao *Simple) GetStorageItemsWithPrefix(id int32, prefix []byte) (map[strin saveToMap := func(k, v []byte) { // Cut prefix and hash. // Must copy here, #1468. - key := make([]byte, len(k)) - copy(key, k) - si := make(state.StorageItem, len(v)) - copy(si, v) - siMap[string(key)] = si + key := slice.Copy(k) + val := slice.Copy(v) + siMap[string(key)] = state.StorageItem(val) } dao.Seek(id, prefix, saveToMap) return siMap, nil diff --git a/pkg/core/mpt/helpers.go b/pkg/core/mpt/helpers.go index 03b4e3337..a7399d37d 100644 --- a/pkg/core/mpt/helpers.go +++ b/pkg/core/mpt/helpers.go @@ -31,13 +31,6 @@ func lcpMany(kv []keyValue) []byte { return p } -// copySlice is a helper for copying slice if needed. -func copySlice(a []byte) []byte { - b := make([]byte, len(a)) - copy(b, a) - return b -} - // toNibbles mangles path by splitting every byte into 2 containing low- and high- 4-byte part. func toNibbles(path []byte) []byte { result := make([]byte, len(path)*2) diff --git a/pkg/core/mpt/proof.go b/pkg/core/mpt/proof.go index 2948f46f9..2def5ac04 100644 --- a/pkg/core/mpt/proof.go +++ b/pkg/core/mpt/proof.go @@ -6,6 +6,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/storage" "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/util/slice" ) // GetProof returns a proof that key belongs to t. @@ -25,11 +26,11 @@ func (t *Trie) getProof(curr Node, path []byte, proofs *[][]byte) (Node, error) switch n := curr.(type) { case *LeafNode: if len(path) == 0 { - *proofs = append(*proofs, copySlice(n.Bytes())) + *proofs = append(*proofs, slice.Copy(n.Bytes())) return n, nil } case *BranchNode: - *proofs = append(*proofs, copySlice(n.Bytes())) + *proofs = append(*proofs, slice.Copy(n.Bytes())) i, path := splitPath(path) r, err := t.getProof(n.Children[i], path, proofs) if err != nil { @@ -39,7 +40,7 @@ func (t *Trie) getProof(curr Node, path []byte, proofs *[][]byte) (Node, error) return n, nil case *ExtensionNode: if bytes.HasPrefix(path, n.key) { - *proofs = append(*proofs, copySlice(n.Bytes())) + *proofs = append(*proofs, slice.Copy(n.Bytes())) r, err := t.getProof(n.next, path[len(n.key):], proofs) if err != nil { return nil, err diff --git a/pkg/core/mpt/trie.go b/pkg/core/mpt/trie.go index fea6141e6..a59f925e7 100644 --- a/pkg/core/mpt/trie.go +++ b/pkg/core/mpt/trie.go @@ -9,6 +9,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/storage" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/util/slice" ) // Trie is an MPT trie storing all key-value pairs. @@ -64,7 +65,7 @@ func (t *Trie) getWithPath(curr Node, path []byte) (Node, []byte, error) { switch n := curr.(type) { case *LeafNode: if len(path) == 0 { - return curr, copySlice(n.value), nil + return curr, slice.Copy(n.value), nil } case *BranchNode: i, path := splitPath(path) @@ -179,7 +180,7 @@ func (t *Trie) putIntoExtension(curr *ExtensionNode, path []byte, val Node) (Nod t.addRef(b.Hash(), b.bytes) if lp > 0 { - e := NewExtensionNode(copySlice(pref), b) + e := NewExtensionNode(slice.Copy(pref), b) t.addRef(e.Hash(), e.bytes) return e, nil } diff --git a/pkg/core/native/oracle.go b/pkg/core/native/oracle.go index 9f7ead2ac..c060a9739 100644 --- a/pkg/core/native/oracle.go +++ b/pkg/core/native/oracle.go @@ -27,6 +27,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "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/emit" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" @@ -135,9 +136,7 @@ func newOracle() *Oracle { // GetOracleResponseScript returns script for transaction with oracle response. func (o *Oracle) GetOracleResponseScript() []byte { - b := make([]byte, len(o.oracleScript)) - copy(b, o.oracleScript) - return b + return slice.Copy(o.oracleScript) } // OnPersist implements Contract interface. diff --git a/pkg/core/storage/badgerdb_store.go b/pkg/core/storage/badgerdb_store.go index 619ca592a..6f5e268e1 100644 --- a/pkg/core/storage/badgerdb_store.go +++ b/pkg/core/storage/badgerdb_store.go @@ -4,6 +4,7 @@ import ( "os" "github.com/dgraph-io/badger/v2" + "github.com/nspcc-dev/neo-go/pkg/util/slice" ) // BadgerDBOptions configuration for BadgerDB. @@ -32,10 +33,8 @@ func (b *BadgerDBBatch) Delete(key []byte) { // Put implements the Batch interface. func (b *BadgerDBBatch) Put(key, value []byte) { - keycopy := make([]byte, len(key)) - copy(keycopy, key) - valuecopy := make([]byte, len(value)) - copy(valuecopy, value) + keycopy := slice.Copy(key) + valuecopy := slice.Copy(value) err := b.batch.Set(keycopy, valuecopy) if err != nil { panic(err) diff --git a/pkg/core/storage/boltdb_store.go b/pkg/core/storage/boltdb_store.go index 3f6bfe233..23868c3f8 100644 --- a/pkg/core/storage/boltdb_store.go +++ b/pkg/core/storage/boltdb_store.go @@ -6,6 +6,7 @@ import ( "os" "github.com/nspcc-dev/neo-go/pkg/io" + "github.com/nspcc-dev/neo-go/pkg/util/slice" "github.com/syndtr/goleveldb/leveldb/util" "go.etcd.io/bbolt" ) @@ -63,9 +64,7 @@ func (s *BoltDBStore) Get(key []byte) (val []byte, err error) { val = b.Get(key) // Value from Get is only valid for the lifetime of transaction, #1482 if val != nil { - var valcopy = make([]byte, len(val)) - copy(valcopy, val) - val = valcopy + val = slice.Copy(val) } return nil }) diff --git a/pkg/core/storage/memory_store.go b/pkg/core/storage/memory_store.go index 0d3e9eae2..21ded2220 100644 --- a/pkg/core/storage/memory_store.go +++ b/pkg/core/storage/memory_store.go @@ -3,6 +3,8 @@ package storage import ( "strings" "sync" + + "github.com/nspcc-dev/neo-go/pkg/util/slice" ) // MemoryStore is an in-memory implementation of a Store, mainly @@ -57,8 +59,7 @@ func (s *MemoryStore) put(key string, value []byte) { // Put implements the Store interface. Never returns an error. func (s *MemoryStore) Put(key, value []byte) error { newKey := string(key) - vcopy := make([]byte, len(value)) - copy(vcopy, value) + vcopy := slice.Copy(value) s.mut.Lock() s.put(newKey, vcopy) s.mut.Unlock() diff --git a/pkg/core/storage/storeandbatch_test.go b/pkg/core/storage/storeandbatch_test.go index 81fa37365..022d83adb 100644 --- a/pkg/core/storage/storeandbatch_test.go +++ b/pkg/core/storage/storeandbatch_test.go @@ -5,6 +5,7 @@ import ( "runtime" "testing" + "github.com/nspcc-dev/neo-go/pkg/util/slice" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -55,10 +56,8 @@ func testStorePutBatch(t *testing.T, s Store) { batch = s.Batch() ) // Test that key and value are copied when batching. - keycopy := make([]byte, len(key)) - copy(keycopy, key) - valuecopy := make([]byte, len(value)) - copy(valuecopy, value) + keycopy := slice.Copy(key) + valuecopy := slice.Copy(value) batch.Put(keycopy, valuecopy) copy(valuecopy, key) diff --git a/pkg/services/oracle/oracle.go b/pkg/services/oracle/oracle.go index 8369add20..bf89d5741 100644 --- a/pkg/services/oracle/oracle.go +++ b/pkg/services/oracle/oracle.go @@ -14,6 +14,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/util" + "github.com/nspcc-dev/neo-go/pkg/util/slice" "github.com/nspcc-dev/neo-go/pkg/wallet" "go.uber.org/zap" ) @@ -211,11 +212,8 @@ func (o *Oracle) Run() { // UpdateNativeContract updates native oracle contract info for tx verification. func (o *Oracle) UpdateNativeContract(script, resp []byte, h util.Uint160, verifyOffset int) { - o.oracleScript = make([]byte, len(script)) - copy(o.oracleScript, script) - - o.oracleResponse = make([]byte, len(resp)) - copy(o.oracleResponse, resp) + o.oracleScript = slice.Copy(script) + o.oracleResponse = slice.Copy(resp) o.oracleHash = h o.verifyOffset = verifyOffset diff --git a/pkg/util/slice/array.go b/pkg/util/slice/array.go index 3ade1a37f..cfa759ccf 100644 --- a/pkg/util/slice/array.go +++ b/pkg/util/slice/array.go @@ -21,3 +21,10 @@ func reverse(dst []byte, src []byte) { dst[i], dst[j] = src[j], src[i] } } + +// Copy copies the byte slice into new slice (make/copy). +func Copy(b []byte) []byte { + d := make([]byte, len(b)) + copy(d, b) + return d +} diff --git a/pkg/util/slice/array_test.go b/pkg/util/slice/array_test.go index 5106b6f2f..535f634fc 100644 --- a/pkg/util/slice/array_test.go +++ b/pkg/util/slice/array_test.go @@ -30,8 +30,8 @@ var testCases = []struct { func TestCopyReverse(t *testing.T) { for _, tc := range testCases { - arg := make([]byte, len(tc.arr)) - copy(arg, tc.arr) + arg := Copy(tc.arr) + require.Equal(t, tc.arr, arg) have := CopyReverse(arg) require.Equal(t, tc.rev, have) @@ -44,5 +44,8 @@ func TestCopyReverse(t *testing.T) { Reverse(arg) require.Equal(t, tc.rev, arg) + if len(tc.arr) > 1 { + require.NotEqual(t, tc.arr, arg) + } } } diff --git a/pkg/vm/stackitem/item.go b/pkg/vm/stackitem/item.go index 3a156b22e..2f9e95ac1 100644 --- a/pkg/vm/stackitem/item.go +++ b/pkg/vm/stackitem/item.go @@ -15,6 +15,7 @@ import ( "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" + "github.com/nspcc-dev/neo-go/pkg/util/slice" ) const ( @@ -188,9 +189,7 @@ func convertPrimitive(item Item, typ Type) (Item, error) { return nil, err } if typ == BufferT { - newb := make([]byte, len(b)) - copy(newb, b) - return NewBuffer(newb), nil + return NewBuffer(slice.Copy(b)), nil } // ByteArray can't really be changed, so it's OK to reuse `b`. return NewByteArray(b), nil @@ -633,9 +632,7 @@ func (i *ByteArray) Equals(s Item) bool { // Dup implements Item interface. func (i *ByteArray) Dup() Item { - a := make([]byte, len(i.value)) - copy(a, i.value) - return &ByteArray{a} + return &ByteArray{slice.Copy(i.value)} } // Type implements Item interface. @@ -1110,9 +1107,7 @@ func (i *Buffer) Convert(typ Type) (Item, error) { case BufferT: return i, nil case ByteArrayT: - val := make([]byte, len(i.value)) - copy(val, i.value) - return NewByteArray(val), nil + return NewByteArray(slice.Copy(i.value)), nil case IntegerT: if len(i.value) > MaxBigIntegerSizeBits/8 { return nil, errTooBigInteger @@ -1173,13 +1168,9 @@ func deepCopy(item Item, seen map[Item]Item) Item { } return NewBigInteger(bi) case *ByteArray: - val := make([]byte, len(it.value)) - copy(val, it.value) - return NewByteArray(val) + return NewByteArray(slice.Copy(it.value)) case *Buffer: - val := make([]byte, len(it.value)) - copy(val, it.value) - return NewBuffer(val) + return NewBuffer(slice.Copy(it.value)) case *Bool: return NewBool(it.value) case *Pointer: