diff --git a/cli/server/dump.go b/cli/server/dump.go index ead0b3b33..e6084a9ee 100644 --- a/cli/server/dump.go +++ b/cli/server/dump.go @@ -9,7 +9,6 @@ import ( "path/filepath" "github.com/nspcc-dev/neo-go/pkg/core/storage" - "github.com/nspcc-dev/neo-go/pkg/util" ) type dump []blockDump @@ -26,26 +25,24 @@ type storageOp struct { Value string `json:"value,omitempty"` } -// NEO has some differences of key storing. -// out format: script hash in LE + key -// neo format: script hash in BE + byte(0) + key with 0 between every 16 bytes, padded to len 16. +// NEO has some differences of key storing (that should go away post-preview2). +// ours format: contract's ID (uint32le) + key +// theirs format: contract's ID (uint32le) + key with 16 between every 16 bytes, padded to len 16. func toNeoStorageKey(key []byte) []byte { - if len(key) < util.Uint160Size { + if len(key) < 4 { panic("invalid key in storage") } - var nkey []byte - for i := util.Uint160Size - 1; i >= 0; i-- { - nkey = append(nkey, key[i]) - } - - key = key[util.Uint160Size:] + // Prefix is a contract's ID in LE. + nkey := make([]byte, 4, len(key)) + copy(nkey, key[:4]) + key = key[4:] index := 0 remain := len(key) for remain >= 16 { nkey = append(nkey, key[index:index+16]...) - nkey = append(nkey, 0) + nkey = append(nkey, 16) index += 16 remain -= 16 } @@ -59,7 +56,7 @@ func toNeoStorageKey(key []byte) []byte { nkey = append(nkey, 0) } - nkey = append(nkey, byte(padding)) + nkey = append(nkey, byte(remain)) return nkey } @@ -84,7 +81,7 @@ func batchToMap(index uint32, batch *storage.MemBatch) blockDump { ops = append(ops, storageOp{ State: op, Key: hex.EncodeToString(key), - Value: "00" + hex.EncodeToString(batch.Put[i].Value), + Value: hex.EncodeToString(batch.Put[i].Value), }) } diff --git a/docs/compiler.md b/docs/compiler.md index 58711237b..56f5367e5 100644 --- a/docs/compiler.md +++ b/docs/compiler.md @@ -236,7 +236,11 @@ type Token struct { func (t Token) AddToCirculation(amount int) bool { ctx := storage.Context() - inCirc := storage.Get(ctx, "in_circ").(int) + var inCirc int + val := storage.Get(ctx, "in_circ") + if val != nil { + inCirc = val.(int) + } inCirc += amount storage.Put(ctx, "in_circ", inCirc) return true diff --git a/examples/token-sale/token_sale.go b/examples/token-sale/token_sale.go index 84732bad9..a9d5e7727 100644 --- a/examples/token-sale/token_sale.go +++ b/examples/token-sale/token_sale.go @@ -71,15 +71,25 @@ func NewTokenConfig() TokenConfig { } } +// getIntFromDB is a helper that checks for nil result of storage.Get and returns +// zero as the default value. +func getIntFromDB(ctx storage.Context, key []byte) int { + var res int + val := storage.Get(ctx, key) + if val != nil { + res = val.(int) + } + return res +} + // InCirculation return the amount of total tokens that are in circulation. func (t TokenConfig) InCirculation(ctx storage.Context) int { - amount := storage.Get(ctx, t.CirculationKey) - return amount.(int) + return getIntFromDB(ctx, t.CirculationKey) } // AddToCirculation sets the given amount as "in circulation" in the storage. func (t TokenConfig) AddToCirculation(ctx storage.Context, amount int) bool { - supply := storage.Get(ctx, t.CirculationKey).(int) + supply := getIntFromDB(ctx, t.CirculationKey) supply += amount storage.Put(ctx, t.CirculationKey, supply) return true @@ -88,8 +98,8 @@ func (t TokenConfig) AddToCirculation(ctx storage.Context, amount int) bool { // TokenSaleAvailableAmount returns the total amount of available tokens left // to be distributed. func (t TokenConfig) TokenSaleAvailableAmount(ctx storage.Context) int { - inCirc := storage.Get(ctx, t.CirculationKey) - return t.TotalSupply - inCirc.(int) + inCirc := getIntFromDB(ctx, t.CirculationKey) + return t.TotalSupply - inCirc } // Main smart contract entry point. @@ -128,11 +138,11 @@ func handleOperation(op string, args []interface{}, ctx storage.Context, cfg Tok return cfg.Symbol } if op == "totalSupply" { - return storage.Get(ctx, cfg.CirculationKey) + return getIntFromDB(ctx, cfg.CirculationKey) } if op == "balanceOf" { if len(args) == 1 { - return storage.Get(ctx, args[0].([]byte)) + return getIntFromDB(ctx, args[0].([]byte)) } } if op == "transfer" { @@ -177,7 +187,7 @@ func transfer(cfg TokenConfig, ctx storage.Context, from, to []byte, amount int) if amount <= 0 || len(to) != 20 || !runtime.CheckWitness(from) { return false } - amountFrom := storage.Get(ctx, from).(int) + amountFrom := getIntFromDB(ctx, from) if amountFrom < amount { return false } @@ -187,7 +197,7 @@ func transfer(cfg TokenConfig, ctx storage.Context, from, to []byte, amount int) diff := amountFrom - amount storage.Put(ctx, from, diff) } - amountTo := storage.Get(ctx, to).(int) + amountTo := getIntFromDB(ctx, to) totalAmountTo := amountTo + amount storage.Put(ctx, to, totalAmountTo) return true @@ -201,15 +211,15 @@ func transferFrom(cfg TokenConfig, ctx storage.Context, from, to []byte, amount if len(availableKey) != 40 { return false } - availableTo := storage.Get(ctx, availableKey).(int) + availableTo := getIntFromDB(ctx, availableKey) if availableTo < amount { return false } - fromBalance := storage.Get(ctx, from).(int) + fromBalance := getIntFromDB(ctx, from) if fromBalance < amount { return false } - toBalance := storage.Get(ctx, to).(int) + toBalance := getIntFromDB(ctx, to) newFromBalance := fromBalance - amount newToBalance := toBalance + amount storage.Put(ctx, to, newToBalance) @@ -231,7 +241,7 @@ func approve(ctx storage.Context, owner, spender []byte, amount int) bool { if len(spender) != 20 { return false } - toSpend := storage.Get(ctx, owner).(int) + toSpend := getIntFromDB(ctx, owner) if toSpend < amount { return false } @@ -246,5 +256,5 @@ func approve(ctx storage.Context, owner, spender []byte, amount int) bool { func allowance(ctx storage.Context, from, to []byte) int { key := append(from, to...) - return storage.Get(ctx, key).(int) + return getIntFromDB(ctx, key) } diff --git a/examples/token/nep5/nep5.go b/examples/token/nep5/nep5.go index 7082ad65b..89c92994c 100644 --- a/examples/token/nep5/nep5.go +++ b/examples/token/nep5/nep5.go @@ -22,14 +22,25 @@ type Token struct { CirculationKey string } +// getIntFromDB is a helper that checks for nil result of storage.Get and returns +// zero as the default value. +func getIntFromDB(ctx storage.Context, key []byte) int { + var res int + val := storage.Get(ctx, key) + if val != nil { + res = val.(int) + } + return res +} + // GetSupply gets the token totalSupply value from VM storage func (t Token) GetSupply(ctx storage.Context) interface{} { - return storage.Get(ctx, t.CirculationKey) + return getIntFromDB(ctx, []byte(t.CirculationKey)) } // BalanceOf gets the token balance of a specific address func (t Token) BalanceOf(ctx storage.Context, hodler []byte) interface{} { - return storage.Get(ctx, hodler) + return getIntFromDB(ctx, hodler) } // Transfer token from one user to another @@ -48,7 +59,7 @@ func (t Token) Transfer(ctx storage.Context, from []byte, to []byte, amount int) storage.Put(ctx, from, diff) } - amountTo := storage.Get(ctx, to).(int) + amountTo := getIntFromDB(ctx, to) totalAmountTo := amountTo + amount storage.Put(ctx, to, totalAmountTo) runtime.Notify("transfer", from, to, amount) @@ -61,7 +72,7 @@ func (t Token) CanTransfer(ctx storage.Context, from []byte, to []byte, amount i return -1 } - amountFrom := storage.Get(ctx, from).(int) + amountFrom := getIntFromDB(ctx, from) if amountFrom < amount { return -1 } @@ -98,8 +109,8 @@ func (t Token) Mint(ctx storage.Context, to []byte) bool { if !IsUsableAddress(t.Owner) { return false } - minted := storage.Get(ctx, []byte("minted")).(bool) - if minted { + minted := storage.Get(ctx, []byte("minted")) + if minted != nil && minted.(bool) == true { return false } diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index 96f9f87e5..b30513dda 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -659,8 +659,26 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { return nil } - ast.Walk(c, n.X) - ast.Walk(c, n.Y) + var checkForNull bool + + if isExprNil(n.X) { + checkForNull = true + } else { + ast.Walk(c, n.X) + } + if isExprNil(n.Y) { + checkForNull = true + } else { + ast.Walk(c, n.Y) + } + if checkForNull { + emit.Opcode(c.prog.BinWriter, opcode.ISNULL) + if n.Op == token.NEQ { + emit.Opcode(c.prog.BinWriter, opcode.NOT) + } + + return nil + } switch { case n.Op == token.ADD: diff --git a/pkg/compiler/nilcheck_test.go b/pkg/compiler/nilcheck_test.go new file mode 100644 index 000000000..54f36e037 --- /dev/null +++ b/pkg/compiler/nilcheck_test.go @@ -0,0 +1,55 @@ +package compiler_test + +import ( + "math/big" + "testing" +) + +var nilTestCases = []testCase{ + { + "nil check positive right", + ` + package foo + func Main() int { + var t interface{} + if t == nil { + return 1 + } + return 2 + } + `, + big.NewInt(1), + }, + { + "nil check negative right", + ` + package foo + func Main() int { + t := []byte{} + if t == nil { + return 1 + } + return 2 + } + `, + big.NewInt(2), + }, + { + "nil check positive left", + ` + package foo + func Main() int { + var t interface{} + if nil == t { + return 1 + } + return 2 + } + `, + big.NewInt(1), + }, +} + +func TestNil(t *testing.T) { + runTestCases(t, nilTestCases) +} diff --git a/pkg/consensus/consensus_test.go b/pkg/consensus/consensus_test.go index 6ad040cc5..196bdd5cf 100644 --- a/pkg/consensus/consensus_test.go +++ b/pkg/consensus/consensus_test.go @@ -213,7 +213,7 @@ func newTestService(t *testing.T) *service { } func getTestValidator(i int) (*privateKey, *publicKey) { - key := testchain.PrivateKey(i) + key := testchain.PrivateKeyByID(i) return &privateKey{PrivateKey: key}, &publicKey{PublicKey: key.PublicKey()} } @@ -241,9 +241,8 @@ func signTx(t *testing.T, feePerByte util.Fixed8, txs ...*transaction.Transactio validators := make([]*keys.PublicKey, 4) privNetKeys := make([]*keys.PrivateKey, 4) for i := 0; i < 4; i++ { - privateKey, publicKey := getTestValidator(i) - validators[i] = publicKey.PublicKey - privNetKeys[i] = privateKey.PrivateKey + privNetKeys[i] = testchain.PrivateKey(i) + validators[i] = privNetKeys[i].PublicKey() } rawScript, err := smartcontract.CreateMultiSigRedeemScript(3, validators) require.NoError(t, err) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 80f06efb9..56123da5e 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -54,7 +54,7 @@ var ( ErrInvalidBlockIndex error = errors.New("invalid block index") ) var ( - genAmount = []int{8, 7, 6, 5, 4, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} + genAmount = []int{6, 5, 4, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} decrementInterval = 2000000 persistInterval = 1 * time.Second ) @@ -117,6 +117,8 @@ type Blockchain struct { // cache for block verification keys. keyCache map[util.Uint160]map[string]*keys.PublicKey + sbValidators keys.PublicKeys + log *zap.Logger lastBatch *storage.MemBatch @@ -152,6 +154,10 @@ func NewBlockchain(s storage.Store, cfg config.ProtocolConfiguration, log *zap.L cfg.MemPoolSize = defaultMemPoolSize log.Info("mempool size is not set or wrong, setting default value", zap.Int("MemPoolSize", cfg.MemPoolSize)) } + validators, err := validatorsFromConfig(cfg) + if err != nil { + return nil, err + } bc := &Blockchain{ config: cfg, dao: dao.NewSimple(s, cfg.Magic), @@ -161,6 +167,7 @@ func NewBlockchain(s storage.Store, cfg config.ProtocolConfiguration, log *zap.L runToExitCh: make(chan struct{}), memPool: mempool.NewMemPool(cfg.MemPoolSize), keyCache: make(map[util.Uint160]map[string]*keys.PublicKey), + sbValidators: validators, log: log, events: make(chan bcEvent), subCh: make(chan interface{}), @@ -1235,8 +1242,10 @@ func (bc *Blockchain) PoolTx(t *transaction.Transaction) error { } //GetStandByValidators returns validators from the configuration. -func (bc *Blockchain) GetStandByValidators() (keys.PublicKeys, error) { - return getValidators(bc.config) +func (bc *Blockchain) GetStandByValidators() keys.PublicKeys { + res := make(keys.PublicKeys, len(bc.sbValidators)) + copy(res, bc.sbValidators) + return res } // GetValidators returns next block validators. diff --git a/pkg/core/blockchainer/blockchainer.go b/pkg/core/blockchainer/blockchainer.go index bbe30a972..ef667c51e 100644 --- a/pkg/core/blockchainer/blockchainer.go +++ b/pkg/core/blockchainer/blockchainer.go @@ -37,7 +37,7 @@ type Blockchainer interface { GetNEP5TransferLog(util.Uint160) *state.NEP5TransferLog GetNEP5Balances(util.Uint160) *state.NEP5Balances GetValidators() ([]*keys.PublicKey, error) - GetStandByValidators() (keys.PublicKeys, error) + GetStandByValidators() keys.PublicKeys GetScriptHashesForVerifying(*transaction.Transaction) ([]util.Uint160, error) GetStorageItem(id int32, key []byte) *state.StorageItem GetStorageItems(id int32) (map[string]*state.StorageItem, error) diff --git a/pkg/core/dao/dao.go b/pkg/core/dao/dao.go index 978121feb..f24c7267c 100644 --- a/pkg/core/dao/dao.go +++ b/pkg/core/dao/dao.go @@ -32,7 +32,7 @@ type DAO interface { GetHeaderHashes() ([]util.Uint256, error) GetNEP5Balances(acc util.Uint160) (*state.NEP5Balances, error) GetNEP5TransferLog(acc util.Uint160, index uint32) (*state.NEP5TransferLog, error) - GetNextContractID() (int32, error) + GetAndUpdateNextContractID() (int32, error) GetStorageItem(id int32, key []byte) *state.StorageItem GetStorageItems(id int32) (map[string]*state.StorageItem, error) GetStorageItemsWithPrefix(id int32, prefix []byte) (map[string]*state.StorageItem, error) @@ -47,7 +47,6 @@ type DAO interface { PutCurrentHeader(hashAndIndex []byte) error PutNEP5Balances(acc util.Uint160, bs *state.NEP5Balances) error PutNEP5TransferLog(acc util.Uint160, index uint32, lg *state.NEP5TransferLog) error - PutNextContractID(id int32) error PutStorageItem(id int32, key []byte, si *state.StorageItem) error PutVersion(v string) error StoreAsBlock(block *block.Block) error @@ -173,25 +172,19 @@ func (dao *Simple) DeleteContractState(hash util.Uint160) error { return dao.Store.Delete(key) } -// GetNextContractID returns id for the next contract and increases stored id. -func (dao *Simple) GetNextContractID() (int32, error) { +// GetAndUpdateNextContractID returns id for the next contract and increases stored ID. +func (dao *Simple) GetAndUpdateNextContractID() (int32, error) { + var id int32 key := storage.SYSContractID.Bytes() data, err := dao.Store.Get(key) - if err != nil { - if err == storage.ErrKeyNotFound { - err = nil - } + if err == nil { + id = int32(binary.LittleEndian.Uint32(data)) + } else if err != storage.ErrKeyNotFound { return 0, err } - return int32(binary.LittleEndian.Uint32(data)), nil -} - -// PutNextContractID sets next contract id to id. -func (dao *Simple) PutNextContractID(id int32) error { - key := storage.SYSContractID.Bytes() - data := make([]byte, 4) - binary.LittleEndian.PutUint32(data, uint32(id)) - return dao.Store.Put(key, data) + data = make([]byte, 4) + binary.LittleEndian.PutUint32(data, uint32(id+1)) + return id, dao.Store.Put(key, data) } // -- end contracts. diff --git a/pkg/core/dao/dao_test.go b/pkg/core/dao/dao_test.go index 2da5f7f70..a94649467 100644 --- a/pkg/core/dao/dao_test.go +++ b/pkg/core/dao/dao_test.go @@ -84,15 +84,17 @@ func TestDeleteContractState(t *testing.T) { require.Nil(t, gotContractState) } -func TestSimple_GetNextContractID(t *testing.T) { +func TestSimple_GetAndUpdateNextContractID(t *testing.T) { dao := NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet) - id, err := dao.GetNextContractID() + id, err := dao.GetAndUpdateNextContractID() require.NoError(t, err) require.EqualValues(t, 0, id) - require.NoError(t, dao.PutNextContractID(10)) - id, err = dao.GetNextContractID() + id, err = dao.GetAndUpdateNextContractID() require.NoError(t, err) - require.EqualValues(t, 10, id) + require.EqualValues(t, 1, id) + id, err = dao.GetAndUpdateNextContractID() + require.NoError(t, err) + require.EqualValues(t, 2, id) } func TestPutGetAppExecResult(t *testing.T) { diff --git a/pkg/core/helper_test.go b/pkg/core/helper_test.go index f7b840cc5..fb16b2f43 100644 --- a/pkg/core/helper_test.go +++ b/pkg/core/helper_test.go @@ -48,7 +48,7 @@ func (bc *Blockchain) newBlock(txs ...*transaction.Transaction) *block.Block { } func newBlock(cfg config.ProtocolConfiguration, index uint32, prev util.Uint256, txs ...*transaction.Transaction) *block.Block { - validators, _ := getValidators(cfg) + validators, _ := validatorsFromConfig(cfg) vlen := len(validators) valScript, _ := smartcontract.CreateMultiSigRedeemScript( vlen-(vlen-1)/3, @@ -210,7 +210,7 @@ func TestCreateBasicChain(t *testing.T) { t.Logf("txMoveNeo: %s", txMoveNeo.Hash().StringLE()) t.Logf("txMoveGas: %s", txMoveGas.Hash().StringLE()) - require.Equal(t, util.Fixed8FromInt64(1000), bc.GetUtilityTokenBalance(priv0ScriptHash)) + require.True(t, util.Fixed8FromInt64(1000).CompareTo(bc.GetUtilityTokenBalance(priv0ScriptHash)) <= 0) // info for getblockheader rpc tests t.Logf("header hash: %s", b.Hash().StringLE()) buf := io.NewBufBinWriter() @@ -399,10 +399,7 @@ func addCosigners(txs ...*transaction.Transaction) { } func signTx(bc *Blockchain, txs ...*transaction.Transaction) error { - validators, err := getValidators(bc.config) - if err != nil { - return errors.Wrap(err, "fail to sign tx") - } + validators := bc.GetStandByValidators() rawScript, err := smartcontract.CreateMultiSigRedeemScript(len(bc.config.StandbyValidators)/2+1, validators) if err != nil { return errors.Wrap(err, "fail to sign tx") diff --git a/pkg/core/interop_neo.go b/pkg/core/interop_neo.go index a2ad11d09..0c77bbc23 100644 --- a/pkg/core/interop_neo.go +++ b/pkg/core/interop_neo.go @@ -91,14 +91,11 @@ func contractCreate(ic *interop.Context, v *vm.VM) error { if contract != nil { return errors.New("contract already exists") } - id, err := ic.DAO.GetNextContractID() + id, err := ic.DAO.GetAndUpdateNextContractID() if err != nil { return err } newcontract.ID = id - if err := ic.DAO.PutNextContractID(id); err != nil { - return err - } if err := ic.DAO.PutContractState(newcontract); err != nil { return err } diff --git a/pkg/core/interop_system.go b/pkg/core/interop_system.go index f51d6fceb..66fd0e478 100644 --- a/pkg/core/interop_system.go +++ b/pkg/core/interop_system.go @@ -289,7 +289,7 @@ func storageGet(ic *interop.Context, v *vm.VM) error { if si != nil && si.Value != nil { v.Estack().PushVal(si.Value) } else { - v.Estack().PushVal([]byte{}) + v.Estack().PushVal(stackitem.Null{}) } return nil } diff --git a/pkg/core/native/native_gas.go b/pkg/core/native/native_gas.go index 2e1e57ada..08683863a 100644 --- a/pkg/core/native/native_gas.go +++ b/pkg/core/native/native_gas.go @@ -58,7 +58,11 @@ func (g *GAS) increaseBalance(_ *interop.Context, _ util.Uint160, si *state.Stor return errors.New("insufficient funds") } acc.Balance.Add(&acc.Balance, amount) - si.Value = acc.Bytes() + if acc.Balance.Sign() != 0 { + si.Value = acc.Bytes() + } else { + si.Value = nil + } return nil } @@ -87,7 +91,7 @@ func (g *GAS) OnPersist(ic *interop.Context) error { absAmount := big.NewInt(int64(tx.SystemFee + tx.NetworkFee)) g.burn(ic, tx.Sender, absAmount) } - validators, err := g.NEO.GetValidatorsInternal(ic.Chain, ic.DAO) + validators, err := g.NEO.GetNextBlockValidatorsInternal(ic.Chain, ic.DAO) if err != nil { return fmt.Errorf("cannot get block validators: %v", err) } @@ -101,10 +105,7 @@ func (g *GAS) OnPersist(ic *interop.Context) error { } func getStandbyValidatorsHash(ic *interop.Context) (util.Uint160, []*keys.PublicKey, error) { - vs, err := ic.Chain.GetStandByValidators() - if err != nil { - return util.Uint160{}, nil, err - } + vs := ic.Chain.GetStandByValidators() s, err := smartcontract.CreateMultiSigRedeemScript(len(vs)/2+1, vs) if err != nil { return util.Uint160{}, nil, err diff --git a/pkg/core/native/native_neo.go b/pkg/core/native/native_neo.go index e09481bc1..10eb38ac8 100644 --- a/pkg/core/native/native_neo.go +++ b/pkg/core/native/native_neo.go @@ -11,6 +11,7 @@ import ( "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/crypto/keys" + "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" @@ -112,8 +113,6 @@ func NewNEO() *NEO { // Initialize initializes NEO contract. func (n *NEO) Initialize(ic *interop.Context) error { - var si state.StorageItem - if err := n.nep5TokenNative.Initialize(ic); err != nil { return err } @@ -122,11 +121,6 @@ func (n *NEO) Initialize(ic *interop.Context) error { return errors.New("already initialized") } - vc := new(ValidatorsCount) - si.Value = vc.Bytes() - if err := ic.DAO.PutStorageItem(n.ContractID, validatorsCountKey, &si); err != nil { - return err - } h, vs, err := getStandbyValidatorsHash(ic) if err != nil { return err @@ -165,10 +159,11 @@ func (n *NEO) increaseBalance(ic *interop.Context, h util.Uint160, si *state.Sto return err } if amount.Sign() == 0 { + si.Value = acc.Bytes() return nil } if len(acc.Votes) > 0 { - if err := n.ModifyAccountVotes(acc, ic.DAO, new(big.Int).Neg(&acc.Balance)); err != nil { + if err := n.ModifyAccountVotes(acc, ic.DAO, amount); err != nil { return err } siVC := ic.DAO.GetStorageItem(n.ContractID, validatorsCountKey) @@ -186,7 +181,11 @@ func (n *NEO) increaseBalance(ic *interop.Context, h util.Uint160, si *state.Sto } } acc.Balance.Add(&acc.Balance, amount) - si.Value = acc.Bytes() + if acc.Balance.Sign() != 0 { + si.Value = acc.Bytes() + } else { + si.Value = nil + } return nil } @@ -225,10 +224,8 @@ func (n *NEO) registerValidatorInternal(ic *interop.Context, pub *keys.PublicKey return errors.New("already registered") } si = new(state.StorageItem) - // It's the same simple counter, calling it `Votes` instead of `Balance` - // doesn't help a lot. - votes := state.NEP5BalanceState{} - si.Value = votes.Bytes() + // Zero value. + si.Value = []byte{} return ic.DAO.PutStorageItem(n.ContractID, key, si) } @@ -284,13 +281,20 @@ func (n *NEO) VoteInternal(ic *interop.Context, h util.Uint160, pubs keys.Public newPubs = append(newPubs, pub) } if lp, lv := len(newPubs), len(acc.Votes); lp != lv { - si := ic.DAO.GetStorageItem(n.ContractID, validatorsCountKey) + var si *state.StorageItem + var vc *ValidatorsCount + var err error + + si = ic.DAO.GetStorageItem(n.ContractID, validatorsCountKey) if si == nil { - return errors.New("validators count uninitialized") - } - vc, err := ValidatorsCountFromBytes(si.Value) - if err != nil { - return err + // The first voter. + si = new(state.StorageItem) + vc = new(ValidatorsCount) + } else { + vc, err = ValidatorsCountFromBytes(si.Value) + if err != nil { + return err + } } if lv > 0 { vc[lv-1].Sub(&vc[lv-1], &acc.Balance) @@ -319,12 +323,9 @@ func (n *NEO) ModifyAccountVotes(acc *state.NEOBalanceState, d dao.DAO, value *b if si == nil { return errors.New("invalid validator") } - votes, err := state.NEP5BalanceStateFromBytes(si.Value) - if err != nil { - return err - } - votes.Balance.Add(&votes.Balance, value) - si.Value = votes.Bytes() + votes := bigint.FromBytes(si.Value) + votes.Add(votes, value) + si.Value = bigint.ToPreallocatedBytes(votes, si.Value[:0]) if err := d.PutStorageItem(n.ContractID, key, si); err != nil { return err } @@ -339,11 +340,8 @@ func (n *NEO) getRegisteredValidators(d dao.DAO) ([]keyWithVotes, error) { } arr := make([]keyWithVotes, 0, len(siMap)) for key, si := range siMap { - votes, err := state.NEP5BalanceStateFromBytes(si.Value) - if err != nil { - return nil, err - } - arr = append(arr, keyWithVotes{key, &votes.Balance}) + votes := bigint.FromBytes(si.Value) + arr = append(arr, keyWithVotes{key, votes}) } sort.Slice(arr, func(i, j int) bool { return strings.Compare(arr[i].Key, arr[j].Key) == -1 }) return arr, nil @@ -384,9 +382,10 @@ func (n *NEO) getRegisteredValidatorsCall(ic *interop.Context, _ []stackitem.Ite // GetValidatorsInternal returns a list of current validators. func (n *NEO) GetValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) (keys.PublicKeys, error) { + standByValidators := bc.GetStandByValidators() si := d.GetStorageItem(n.ContractID, validatorsCountKey) if si == nil { - return nil, errors.New("validators count uninitialized") + return standByValidators, nil } validatorsCount, err := ValidatorsCountFromBytes(si.Value) if err != nil { @@ -407,10 +406,6 @@ func (n *NEO) GetValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) (ke }) count := validatorsCount.GetWeightedAverage() - standByValidators, err := bc.GetStandByValidators() - if err != nil { - return nil, err - } if count < len(standByValidators) { count = len(standByValidators) } diff --git a/pkg/core/native/native_nep5.go b/pkg/core/native/native_nep5.go index 597a407e0..5fa0a74b3 100644 --- a/pkg/core/native/native_nep5.go +++ b/pkg/core/native/native_nep5.go @@ -148,6 +148,28 @@ func (c *nep5TokenNative) emitTransfer(ic *interop.Context, from, to *util.Uint1 ic.Notifications = append(ic.Notifications, ne) } +func (c *nep5TokenNative) updateAccBalance(ic *interop.Context, acc util.Uint160, amount *big.Int) error { + key := makeAccountKey(acc) + si := ic.DAO.GetStorageItem(c.ContractID, key) + if si == nil { + if amount.Sign() <= 0 { + return errors.New("insufficient funds") + } + si = new(state.StorageItem) + } + + err := c.incBalance(ic, acc, si, amount) + if err != nil { + return err + } + if si.Value == nil { + err = ic.DAO.DeleteStorageItem(c.ContractID, key) + } else { + err = ic.DAO.PutStorageItem(c.ContractID, key, si) + } + return err +} + func (c *nep5TokenNative) transfer(ic *interop.Context, from, to util.Uint160, amount *big.Int) error { if amount.Sign() == -1 { return errors.New("negative amount") @@ -164,12 +186,6 @@ func (c *nep5TokenNative) transfer(ic *interop.Context, from, to util.Uint160, a return errors.New("invalid signature") } - keyFrom := makeAccountKey(from) - siFrom := ic.DAO.GetStorageItem(c.ContractID, keyFrom) - if siFrom == nil { - return errors.New("insufficient funds") - } - isEmpty := from.Equals(to) || amount.Sign() == 0 inc := amount if isEmpty { @@ -177,23 +193,12 @@ func (c *nep5TokenNative) transfer(ic *interop.Context, from, to util.Uint160, a } else { inc = new(big.Int).Neg(inc) } - if err := c.incBalance(ic, from, siFrom, inc); err != nil { - return err - } - if err := ic.DAO.PutStorageItem(c.ContractID, keyFrom, siFrom); err != nil { + if err := c.updateAccBalance(ic, from, inc); err != nil { return err } if !isEmpty { - keyTo := makeAccountKey(to) - siTo := ic.DAO.GetStorageItem(c.ContractID, keyTo) - if siTo == nil { - siTo = new(state.StorageItem) - } - if err := c.incBalance(ic, to, siTo, amount); err != nil { - return err - } - if err := ic.DAO.PutStorageItem(c.ContractID, keyTo, siTo); err != nil { + if err := c.updateAccBalance(ic, to, amount); err != nil { return err } } diff --git a/pkg/core/state/native_state.go b/pkg/core/state/native_state.go index bb1d94ebf..bfb9fd10e 100644 --- a/pkg/core/state/native_state.go +++ b/pkg/core/state/native_state.go @@ -104,28 +104,20 @@ func (s *NEOBalanceState) DecodeBinary(r *io.BinReader) { if r.Err != nil { return } - s.fromStackItem(si) + r.Err = s.fromStackItem(si) } func (s *NEOBalanceState) toStackItem() stackitem.Item { result := s.NEP5BalanceState.toStackItem().(*stackitem.Struct) result.Append(stackitem.NewBigInteger(big.NewInt(int64(s.BalanceHeight)))) - votes := make([]stackitem.Item, len(s.Votes)) - for i, v := range s.Votes { - votes[i] = stackitem.NewByteArray(v.Bytes()) - } - result.Append(stackitem.NewArray(votes)) + result.Append(stackitem.NewByteArray(s.Votes.Bytes())) return result } -func (s *NEOBalanceState) fromStackItem(item stackitem.Item) { +func (s *NEOBalanceState) fromStackItem(item stackitem.Item) error { structItem := item.Value().([]stackitem.Item) s.Balance = *structItem[0].Value().(*big.Int) s.BalanceHeight = uint32(structItem[1].Value().(*big.Int).Int64()) - votes := structItem[2].Value().([]stackitem.Item) - s.Votes = make([]*keys.PublicKey, len(votes)) - for i, v := range votes { - s.Votes[i] = new(keys.PublicKey) - s.Votes[i].DecodeBytes(v.Value().([]byte)) - } + s.Votes = make(keys.PublicKeys, 0) + return s.Votes.DecodeBytes(structItem[2].Value().([]byte)) } diff --git a/pkg/core/transaction/transaction.go b/pkg/core/transaction/transaction.go index ce4f1a46a..96c0864d1 100644 --- a/pkg/core/transaction/transaction.go +++ b/pkg/core/transaction/transaction.go @@ -284,7 +284,7 @@ func (t *Transaction) FeePerByte() util.Fixed8 { // transactionJSON is a wrapper for Transaction and // used for correct marhalling of transaction.Data type transactionJSON struct { - TxID util.Uint256 `json:"txid"` + TxID util.Uint256 `json:"hash"` Size int `json:"size"` Version uint8 `json:"version"` Nonce uint32 `json:"nonce"` diff --git a/pkg/core/util.go b/pkg/core/util.go index de081004f..339422900 100644 --- a/pkg/core/util.go +++ b/pkg/core/util.go @@ -34,7 +34,7 @@ var ( // createGenesisBlock creates a genesis block based on the given configuration. func createGenesisBlock(cfg config.ProtocolConfiguration) (*block.Block, error) { - validators, err := getValidators(cfg) + validators, err := validatorsFromConfig(cfg) if err != nil { return nil, err } @@ -91,7 +91,7 @@ func deployNativeContracts(magic netmode.Magic) *transaction.Transaction { return tx } -func getValidators(cfg config.ProtocolConfiguration) ([]*keys.PublicKey, error) { +func validatorsFromConfig(cfg config.ProtocolConfiguration) ([]*keys.PublicKey, error) { validators := make([]*keys.PublicKey, len(cfg.StandbyValidators)) for i, pubKeyStr := range cfg.StandbyValidators { pubKey, err := keys.NewPublicKeyFromString(pubKeyStr) diff --git a/pkg/core/util_test.go b/pkg/core/util_test.go index 9d5002742..de438e42c 100644 --- a/pkg/core/util_test.go +++ b/pkg/core/util_test.go @@ -34,7 +34,7 @@ func TestGetConsensusAddressMainNet(t *testing.T) { cfg, err := config.Load("../../config", netmode.MainNet) require.NoError(t, err) - validators, err := getValidators(cfg.ProtocolConfiguration) + validators, err := validatorsFromConfig(cfg.ProtocolConfiguration) require.NoError(t, err) script, err := getNextConsensusAddress(validators) diff --git a/pkg/interop/storage/storage.go b/pkg/interop/storage/storage.go index 748157ef6..eb6e1faa6 100644 --- a/pkg/interop/storage/storage.go +++ b/pkg/interop/storage/storage.go @@ -38,9 +38,9 @@ func GetReadOnlyContext() Context { return Context{} } func Put(ctx Context, key interface{}, value interface{}) {} // Get retrieves value stored for the given key using given Context. See Put -// documentation on possible key and value types. This function uses -// `System.Storage.Get` syscall. -func Get(ctx Context, key interface{}) interface{} { return 0 } +// documentation on possible key and value types. If the value is not present in +// the database it returns nil. This function uses `System.Storage.Get` syscall. +func Get(ctx Context, key interface{}) interface{} { return nil } // Delete removes key-value pair from storage by the given key using given // Context. See Put documentation on possible key types. This function uses diff --git a/pkg/network/helper_test.go b/pkg/network/helper_test.go index cf7f39e2a..e09ae2727 100644 --- a/pkg/network/helper_test.go +++ b/pkg/network/helper_test.go @@ -85,7 +85,7 @@ func (chain testChain) GetNEP5Balances(util.Uint160) *state.NEP5Balances { func (chain testChain) GetValidators() ([]*keys.PublicKey, error) { panic("TODO") } -func (chain testChain) GetStandByValidators() (keys.PublicKeys, error) { +func (chain testChain) GetStandByValidators() keys.PublicKeys { panic("TODO") } func (chain testChain) GetEnrollments() ([]state.Validator, error) { diff --git a/pkg/rpc/client/rpc_test.go b/pkg/rpc/client/rpc_test.go index d2749af36..8995948de 100644 --- a/pkg/rpc/client/rpc_test.go +++ b/pkg/rpc/client/rpc_test.go @@ -41,13 +41,13 @@ const hexB1 = "000000008aaab19c43c4ca2870c3e616b123f1b689866f44b138ae4d6a5352e54 const hexTxMoveNeo = "0002000000abec5362f11e75b6e02e407bb98d63675d14384100000000000000003e5f0d0000000000b00400000001abec5362f11e75b6e02e407bb98d63675d14384101590218ddf5050c14316e851039019d39dfc2c37d6c3fee19fd5809870c14abec5362f11e75b6e02e407bb98d63675d14384113c00c087472616e736665720c14897720d8cd76f4f00abfa37c0edd889c208fde9b41627d5b523801fd08010c40ae6fc04fe4b6c22218ca9617c98d607d9ec9b1faf8cfdc3391bec485ae76b11adc6cc6abeb31a50b536ea8073e674d62a5566fce5e0a0ceb0718cb971c1ae3d00c40603071b725a58d052cad7afd88e99b27baab931afd5bb50d16e224335aab450170aabe251d3c0c6ad3f31dd7e9b89b209baabe5a1e2fa588bd8118f9e2a6960f0c40d72afcf39e663dba2d70fb8c36a09d1a6a6ad0d2fd38c857a8e7dc71e2b98711324e0d2ec641fe6896ba63ba80d3ea341c1aad11e082fb188ee07e215b4031b10c409afb2808b60286a56343b7ffcef28bb2ab0c595603e7323b5e5b0b9c1c10edfa5c40754d921865cb6fd71668a206b37a1eb10c0029a9fcd3a856aed07742cd3f94130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb" -const b1Verbose = `{"id":5,"jsonrpc":"2.0","result":{"size":1681,"nextblockhash":"0x45f62d72e37b074ecdc9f687222b0f91ec98d6d9af4429a2caa2e076a9196b0d","confirmations":6,"hash":"0x4f2c5539b0213ea444608cc217c5cb191255c1858ccd051ad9a36f08df26a288","version":0,"previousblockhash":"0x56fd4244e552536a4dae38b1446f8689b6f123b116e6c37028cac4439cb1aa8a","merkleroot":"0xf9dce467385206ad220a8b85d238f77239766eaffffed19955e3e47d24071140","time":1592472500001,"index":1,"nextconsensus":"Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2","witnesses":[{"invocation":"DEAPAetjcaE1Un3bIFoNrh9p0tMkg3zOEovq2QMwkYg/nOIebXWaQPaQdGWSsCHTl/CI47e0F/zvc80b/cKAB2lSDECE965dn1igmqVu6y8oJ0SlmoLRfjvg6xx/VObo33liDiYIGRusVygKg22y7Cp3bQfkPxa8dsR9NIsPzQnVbHwyDEArlbTjnsNRYqZAeV/yI+kt7JU5CgcutFf2pDIwUvgHMVF+DfAp5KRXIE93f1JhxqSojUbUw6vexjWm7tWA1sd/DEC5c1dFsd15WiWMMdjm+ofVz8Lps6SJDWENM7z4M7ZMWLDFzqF/OnEo8QZe0ZPmNpcVkPGT8ovdHu67vB/m587l","verification":"EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBMHOzuw=="}],"consensus_data":{"primary":0,"nonce":"0000000000000457"},"tx":[{"txid":"0x7fb05b593cf4b1eb2d9a283c5488ca1bfe61191b5775bafa43b8647e7b20f22c","size":575,"version":0,"nonce":2,"sender":"Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2","sys_fee":"0","net_fee":"0.0087635","valid_until_block":1200,"attributes":[],"cosigners":[{"account":"0x4138145d67638db97b402ee0b6751ef16253ecab","scopes":"CalledByEntry"}],"script":"Ahjd9QUMFDFuhRA5AZ0538LDfWw/7hn9WAmHDBSr7FNi8R51tuAuQHu5jWNnXRQ4QRPADAh0cmFuc2ZlcgwUiXcg2M129PAKv6N8Dt2InCCP3ptBYn1bUjg=","scripts":[{"invocation":"DECub8BP5LbCIhjKlhfJjWB9nsmx+vjP3DORvsSFrnaxGtxsxqvrMaULU26oBz5nTWKlVm/OXgoM6wcYy5ccGuPQDEBgMHG3JaWNBSytev2I6ZsnuquTGv1btQ0W4iQzWqtFAXCqviUdPAxq0/Md1+m4myCbqr5aHi+liL2BGPnippYPDEDXKvzznmY9ui1w+4w2oJ0aamrQ0v04yFeo59xx4rmHETJODS7GQf5olrpjuoDT6jQcGq0R4IL7GI7gfiFbQDGxDECa+ygItgKGpWNDt//O8ouyqwxZVgPnMjteWwucHBDt+lxAdU2SGGXLb9cWaKIGs3oesQwAKan806hWrtB3Qs0/","verification":"EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBMHOzuw=="}]},{"txid":"0xb661d5e4d9e41c3059b068f8abb6f1566a47ec800879e34c0ebd2799781a2760","size":579,"version":0,"nonce":3,"sender":"Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2","sys_fee":"0","net_fee":"0.0088035","valid_until_block":1200,"attributes":[],"cosigners":[{"account":"0x4138145d67638db97b402ee0b6751ef16253ecab","scopes":"CalledByEntry"}],"script":"AwDodkgXAAAADBQxboUQOQGdOd/Cw31sP+4Z/VgJhwwUq+xTYvEedbbgLkB7uY1jZ10UOEETwAwIdHJhbnNmZXIMFDt9NxHG8Mz5sdypA9G/odiW8SOMQWJ9W1I4","scripts":[{"invocation":"DECHEOe12Kxs2NCdb7Nb8tzOX1+zhZXdttBKVwvJJbwsVac83188taH+sKzE8myLvKbEPfO0qYuMPCyAnC8JbrJaDEDBhsECy/cjE/2U31B3/Fu+/NMiJ+0hWaR6RllId/o58zDYIjtFqiSv8AW+u1skJ6UMjE3oYY59ewDXPYNsRJQuDECiQ7W1Zb1LwvC7ES92JPazUUwSQTxalSMIGfrOP3YPA/y5axiPmAOKPyUWhrU6iNaXRPTkqYXmKXADqAzbFp6ADEBKlR5hrJnV7jGDHREXVK23EbSpBgqVJP44OpB3GEPNsJY4JnQCeofyoVqDvXfesrKrH+iz3m5UYpPvPfmxEp4o","verification":"EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBMHOzuw=="}]}]}}` +const b1Verbose = `{"id":5,"jsonrpc":"2.0","result":{"size":1681,"nextblockhash":"0x45f62d72e37b074ecdc9f687222b0f91ec98d6d9af4429a2caa2e076a9196b0d","confirmations":6,"hash":"0x4f2c5539b0213ea444608cc217c5cb191255c1858ccd051ad9a36f08df26a288","version":0,"previousblockhash":"0x56fd4244e552536a4dae38b1446f8689b6f123b116e6c37028cac4439cb1aa8a","merkleroot":"0xf9dce467385206ad220a8b85d238f77239766eaffffed19955e3e47d24071140","time":1592472500001,"index":1,"nextconsensus":"Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2","witnesses":[{"invocation":"DEAPAetjcaE1Un3bIFoNrh9p0tMkg3zOEovq2QMwkYg/nOIebXWaQPaQdGWSsCHTl/CI47e0F/zvc80b/cKAB2lSDECE965dn1igmqVu6y8oJ0SlmoLRfjvg6xx/VObo33liDiYIGRusVygKg22y7Cp3bQfkPxa8dsR9NIsPzQnVbHwyDEArlbTjnsNRYqZAeV/yI+kt7JU5CgcutFf2pDIwUvgHMVF+DfAp5KRXIE93f1JhxqSojUbUw6vexjWm7tWA1sd/DEC5c1dFsd15WiWMMdjm+ofVz8Lps6SJDWENM7z4M7ZMWLDFzqF/OnEo8QZe0ZPmNpcVkPGT8ovdHu67vB/m587l","verification":"EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBMHOzuw=="}],"consensus_data":{"primary":0,"nonce":"0000000000000457"},"tx":[{"hash":"0x7fb05b593cf4b1eb2d9a283c5488ca1bfe61191b5775bafa43b8647e7b20f22c","size":575,"version":0,"nonce":2,"sender":"Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2","sys_fee":"0","net_fee":"0.0087635","valid_until_block":1200,"attributes":[],"cosigners":[{"account":"0x4138145d67638db97b402ee0b6751ef16253ecab","scopes":"CalledByEntry"}],"script":"Ahjd9QUMFDFuhRA5AZ0538LDfWw/7hn9WAmHDBSr7FNi8R51tuAuQHu5jWNnXRQ4QRPADAh0cmFuc2ZlcgwUiXcg2M129PAKv6N8Dt2InCCP3ptBYn1bUjg=","scripts":[{"invocation":"DECub8BP5LbCIhjKlhfJjWB9nsmx+vjP3DORvsSFrnaxGtxsxqvrMaULU26oBz5nTWKlVm/OXgoM6wcYy5ccGuPQDEBgMHG3JaWNBSytev2I6ZsnuquTGv1btQ0W4iQzWqtFAXCqviUdPAxq0/Md1+m4myCbqr5aHi+liL2BGPnippYPDEDXKvzznmY9ui1w+4w2oJ0aamrQ0v04yFeo59xx4rmHETJODS7GQf5olrpjuoDT6jQcGq0R4IL7GI7gfiFbQDGxDECa+ygItgKGpWNDt//O8ouyqwxZVgPnMjteWwucHBDt+lxAdU2SGGXLb9cWaKIGs3oesQwAKan806hWrtB3Qs0/","verification":"EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBMHOzuw=="}]},{"hash":"0xb661d5e4d9e41c3059b068f8abb6f1566a47ec800879e34c0ebd2799781a2760","size":579,"version":0,"nonce":3,"sender":"Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2","sys_fee":"0","net_fee":"0.0088035","valid_until_block":1200,"attributes":[],"cosigners":[{"account":"0x4138145d67638db97b402ee0b6751ef16253ecab","scopes":"CalledByEntry"}],"script":"AwDodkgXAAAADBQxboUQOQGdOd/Cw31sP+4Z/VgJhwwUq+xTYvEedbbgLkB7uY1jZ10UOEETwAwIdHJhbnNmZXIMFDt9NxHG8Mz5sdypA9G/odiW8SOMQWJ9W1I4","scripts":[{"invocation":"DECHEOe12Kxs2NCdb7Nb8tzOX1+zhZXdttBKVwvJJbwsVac83188taH+sKzE8myLvKbEPfO0qYuMPCyAnC8JbrJaDEDBhsECy/cjE/2U31B3/Fu+/NMiJ+0hWaR6RllId/o58zDYIjtFqiSv8AW+u1skJ6UMjE3oYY59ewDXPYNsRJQuDECiQ7W1Zb1LwvC7ES92JPazUUwSQTxalSMIGfrOP3YPA/y5axiPmAOKPyUWhrU6iNaXRPTkqYXmKXADqAzbFp6ADEBKlR5hrJnV7jGDHREXVK23EbSpBgqVJP44OpB3GEPNsJY4JnQCeofyoVqDvXfesrKrH+iz3m5UYpPvPfmxEp4o","verification":"EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBMHOzuw=="}]}]}}` const hexHeader1 = "000000008aaab19c43c4ca2870c3e616b123f1b689866f44b138ae4d6a5352e54442fd56401107247de4e35599d1feffaf6e763972f738d2858b0a22ad06523867e4dcf921f7c1c67201000001000000abec5362f11e75b6e02e407bb98d63675d14384101fd08010c400f01eb6371a135527ddb205a0dae1f69d2d324837cce128bead9033091883f9ce21e6d759a40f690746592b021d397f088e3b7b417fcef73cd1bfdc2800769520c4084f7ae5d9f58a09aa56eeb2f282744a59a82d17e3be0eb1c7f54e6e8df79620e2608191bac57280a836db2ec2a776d07e43f16bc76c47d348b0fcd09d56c7c320c402b95b4e39ec35162a640795ff223e92dec95390a072eb457f6a4323052f80731517e0df029e4a457204f777f5261c6a4a88d46d4c3abdec635a6eed580d6c77f0c40b9735745b1dd795a258c31d8e6fa87d5cfc2e9b3a4890d610d33bcf833b64c58b0c5cea17f3a7128f1065ed193e636971590f193f28bdd1eeebbbc1fe6e7cee594130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb00" const header1Verbose = `{"id":5,"jsonrpc":"2.0","result":{"hash":"0x4f2c5539b0213ea444608cc217c5cb191255c1858ccd051ad9a36f08df26a288","size":518,"version":0,"previousblockhash":"0x56fd4244e552536a4dae38b1446f8689b6f123b116e6c37028cac4439cb1aa8a","merkleroot":"0xf9dce467385206ad220a8b85d238f77239766eaffffed19955e3e47d24071140","time":1592472500001,"index":1,"nextconsensus":"Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2","witnesses":[{"invocation":"DEAPAetjcaE1Un3bIFoNrh9p0tMkg3zOEovq2QMwkYg/nOIebXWaQPaQdGWSsCHTl/CI47e0F/zvc80b/cKAB2lSDECE965dn1igmqVu6y8oJ0SlmoLRfjvg6xx/VObo33liDiYIGRusVygKg22y7Cp3bQfkPxa8dsR9NIsPzQnVbHwyDEArlbTjnsNRYqZAeV/yI+kt7JU5CgcutFf2pDIwUvgHMVF+DfAp5KRXIE93f1JhxqSojUbUw6vexjWm7tWA1sd/DEC5c1dFsd15WiWMMdjm+ofVz8Lps6SJDWENM7z4M7ZMWLDFzqF/OnEo8QZe0ZPmNpcVkPGT8ovdHu67vB/m587l","verification":"EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBMHOzuw=="}],"confirmations":6,"nextblockhash":"0x45f62d72e37b074ecdc9f687222b0f91ec98d6d9af4429a2caa2e076a9196b0d"}}` -const txMoveNeoVerbose = `{"id":5,"jsonrpc":"2.0","result":{"blockhash":"0x4f2c5539b0213ea444608cc217c5cb191255c1858ccd051ad9a36f08df26a288","confirmations":6,"blocktime":1592472500001,"txid":"0x7fb05b593cf4b1eb2d9a283c5488ca1bfe61191b5775bafa43b8647e7b20f22c","size":575,"version":0,"nonce":2,"sender":"Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2","sys_fee":"0","net_fee":"0.0087635","valid_until_block":1200,"attributes":[],"cosigners":[{"account":"0x4138145d67638db97b402ee0b6751ef16253ecab","scopes":"CalledByEntry"}],"script":"Ahjd9QUMFDFuhRA5AZ0538LDfWw/7hn9WAmHDBSr7FNi8R51tuAuQHu5jWNnXRQ4QRPADAh0cmFuc2ZlcgwUiXcg2M129PAKv6N8Dt2InCCP3ptBYn1bUjg=","scripts":[{"invocation":"DECub8BP5LbCIhjKlhfJjWB9nsmx+vjP3DORvsSFrnaxGtxsxqvrMaULU26oBz5nTWKlVm/OXgoM6wcYy5ccGuPQDEBgMHG3JaWNBSytev2I6ZsnuquTGv1btQ0W4iQzWqtFAXCqviUdPAxq0/Md1+m4myCbqr5aHi+liL2BGPnippYPDEDXKvzznmY9ui1w+4w2oJ0aamrQ0v04yFeo59xx4rmHETJODS7GQf5olrpjuoDT6jQcGq0R4IL7GI7gfiFbQDGxDECa+ygItgKGpWNDt//O8ouyqwxZVgPnMjteWwucHBDt+lxAdU2SGGXLb9cWaKIGs3oesQwAKan806hWrtB3Qs0/","verification":"EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBMHOzuw=="}]}}` +const txMoveNeoVerbose = `{"id":5,"jsonrpc":"2.0","result":{"blockhash":"0x4f2c5539b0213ea444608cc217c5cb191255c1858ccd051ad9a36f08df26a288","confirmations":6,"blocktime":1592472500001,"hash":"0x7fb05b593cf4b1eb2d9a283c5488ca1bfe61191b5775bafa43b8647e7b20f22c","size":575,"version":0,"nonce":2,"sender":"Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2","sys_fee":"0","net_fee":"0.0087635","valid_until_block":1200,"attributes":[],"cosigners":[{"account":"0x4138145d67638db97b402ee0b6751ef16253ecab","scopes":"CalledByEntry"}],"script":"Ahjd9QUMFDFuhRA5AZ0538LDfWw/7hn9WAmHDBSr7FNi8R51tuAuQHu5jWNnXRQ4QRPADAh0cmFuc2ZlcgwUiXcg2M129PAKv6N8Dt2InCCP3ptBYn1bUjg=","scripts":[{"invocation":"DECub8BP5LbCIhjKlhfJjWB9nsmx+vjP3DORvsSFrnaxGtxsxqvrMaULU26oBz5nTWKlVm/OXgoM6wcYy5ccGuPQDEBgMHG3JaWNBSytev2I6ZsnuquTGv1btQ0W4iQzWqtFAXCqviUdPAxq0/Md1+m4myCbqr5aHi+liL2BGPnippYPDEDXKvzznmY9ui1w+4w2oJ0aamrQ0v04yFeo59xx4rmHETJODS7GQf5olrpjuoDT6jQcGq0R4IL7GI7gfiFbQDGxDECa+ygItgKGpWNDt//O8ouyqwxZVgPnMjteWwucHBDt+lxAdU2SGGXLb9cWaKIGs3oesQwAKan806hWrtB3Qs0/","verification":"EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBMHOzuw=="}]}}` // getResultBlock1 returns data for block number 1 which is used by several tests. func getResultBlock1() *result.Block { diff --git a/pkg/rpc/client/wsclient_test.go b/pkg/rpc/client/wsclient_test.go index f2221a675..50fed7ccf 100644 --- a/pkg/rpc/client/wsclient_test.go +++ b/pkg/rpc/client/wsclient_test.go @@ -119,7 +119,7 @@ func TestWSClientEvents(t *testing.T) { `{"jsonrpc":"2.0","method":"transaction_executed","params":[{"txid":"0xe1cd5e57e721d2a2e05fb1f08721b12057b25ab1dd7fd0f33ee1639932fdfad7","trigger":"Application","vmstate":"HALT","gas_consumed":"2.291","stack":[],"notifications":[{"contract":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176","state":{"type":"Array","value":[{"type":"ByteArray","value":"Y29udHJhY3QgY2FsbA=="},{"type":"ByteArray","value":"dHJhbnNmZXI="},{"type":"Array","value":[{"type":"ByteArray","value":"dpFiJB7t+XwkgWUq3xug9b9XQxs="},{"type":"ByteArray","value":"MW6FEDkBnTnfwsN9bD/uGf1YCYc="},{"type":"Integer","value":"1000"}]}]}},{"contract":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176","state":{"type":"Array","value":[{"type":"ByteArray","value":"dHJhbnNmZXI="},{"type":"ByteArray","value":"dpFiJB7t+XwkgWUq3xug9b9XQxs="},{"type":"ByteArray","value":"MW6FEDkBnTnfwsN9bD/uGf1YCYc="},{"type":"Integer","value":"1000"}]}}]}]}`, `{"jsonrpc":"2.0","method":"notification_from_execution","params":[{"contract":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176","state":{"type":"Array","value":[{"type":"ByteArray","value":"Y29udHJhY3QgY2FsbA=="},{"type":"ByteArray","value":"dHJhbnNmZXI="},{"type":"Array","value":[{"type":"ByteArray","value":"dpFiJB7t+XwkgWUq3xug9b9XQxs="},{"type":"ByteArray","value":"MW6FEDkBnTnfwsN9bD/uGf1YCYc="},{"type":"Integer","value":"1000"}]}]}}]}`, `{"jsonrpc":"2.0","method":"transaction_executed","params":[{"txid":"0xf97a72b7722c109f909a8bc16c22368c5023d85828b09b127b237aace33cf099","trigger":"Application","vmstate":"HALT","gas_consumed":"0.0604261","stack":[],"notifications":[{"contract":"0xe65ff7b3a02d207b584a5c27057d4e9862ef01da","state":{"type":"Array","value":[{"type":"ByteArray","value":"Y29udHJhY3QgY2FsbA=="},{"type":"ByteArray","value":"dHJhbnNmZXI="},{"type":"Array","value":[{"type":"ByteArray","value":"MW6FEDkBnTnfwsN9bD/uGf1YCYc="},{"type":"ByteArray","value":"IHKCdK+vw29DoHHTKM+j5inZy7A="},{"type":"Integer","value":"123"}]}]}},{"contract":"0xe65ff7b3a02d207b584a5c27057d4e9862ef01da","state":{"type":"Array","value":[{"type":"ByteArray","value":"dHJhbnNmZXI="},{"type":"ByteArray","value":"MW6FEDkBnTnfwsN9bD/uGf1YCYc="},{"type":"ByteArray","value":"IHKCdK+vw29DoHHTKM+j5inZy7A="},{"type":"Integer","value":"123"}]}}]}]}`, - `{"jsonrpc":"2.0","method":"block_added","params":[{"hash":"0x2d312f6379ead13cf62634c703091b750e7903728df2a3cf5bd96ce80b84a849","version":0,"previousblockhash":"0xb8237d34c156cac6be7b01578decf8ac8c99a62f0b6f774d622aad7be0fe189d","merkleroot":"0xf89169e89361692b71e671f13c088e84c5325015c413e8f89e7ba38efdb41287","time":1592472500006,"index":6,"nextconsensus":"Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2","witnesses":[{"invocation":"DEDblVguNGXWbUswDvBfVJzBt76BJyJ0Ga6siquyjioGn4Dbr6zy1IvcLl3xN5akcejRy9e+Mr1qvpe/gkLgtW4QDEDRwPISZagMFjE/plXTnZ/gEU0IbBAAe23U29zVWteUmzRsPxF/MdzXvdffR9W0edkj17AmkWpn+5rqzH9aCOpLDECEvjgxZaRoAHEDNzp1REllLcGzMwrwSjudtzfgRglQL3g1BKerDx6cGHH73medRVkL9QVm4KzSxlywVtvhwBMrDEBuPKvzg5TtakFW2jr/bfmy1bn2FiLARlOySwaGdKRV93ozA5lVEIAvHbBlJtT4/5H8jHjbncXXMrP3OUHqebZz","verification":"EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBMHOzuw=="}],"consensus_data":{"primary":0,"nonce":"0000000000000457"},"tx":[{"txid":"0xf97a72b7722c109f909a8bc16c22368c5023d85828b09b127b237aace33cf099","size":265,"version":0,"nonce":9,"sender":"NQRLhCpAru9BjGsMwk67vdMwmzKMRgsnnN","sys_fee":"0","net_fee":"0.0036521","valid_until_block":1200,"attributes":[],"cosigners":[{"account":"0x870958fd19ee3f6c7dc3c2df399d013910856e31","scopes":"CalledByEntry"}],"script":"AHsMFCBygnSvr8NvQ6Bx0yjPo+Yp2cuwDBQxboUQOQGdOd/Cw31sP+4Z/VgJhxPADAh0cmFuc2ZlcgwU2gHvYphOfQUnXEpYeyAtoLP3X+ZBYn1bUjg=","scripts":[{"invocation":"DECwklSj3liZOJbktRtkVdUCu8U2LQlrU6Dv8NtMgd0xXbk5lXjc2p68xv6xtJXbJ4aoFMJZ9lkcNpGoeUCcaCet","verification":"DCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcILQQqQatQ="}]}]}]}`, + `{"jsonrpc":"2.0","method":"block_added","params":[{"hash":"0x2d312f6379ead13cf62634c703091b750e7903728df2a3cf5bd96ce80b84a849","version":0,"previousblockhash":"0xb8237d34c156cac6be7b01578decf8ac8c99a62f0b6f774d622aad7be0fe189d","merkleroot":"0xf89169e89361692b71e671f13c088e84c5325015c413e8f89e7ba38efdb41287","time":1592472500006,"index":6,"nextconsensus":"Nbb1qkwcwNSBs9pAnrVVrnFbWnbWBk91U2","witnesses":[{"invocation":"DEDblVguNGXWbUswDvBfVJzBt76BJyJ0Ga6siquyjioGn4Dbr6zy1IvcLl3xN5akcejRy9e+Mr1qvpe/gkLgtW4QDEDRwPISZagMFjE/plXTnZ/gEU0IbBAAe23U29zVWteUmzRsPxF/MdzXvdffR9W0edkj17AmkWpn+5rqzH9aCOpLDECEvjgxZaRoAHEDNzp1REllLcGzMwrwSjudtzfgRglQL3g1BKerDx6cGHH73medRVkL9QVm4KzSxlywVtvhwBMrDEBuPKvzg5TtakFW2jr/bfmy1bn2FiLARlOySwaGdKRV93ozA5lVEIAvHbBlJtT4/5H8jHjbncXXMrP3OUHqebZz","verification":"EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBMHOzuw=="}],"consensus_data":{"primary":0,"nonce":"0000000000000457"},"tx":[{"hash":"0xf97a72b7722c109f909a8bc16c22368c5023d85828b09b127b237aace33cf099","size":265,"version":0,"nonce":9,"sender":"NQRLhCpAru9BjGsMwk67vdMwmzKMRgsnnN","sys_fee":"0","net_fee":"0.0036521","valid_until_block":1200,"attributes":[],"cosigners":[{"account":"0x870958fd19ee3f6c7dc3c2df399d013910856e31","scopes":"CalledByEntry"}],"script":"AHsMFCBygnSvr8NvQ6Bx0yjPo+Yp2cuwDBQxboUQOQGdOd/Cw31sP+4Z/VgJhxPADAh0cmFuc2ZlcgwU2gHvYphOfQUnXEpYeyAtoLP3X+ZBYn1bUjg=","scripts":[{"invocation":"DECwklSj3liZOJbktRtkVdUCu8U2LQlrU6Dv8NtMgd0xXbk5lXjc2p68xv6xtJXbJ4aoFMJZ9lkcNpGoeUCcaCet","verification":"DCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcILQQqQatQ="}]}]}]}`, `{"jsonrpc":"2.0","method":"event_missed","params":[]}`, } srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { diff --git a/pkg/rpc/server/server_test.go b/pkg/rpc/server/server_test.go index 14729e5ed..80c727c0d 100644 --- a/pkg/rpc/server/server_test.go +++ b/pkg/rpc/server/server_test.go @@ -50,8 +50,8 @@ type rpcTestCase struct { check func(t *testing.T, e *executor, result interface{}) } -const testContractHash = "e65ff7b3a02d207b584a5c27057d4e9862ef01da" -const deploymentTxHash = "b0428600383ec7f7b06734978a24dbe81edc87b781f58c0614f122c735d8cf6a" +const testContractHash = "10e262ef80c76bdecca287a2c047841fc02c3129" +const deploymentTxHash = "ad8b149c799d4b2337162b0ad23e0ba8845cddb9cfca8a45587ee207015d2a7c" var rpcTestCases = map[string][]rpcTestCase{ "getapplicationlog": { @@ -147,7 +147,7 @@ var rpcTestCases = map[string][]rpcTestCase{ }, { Asset: e.chain.UtilityTokenHash(), - Amount: "923.96937740", + Amount: "918.01738700", LastUpdated: 6, }}, Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(), @@ -229,7 +229,7 @@ var rpcTestCases = map[string][]rpcTestCase{ Timestamp: blockSendNEO.Timestamp, Asset: e.chain.UtilityTokenHash(), Address: "", // Minted GAS. - Amount: "23.99976000", + Amount: "17.99982000", Index: 4, NotifyIndex: 0, TxHash: txSendNEOHash, @@ -259,6 +259,7 @@ var rpcTestCases = map[string][]rpcTestCase{ // take burned gas into account u := testchain.PrivateKeyByID(0).GetScriptHash() for i := 0; i <= int(e.chain.BlockHeight()); i++ { + var netFee util.Fixed8 h := e.chain.GetHeaderHash(i) b, err := e.chain.GetBlock(h) require.NoError(t, err) @@ -274,7 +275,19 @@ var rpcTestCases = map[string][]rpcTestCase{ TxHash: b.Hash(), }) } + netFee += b.Transactions[j].NetworkFee } + if i > 0 { + expected.Received = append(expected.Received, result.NEP5Transfer{ + Timestamp: b.Timestamp, + Asset: e.chain.UtilityTokenHash(), + Address: "", // minted from network fees. + Amount: amountToString(int64(netFee), 8), + Index: b.Index, + TxHash: b.Hash(), + }) + } + } require.Equal(t, expected.Address, res.Address) require.ElementsMatch(t, expected.Sent, res.Sent) @@ -552,8 +565,7 @@ var rpcTestCases = map[string][]rpcTestCase{ check: func(t *testing.T, e *executor, resp interface{}) { s, ok := resp.(*string) require.True(t, ok) - // Incorrect, to be fixed later. - assert.Equal(t, "48000", *s) + assert.Equal(t, "36000", *s) }, }, }, @@ -565,8 +577,7 @@ var rpcTestCases = map[string][]rpcTestCase{ }, check: func(t *testing.T, e *executor, validators interface{}) { var expected []result.Validator - sBValidators, err := e.chain.GetStandByValidators() - require.NoError(t, err) + sBValidators := e.chain.GetStandByValidators() for _, sbValidator := range sBValidators { expected = append(expected, result.Validator{ PublicKey: *sbValidator, diff --git a/pkg/rpc/server/testdata/test_contract.avm b/pkg/rpc/server/testdata/test_contract.avm index eec2f3ea4..bdb814d37 100755 Binary files a/pkg/rpc/server/testdata/test_contract.avm and b/pkg/rpc/server/testdata/test_contract.avm differ diff --git a/pkg/rpc/server/testdata/test_contract.go b/pkg/rpc/server/testdata/test_contract.go index c8d7609d0..af08ea213 100644 --- a/pkg/rpc/server/testdata/test_contract.go +++ b/pkg/rpc/server/testdata/test_contract.go @@ -32,7 +32,11 @@ func Main(operation string, args []interface{}) interface{} { runtime.Log("invalid address") return false } - amount := storage.Get(ctx, addr).(int) + var amount int + val := storage.Get(ctx, addr) + if val != nil { + amount = val.(int) + } runtime.Notify("balanceOf", addr, amount) return amount case "transfer": @@ -53,7 +57,11 @@ func Main(operation string, args []interface{}) interface{} { return false } - fromBalance := storage.Get(ctx, from).(int) + var fromBalance int + val := storage.Get(ctx, from) + if val != nil { + fromBalance = val.(int) + } if fromBalance < amount { runtime.Log("insufficient funds") return false @@ -61,7 +69,11 @@ func Main(operation string, args []interface{}) interface{} { fromBalance -= amount storage.Put(ctx, from, fromBalance) - toBalance := storage.Get(ctx, to).(int) + var toBalance int + val = storage.Get(ctx, to) + if val != nil { + toBalance = val.(int) + } toBalance += amount storage.Put(ctx, to, toBalance) diff --git a/pkg/rpc/server/testdata/testblocks.acc b/pkg/rpc/server/testdata/testblocks.acc index aa3bb8e87..d1756b35b 100644 Binary files a/pkg/rpc/server/testdata/testblocks.acc and b/pkg/rpc/server/testdata/testblocks.acc differ diff --git a/pkg/smartcontract/parameter.go b/pkg/smartcontract/parameter.go index c4dfe7fc5..56072d16a 100644 --- a/pkg/smartcontract/parameter.go +++ b/pkg/smartcontract/parameter.go @@ -212,7 +212,7 @@ func (p *Parameter) EncodeBinary(w *io.BinWriter) { w.WriteBytes(p.Value.(util.Uint160).BytesBE()) case Hash256Type: w.WriteBytes(p.Value.(util.Uint256).BytesBE()) - case InteropInterfaceType: + case InteropInterfaceType, AnyType: default: w.Err = fmt.Errorf("unknown type: %x", p.Type) } @@ -251,7 +251,7 @@ func (p *Parameter) DecodeBinary(r *io.BinReader) { var u util.Uint256 r.ReadBytes(u[:]) p.Value = u - case InteropInterfaceType: + case InteropInterfaceType, AnyType: default: r.Err = fmt.Errorf("unknown type: %x", p.Type) } diff --git a/pkg/vm/context.go b/pkg/vm/context.go index 8fa7afd98..bfbf2f2bb 100644 --- a/pkg/vm/context.go +++ b/pkg/vm/context.go @@ -41,6 +41,9 @@ type Context struct { // Script hash of the prog. scriptHash util.Uint160 + // Caller's contract script hash. + callingScriptHash util.Uint160 + // Call flags this context was created with. callFlag smartcontract.CallFlag } diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index 1c201dfd7..90d16014b 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -276,9 +276,11 @@ func (v *VM) LoadScriptWithFlags(b []byte, f smartcontract.CallFlag) { // given script hash directly into the Context to avoid its recalculations. It's // up to user of this function to make sure the script and hash match each other. func (v *VM) LoadScriptWithHash(b []byte, hash util.Uint160, f smartcontract.CallFlag) { + shash := v.GetCurrentScriptHash() v.LoadScriptWithFlags(b, f) ctx := v.Context() ctx.scriptHash = hash + ctx.callingScriptHash = shash } // Context returns the current executed context. Nil if there is no context, @@ -1607,7 +1609,7 @@ func (v *VM) bytesToPublicKey(b []byte) *keys.PublicKey { // GetCallingScriptHash implements ScriptHashGetter interface func (v *VM) GetCallingScriptHash() util.Uint160 { - return v.getContextScriptHash(1) + return v.Context().callingScriptHash } // GetEntryScriptHash implements ScriptHashGetter interface