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: