From 7ca280787548b6b5d6032b7dbef340f9c353d06a Mon Sep 17 00:00:00 2001 From: Anna Shaleva Date: Thu, 4 Jun 2020 21:11:27 +0300 Subject: [PATCH] vm/encoding: move bigint from vm to encoding package --- pkg/core/blockchain.go | 3 ++- pkg/core/native/native_nep5.go | 6 +++--- pkg/core/native/validators_count.go | 6 +++--- pkg/core/state/native_state.go | 6 +++--- pkg/{vm/emit => encoding/bigint}/bigint.go | 16 +++++++++------- pkg/{vm/emit => encoding/bigint}/bigint_test.go | 16 ++++++++-------- pkg/rpc/client/nep5.go | 3 ++- pkg/rpc/server/server.go | 4 ++-- pkg/vm/contract_checks.go | 3 ++- pkg/vm/emit/emit.go | 3 ++- pkg/vm/emit/emit_test.go | 9 +++++---- pkg/vm/json_test.go | 4 ++-- pkg/vm/stackitem/item.go | 8 ++++---- pkg/vm/stackitem/serialization.go | 6 +++--- pkg/vm/vm.go | 4 ++-- pkg/vm/vm_test.go | 5 +++-- 16 files changed, 55 insertions(+), 47 deletions(-) rename pkg/{vm/emit => encoding/bigint}/bigint.go (84%) rename pkg/{vm/emit => encoding/bigint}/bigint_test.go (97%) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 3286c64ac..982886bcc 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -18,6 +18,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/storage" "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/encoding/bigint" "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" @@ -606,7 +607,7 @@ func (bc *Blockchain) storeBlock(block *block.Block) error { if !ok { continue } - amount = emit.BytesToInt(bs) + amount = bigint.FromBytes(bs) } bc.processNEP5Transfer(cache, tx, block, note.ScriptHash, from, to, amount.Int64()) } diff --git a/pkg/core/native/native_nep5.go b/pkg/core/native/native_nep5.go index 8745c7fbd..66d9fbd3d 100644 --- a/pkg/core/native/native_nep5.go +++ b/pkg/core/native/native_nep5.go @@ -7,10 +7,10 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/core/state" + "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "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/stackitem" ) @@ -107,11 +107,11 @@ func (c *nep5TokenNative) getTotalSupply(ic *interop.Context) *big.Int { if si == nil { return big.NewInt(0) } - return emit.BytesToInt(si.Value) + return bigint.FromBytes(si.Value) } func (c *nep5TokenNative) saveTotalSupply(ic *interop.Context, supply *big.Int) error { - si := &state.StorageItem{Value: emit.IntToBytes(supply)} + si := &state.StorageItem{Value: bigint.ToBytes(supply)} return ic.DAO.PutStorageItem(c.Hash, totalSupplyKey, si) } diff --git a/pkg/core/native/validators_count.go b/pkg/core/native/validators_count.go index 25cfe3c7c..be551bfdb 100644 --- a/pkg/core/native/validators_count.go +++ b/pkg/core/native/validators_count.go @@ -3,8 +3,8 @@ package native import ( "math/big" + "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "github.com/nspcc-dev/neo-go/pkg/io" - "github.com/nspcc-dev/neo-go/pkg/vm/emit" ) // MaxValidatorsVoted limits the number of validators that one can vote for. @@ -42,7 +42,7 @@ func (vc *ValidatorsCount) Bytes() []byte { // EncodeBinary implements io.Serializable interface. func (vc *ValidatorsCount) EncodeBinary(w *io.BinWriter) { for i := range vc { - w.WriteVarBytes(emit.IntToBytes(&vc[i])) + w.WriteVarBytes(bigint.ToBytes(&vc[i])) } } @@ -53,7 +53,7 @@ func (vc *ValidatorsCount) DecodeBinary(r *io.BinReader) { if r.Err != nil { return } - vc[i] = *emit.BytesToInt(buf) + vc[i] = *bigint.FromBytes(buf) } } diff --git a/pkg/core/state/native_state.go b/pkg/core/state/native_state.go index f1377f460..9c8b3d915 100644 --- a/pkg/core/state/native_state.go +++ b/pkg/core/state/native_state.go @@ -4,8 +4,8 @@ import ( "math/big" "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/io" - "github.com/nspcc-dev/neo-go/pkg/vm/emit" ) // NEP5BalanceState represents balance state of a NEP5-token. @@ -46,7 +46,7 @@ func (s *NEP5BalanceState) Bytes() []byte { // EncodeBinary implements io.Serializable interface. func (s *NEP5BalanceState) EncodeBinary(w *io.BinWriter) { - w.WriteVarBytes(emit.IntToBytes(&s.Balance)) + w.WriteVarBytes(bigint.ToBytes(&s.Balance)) } // DecodeBinary implements io.Serializable interface. @@ -55,7 +55,7 @@ func (s *NEP5BalanceState) DecodeBinary(r *io.BinReader) { if r.Err != nil { return } - s.Balance = *emit.BytesToInt(buf) + s.Balance = *bigint.FromBytes(buf) } // NEOBalanceStateFromBytes converts serialized NEOBalanceState to structure. diff --git a/pkg/vm/emit/bigint.go b/pkg/encoding/bigint/bigint.go similarity index 84% rename from pkg/vm/emit/bigint.go rename to pkg/encoding/bigint/bigint.go index c3fc6fb18..33cdc00c2 100644 --- a/pkg/vm/emit/bigint.go +++ b/pkg/encoding/bigint/bigint.go @@ -1,4 +1,4 @@ -package emit +package bigint import ( "encoding/binary" @@ -9,9 +9,9 @@ import ( // wordSizeBytes is a size of a big.Word (uint) in bytes.` const wordSizeBytes = bits.UintSize / 8 -// BytesToInt converts data in little-endian format to +// FromBytes converts data in little-endian format to // an integer. -func BytesToInt(data []byte) *big.Int { +func FromBytes(data []byte) *big.Int { n := new(big.Int) size := len(data) if size == 0 { @@ -79,15 +79,17 @@ func getEffectiveSize(buf []byte, isNeg bool) int { return size } -// IntToBytes converts integer to a slice in little-endian format. +// ToBytes converts integer to a slice in little-endian format. // Note: NEO3 serialization differs from default C# BigInteger.ToByteArray() // when n == 0. For zero is equal to empty slice in NEO3. // https://github.com/neo-project/neo-vm/blob/master/src/neo-vm/Types/Integer.cs#L16 -func IntToBytes(n *big.Int) []byte { - return intToBytes(n, []byte{}) +func ToBytes(n *big.Int) []byte { + return ToPreallocatedBytes(n, []byte{}) } -func intToBytes(n *big.Int, data []byte) []byte { +// ToPreallocatedBytes converts integer to a slice in little-endian format using given +// byte array for conversion result. +func ToPreallocatedBytes(n *big.Int, data []byte) []byte { sign := n.Sign() if sign == 0 { return data diff --git a/pkg/vm/emit/bigint_test.go b/pkg/encoding/bigint/bigint_test.go similarity index 97% rename from pkg/vm/emit/bigint_test.go rename to pkg/encoding/bigint/bigint_test.go index 71ec2326a..7b0455c34 100644 --- a/pkg/vm/emit/bigint_test.go +++ b/pkg/encoding/bigint/bigint_test.go @@ -1,4 +1,4 @@ -package emit +package bigint import ( "math" @@ -106,19 +106,19 @@ var testCases = []struct { func TestIntToBytes(t *testing.T) { for _, tc := range testCases { - buf := IntToBytes(big.NewInt(tc.number)) + buf := ToBytes(big.NewInt(tc.number)) assert.Equal(t, tc.buf, buf, "error while converting %d", tc.number) } } func TestBytesToInt(t *testing.T) { for _, tc := range testCases { - num := BytesToInt(tc.buf) + num := FromBytes(tc.buf) assert.Equal(t, tc.number, num.Int64(), "error while converting %d", tc.number) } t.Run("empty array", func(t *testing.T) { - require.EqualValues(t, 0, BytesToInt([]byte{}).Int64()) + require.EqualValues(t, 0, FromBytes([]byte{}).Int64()) }) } @@ -131,7 +131,7 @@ func TestEquivalentRepresentations(t *testing.T) { buf = append(buf, 0xFF, 0xFF, 0xFF) } - num := BytesToInt(buf) + num := FromBytes(buf) assert.Equal(t, tc.number, num.Int64(), "error while converting %d", tc.number) } } @@ -170,16 +170,16 @@ func TestVeryBigInts(t *testing.T) { num, ok := new(big.Int).SetString(tc.numStr, 10) assert.True(t, ok) - result := BytesToInt(tc.buf) + result := FromBytes(tc.buf) assert.Equal(t, num, result, "error while converting %s from bytes", tc.numStr) - assert.Equal(t, tc.buf, IntToBytes(result), "error while converting %s to bytes", tc.numStr) + assert.Equal(t, tc.buf, ToBytes(result), "error while converting %s to bytes", tc.numStr) } for _, tc := range stdlibCases { num, ok := new(big.Int).SetString(tc.numStr, 10) assert.True(t, ok) - result := BytesToInt(util.ArrayReverse(tc.buf)) + result := FromBytes(util.ArrayReverse(tc.buf)) assert.Equal(t, num, result, "error while converting %s from bytes", tc.numStr) } } diff --git a/pkg/rpc/client/nep5.go b/pkg/rpc/client/nep5.go index 95fcf83ec..ee06343e3 100644 --- a/pkg/rpc/client/nep5.go +++ b/pkg/rpc/client/nep5.go @@ -7,6 +7,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/encoding/address" + "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" "github.com/nspcc-dev/neo-go/pkg/util" @@ -180,7 +181,7 @@ func topIntFromStack(st []smartcontract.Parameter) (int64, error) { if !ok { return 0, errors.New("invalid ByteArray item") } - decimals = emit.BytesToInt(data).Int64() + decimals = bigint.FromBytes(data).Int64() default: return 0, fmt.Errorf("invalid stack item type: %s", typ) } diff --git a/pkg/rpc/server/server.go b/pkg/rpc/server/server.go index dbed9ab8b..8006b7293 100644 --- a/pkg/rpc/server/server.go +++ b/pkg/rpc/server/server.go @@ -21,6 +21,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/encoding/address" + "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/network" "github.com/nspcc-dev/neo-go/pkg/rpc" @@ -29,7 +30,6 @@ import ( "github.com/nspcc-dev/neo-go/pkg/rpc/response/result" "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/util" - "github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/pkg/errors" "go.uber.org/zap" ) @@ -625,7 +625,7 @@ func (s *Server) getDecimals(h util.Uint160, cache map[util.Uint160]int64) (int6 case smartcontract.IntegerType: d = item.Value.(int64) case smartcontract.ByteArrayType: - d = emit.BytesToInt(item.Value.([]byte)).Int64() + d = bigint.FromBytes(item.Value.([]byte)).Int64() default: return 0, response.NewInternalServerError("invalid result", errors.New("not an integer")) } diff --git a/pkg/vm/contract_checks.go b/pkg/vm/contract_checks.go index e4acc3766..9622d2781 100644 --- a/pkg/vm/contract_checks.go +++ b/pkg/vm/contract_checks.go @@ -3,6 +3,7 @@ package vm import ( "encoding/binary" + "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" ) @@ -19,7 +20,7 @@ func getNumOfThingsFromInstr(instr opcode.Opcode, param []byte) (int, bool) { case opcode.PUSH1 <= instr && instr <= opcode.PUSH16: nthings = int(instr-opcode.PUSH1) + 1 case instr <= opcode.PUSHINT256: - n := emit.BytesToInt(param) + n := bigint.FromBytes(param) if !n.IsInt64() || n.Int64() > MaxArraySize { return 0, false } diff --git a/pkg/vm/emit/emit.go b/pkg/vm/emit/emit.go index b962efb99..e875f15e1 100644 --- a/pkg/vm/emit/emit.go +++ b/pkg/vm/emit/emit.go @@ -8,6 +8,7 @@ import ( "math/big" "math/bits" + "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" @@ -54,7 +55,7 @@ func Int(w *io.BinWriter, i int64) { val := opcode.Opcode(int(opcode.PUSH1) - 1 + int(i)) Opcode(w, val) default: - buf := intToBytes(big.NewInt(i), make([]byte, 0, 32)) + buf := bigint.ToPreallocatedBytes(big.NewInt(i), make([]byte, 0, 32)) // l != 0 becase of switch padSize := byte(8 - bits.LeadingZeros8(byte(len(buf)-1))) Opcode(w, opcode.PUSHINT8+opcode.Opcode(padSize)) diff --git a/pkg/vm/emit/emit_test.go b/pkg/vm/emit/emit_test.go index 8d76f4170..bb0024fb9 100644 --- a/pkg/vm/emit/emit_test.go +++ b/pkg/vm/emit/emit_test.go @@ -5,6 +5,7 @@ import ( "errors" "testing" + "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/stretchr/testify/assert" @@ -49,7 +50,7 @@ func TestEmitInt(t *testing.T) { result := buf.Bytes() assert.Equal(t, 3, len(result)) assert.EqualValues(t, opcode.PUSHINT16, result[0]) - assert.EqualValues(t, 300, BytesToInt(result[1:]).Int64()) + assert.EqualValues(t, 300, bigint.FromBytes(result[1:]).Int64()) }) t.Run("3-byte int", func(t *testing.T) { @@ -58,7 +59,7 @@ func TestEmitInt(t *testing.T) { result := buf.Bytes() assert.Equal(t, 5, len(result)) assert.EqualValues(t, opcode.PUSHINT32, result[0]) - assert.EqualValues(t, 1<<20, BytesToInt(result[1:]).Int64()) + assert.EqualValues(t, 1<<20, bigint.FromBytes(result[1:]).Int64()) }) t.Run("4-byte int", func(t *testing.T) { @@ -67,7 +68,7 @@ func TestEmitInt(t *testing.T) { result := buf.Bytes() assert.Equal(t, 5, len(result)) assert.EqualValues(t, opcode.PUSHINT32, result[0]) - assert.EqualValues(t, 1<<28, BytesToInt(result[1:]).Int64()) + assert.EqualValues(t, 1<<28, bigint.FromBytes(result[1:]).Int64()) }) t.Run("negative 3-byte int with padding", func(t *testing.T) { @@ -77,7 +78,7 @@ func TestEmitInt(t *testing.T) { result := buf.Bytes() assert.Equal(t, 5, len(result)) assert.EqualValues(t, opcode.PUSHINT32, result[0]) - assert.EqualValues(t, num, BytesToInt(result[1:]).Int64()) + assert.EqualValues(t, num, bigint.FromBytes(result[1:]).Int64()) }) } diff --git a/pkg/vm/json_test.go b/pkg/vm/json_test.go index d9abdd331..ee2823d49 100644 --- a/pkg/vm/json_test.go +++ b/pkg/vm/json_test.go @@ -16,7 +16,7 @@ import ( "strings" "testing" - "github.com/nspcc-dev/neo-go/pkg/vm/emit" + "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/stretchr/testify/require" @@ -183,7 +183,7 @@ func compareItems(t *testing.T, a, b stackitem.Item) { case *stackitem.BigInteger: require.Equal(t, val, ac.Value().(*big.Int).Int64()) case *stackitem.ByteArray: - require.Equal(t, val, emit.BytesToInt(ac.Value().([]byte)).Int64()) + require.Equal(t, val, bigint.FromBytes(ac.Value().([]byte)).Int64()) case *stackitem.Bool: if ac.Value().(bool) { require.Equal(t, val, int64(1)) diff --git a/pkg/vm/stackitem/item.go b/pkg/vm/stackitem/item.go index 734711632..07304b037 100644 --- a/pkg/vm/stackitem/item.go +++ b/pkg/vm/stackitem/item.go @@ -11,8 +11,8 @@ import ( "reflect" "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/vm/emit" ) // MaxBigIntegerSizeBits is the maximum size of BigInt item in bits. @@ -324,7 +324,7 @@ func NewBigInteger(value *big.Int) *BigInteger { // Bytes converts i to a slice of bytes. func (i *BigInteger) Bytes() []byte { - return emit.IntToBytes(i.value) + return bigint.ToBytes(i.value) } // Bool implements Item interface. @@ -514,7 +514,7 @@ func (i *ByteArray) TryInteger() (*big.Int, error) { if len(i.value) > MaxBigIntegerSizeBits/8 { return nil, errors.New("integer is too big") } - return emit.BytesToInt(i.value), nil + return bigint.FromBytes(i.value), nil } // Equals implements Item interface. @@ -998,7 +998,7 @@ func (i *Buffer) Convert(typ Type) (Item, error) { if len(i.value) > MaxBigIntegerSizeBits/8 { return nil, errInvalidConversion } - return NewBigInteger(emit.BytesToInt(i.value)), nil + return NewBigInteger(bigint.FromBytes(i.value)), nil default: return nil, errInvalidConversion } diff --git a/pkg/vm/stackitem/serialization.go b/pkg/vm/stackitem/serialization.go index a912358b2..e6cd51d90 100644 --- a/pkg/vm/stackitem/serialization.go +++ b/pkg/vm/stackitem/serialization.go @@ -4,8 +4,8 @@ import ( "errors" "math/big" + "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "github.com/nspcc-dev/neo-go/pkg/io" - "github.com/nspcc-dev/neo-go/pkg/vm/emit" ) // SerializeItem encodes given Item into the byte slice. @@ -43,7 +43,7 @@ func serializeItemTo(item Item, w *io.BinWriter, seen map[Item]bool) { w.WriteBool(t.Value().(bool)) case *BigInteger: w.WriteBytes([]byte{byte(IntegerT)}) - w.WriteVarBytes(emit.IntToBytes(t.Value().(*big.Int))) + w.WriteVarBytes(bigint.ToBytes(t.Value().(*big.Int))) case *Interop: w.Err = errors.New("interop item can't be serialized") case *Array, *Struct: @@ -102,7 +102,7 @@ func DecodeBinaryStackItem(r *io.BinReader) Item { return NewBool(b) case IntegerT: data := r.ReadVarBytes() - num := emit.BytesToInt(data) + num := bigint.FromBytes(data) return NewBigInteger(num) case ArrayT, StructT: size := int(r.ReadVarUint()) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index b17c786bb..6d0c36f29 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -12,8 +12,8 @@ import ( "unicode/utf8" "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/emit" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/pkg/errors" @@ -511,7 +511,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro } if op <= opcode.PUSHINT256 { - v.estack.PushVal(emit.BytesToInt(parameter)) + v.estack.PushVal(bigint.FromBytes(parameter)) return } diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index 45fc45c4a..bf7d75702 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -9,6 +9,7 @@ import ( "math/rand" "testing" + "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "github.com/nspcc-dev/neo-go/pkg/internal/random" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/util" @@ -181,7 +182,7 @@ func TestPUSHINT(t *testing.T) { t.Run(op.String(), func(t *testing.T) { buf := random.Bytes((8 << i) / 8) prog := append([]byte{byte(op)}, buf...) - runWithArgs(t, prog, emit.BytesToInt(buf)) + runWithArgs(t, prog, bigint.FromBytes(buf)) }) } } @@ -295,7 +296,7 @@ func TestCONVERT(t *testing.T) { t.Run("primitive -> Integer/ByteArray", func(t *testing.T) { n := big.NewInt(42) - b := emit.IntToBytes(n) + b := bigint.ToBytes(n) itemInt := stackitem.NewBigInteger(n) itemBytes := stackitem.NewByteArray(b)