Merge pull request #1093 from nspcc-dev/fix-some-inconsistencies-wrt-sharp

Fix some inconsistencies wrt sharp
This commit is contained in:
Roman Khimov 2020-06-24 10:47:16 +03:00 committed by GitHub
commit 5d51ceaeac
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 289 additions and 176 deletions

View file

@ -9,7 +9,6 @@ import (
"path/filepath" "path/filepath"
"github.com/nspcc-dev/neo-go/pkg/core/storage" "github.com/nspcc-dev/neo-go/pkg/core/storage"
"github.com/nspcc-dev/neo-go/pkg/util"
) )
type dump []blockDump type dump []blockDump
@ -26,26 +25,24 @@ type storageOp struct {
Value string `json:"value,omitempty"` Value string `json:"value,omitempty"`
} }
// NEO has some differences of key storing. // NEO has some differences of key storing (that should go away post-preview2).
// out format: script hash in LE + key // ours format: contract's ID (uint32le) + key
// neo format: script hash in BE + byte(0) + key with 0 between every 16 bytes, padded to len 16. // theirs format: contract's ID (uint32le) + key with 16 between every 16 bytes, padded to len 16.
func toNeoStorageKey(key []byte) []byte { func toNeoStorageKey(key []byte) []byte {
if len(key) < util.Uint160Size { if len(key) < 4 {
panic("invalid key in storage") panic("invalid key in storage")
} }
var nkey []byte // Prefix is a contract's ID in LE.
for i := util.Uint160Size - 1; i >= 0; i-- { nkey := make([]byte, 4, len(key))
nkey = append(nkey, key[i]) copy(nkey, key[:4])
} key = key[4:]
key = key[util.Uint160Size:]
index := 0 index := 0
remain := len(key) remain := len(key)
for remain >= 16 { for remain >= 16 {
nkey = append(nkey, key[index:index+16]...) nkey = append(nkey, key[index:index+16]...)
nkey = append(nkey, 0) nkey = append(nkey, 16)
index += 16 index += 16
remain -= 16 remain -= 16
} }
@ -59,7 +56,7 @@ func toNeoStorageKey(key []byte) []byte {
nkey = append(nkey, 0) nkey = append(nkey, 0)
} }
nkey = append(nkey, byte(padding)) nkey = append(nkey, byte(remain))
return nkey return nkey
} }
@ -84,7 +81,7 @@ func batchToMap(index uint32, batch *storage.MemBatch) blockDump {
ops = append(ops, storageOp{ ops = append(ops, storageOp{
State: op, State: op,
Key: hex.EncodeToString(key), Key: hex.EncodeToString(key),
Value: "00" + hex.EncodeToString(batch.Put[i].Value), Value: hex.EncodeToString(batch.Put[i].Value),
}) })
} }

View file

@ -236,7 +236,11 @@ type Token struct {
func (t Token) AddToCirculation(amount int) bool { func (t Token) AddToCirculation(amount int) bool {
ctx := storage.Context() 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 inCirc += amount
storage.Put(ctx, "in_circ", inCirc) storage.Put(ctx, "in_circ", inCirc)
return true return true

View file

@ -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. // InCirculation return the amount of total tokens that are in circulation.
func (t TokenConfig) InCirculation(ctx storage.Context) int { func (t TokenConfig) InCirculation(ctx storage.Context) int {
amount := storage.Get(ctx, t.CirculationKey) return getIntFromDB(ctx, t.CirculationKey)
return amount.(int)
} }
// AddToCirculation sets the given amount as "in circulation" in the storage. // AddToCirculation sets the given amount as "in circulation" in the storage.
func (t TokenConfig) AddToCirculation(ctx storage.Context, amount int) bool { func (t TokenConfig) AddToCirculation(ctx storage.Context, amount int) bool {
supply := storage.Get(ctx, t.CirculationKey).(int) supply := getIntFromDB(ctx, t.CirculationKey)
supply += amount supply += amount
storage.Put(ctx, t.CirculationKey, supply) storage.Put(ctx, t.CirculationKey, supply)
return true 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 // TokenSaleAvailableAmount returns the total amount of available tokens left
// to be distributed. // to be distributed.
func (t TokenConfig) TokenSaleAvailableAmount(ctx storage.Context) int { func (t TokenConfig) TokenSaleAvailableAmount(ctx storage.Context) int {
inCirc := storage.Get(ctx, t.CirculationKey) inCirc := getIntFromDB(ctx, t.CirculationKey)
return t.TotalSupply - inCirc.(int) return t.TotalSupply - inCirc
} }
// Main smart contract entry point. // Main smart contract entry point.
@ -128,11 +138,11 @@ func handleOperation(op string, args []interface{}, ctx storage.Context, cfg Tok
return cfg.Symbol return cfg.Symbol
} }
if op == "totalSupply" { if op == "totalSupply" {
return storage.Get(ctx, cfg.CirculationKey) return getIntFromDB(ctx, cfg.CirculationKey)
} }
if op == "balanceOf" { if op == "balanceOf" {
if len(args) == 1 { if len(args) == 1 {
return storage.Get(ctx, args[0].([]byte)) return getIntFromDB(ctx, args[0].([]byte))
} }
} }
if op == "transfer" { 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) { if amount <= 0 || len(to) != 20 || !runtime.CheckWitness(from) {
return false return false
} }
amountFrom := storage.Get(ctx, from).(int) amountFrom := getIntFromDB(ctx, from)
if amountFrom < amount { if amountFrom < amount {
return false return false
} }
@ -187,7 +197,7 @@ func transfer(cfg TokenConfig, ctx storage.Context, from, to []byte, amount int)
diff := amountFrom - amount diff := amountFrom - amount
storage.Put(ctx, from, diff) storage.Put(ctx, from, diff)
} }
amountTo := storage.Get(ctx, to).(int) amountTo := getIntFromDB(ctx, to)
totalAmountTo := amountTo + amount totalAmountTo := amountTo + amount
storage.Put(ctx, to, totalAmountTo) storage.Put(ctx, to, totalAmountTo)
return true return true
@ -201,15 +211,15 @@ func transferFrom(cfg TokenConfig, ctx storage.Context, from, to []byte, amount
if len(availableKey) != 40 { if len(availableKey) != 40 {
return false return false
} }
availableTo := storage.Get(ctx, availableKey).(int) availableTo := getIntFromDB(ctx, availableKey)
if availableTo < amount { if availableTo < amount {
return false return false
} }
fromBalance := storage.Get(ctx, from).(int) fromBalance := getIntFromDB(ctx, from)
if fromBalance < amount { if fromBalance < amount {
return false return false
} }
toBalance := storage.Get(ctx, to).(int) toBalance := getIntFromDB(ctx, to)
newFromBalance := fromBalance - amount newFromBalance := fromBalance - amount
newToBalance := toBalance + amount newToBalance := toBalance + amount
storage.Put(ctx, to, newToBalance) storage.Put(ctx, to, newToBalance)
@ -231,7 +241,7 @@ func approve(ctx storage.Context, owner, spender []byte, amount int) bool {
if len(spender) != 20 { if len(spender) != 20 {
return false return false
} }
toSpend := storage.Get(ctx, owner).(int) toSpend := getIntFromDB(ctx, owner)
if toSpend < amount { if toSpend < amount {
return false 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 { func allowance(ctx storage.Context, from, to []byte) int {
key := append(from, to...) key := append(from, to...)
return storage.Get(ctx, key).(int) return getIntFromDB(ctx, key)
} }

View file

@ -22,14 +22,25 @@ type Token struct {
CirculationKey string 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 // GetSupply gets the token totalSupply value from VM storage
func (t Token) GetSupply(ctx storage.Context) interface{} { 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 // BalanceOf gets the token balance of a specific address
func (t Token) BalanceOf(ctx storage.Context, hodler []byte) interface{} { 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 // 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) storage.Put(ctx, from, diff)
} }
amountTo := storage.Get(ctx, to).(int) amountTo := getIntFromDB(ctx, to)
totalAmountTo := amountTo + amount totalAmountTo := amountTo + amount
storage.Put(ctx, to, totalAmountTo) storage.Put(ctx, to, totalAmountTo)
runtime.Notify("transfer", from, to, amount) 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 return -1
} }
amountFrom := storage.Get(ctx, from).(int) amountFrom := getIntFromDB(ctx, from)
if amountFrom < amount { if amountFrom < amount {
return -1 return -1
} }
@ -98,8 +109,8 @@ func (t Token) Mint(ctx storage.Context, to []byte) bool {
if !IsUsableAddress(t.Owner) { if !IsUsableAddress(t.Owner) {
return false return false
} }
minted := storage.Get(ctx, []byte("minted")).(bool) minted := storage.Get(ctx, []byte("minted"))
if minted { if minted != nil && minted.(bool) == true {
return false return false
} }

View file

@ -659,8 +659,26 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
return nil return nil
} }
ast.Walk(c, n.X) var checkForNull bool
ast.Walk(c, n.Y)
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 { switch {
case n.Op == token.ADD: case n.Op == token.ADD:

View file

@ -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)
}

View file

@ -213,7 +213,7 @@ func newTestService(t *testing.T) *service {
} }
func getTestValidator(i int) (*privateKey, *publicKey) { func getTestValidator(i int) (*privateKey, *publicKey) {
key := testchain.PrivateKey(i) key := testchain.PrivateKeyByID(i)
return &privateKey{PrivateKey: key}, &publicKey{PublicKey: key.PublicKey()} 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) validators := make([]*keys.PublicKey, 4)
privNetKeys := make([]*keys.PrivateKey, 4) privNetKeys := make([]*keys.PrivateKey, 4)
for i := 0; i < 4; i++ { for i := 0; i < 4; i++ {
privateKey, publicKey := getTestValidator(i) privNetKeys[i] = testchain.PrivateKey(i)
validators[i] = publicKey.PublicKey validators[i] = privNetKeys[i].PublicKey()
privNetKeys[i] = privateKey.PrivateKey
} }
rawScript, err := smartcontract.CreateMultiSigRedeemScript(3, validators) rawScript, err := smartcontract.CreateMultiSigRedeemScript(3, validators)
require.NoError(t, err) require.NoError(t, err)

View file

@ -54,7 +54,7 @@ var (
ErrInvalidBlockIndex error = errors.New("invalid block index") ErrInvalidBlockIndex error = errors.New("invalid block index")
) )
var ( 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 decrementInterval = 2000000
persistInterval = 1 * time.Second persistInterval = 1 * time.Second
) )
@ -117,6 +117,8 @@ type Blockchain struct {
// cache for block verification keys. // cache for block verification keys.
keyCache map[util.Uint160]map[string]*keys.PublicKey keyCache map[util.Uint160]map[string]*keys.PublicKey
sbValidators keys.PublicKeys
log *zap.Logger log *zap.Logger
lastBatch *storage.MemBatch lastBatch *storage.MemBatch
@ -152,6 +154,10 @@ func NewBlockchain(s storage.Store, cfg config.ProtocolConfiguration, log *zap.L
cfg.MemPoolSize = defaultMemPoolSize cfg.MemPoolSize = defaultMemPoolSize
log.Info("mempool size is not set or wrong, setting default value", zap.Int("MemPoolSize", cfg.MemPoolSize)) 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{ bc := &Blockchain{
config: cfg, config: cfg,
dao: dao.NewSimple(s, cfg.Magic), 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{}), runToExitCh: make(chan struct{}),
memPool: mempool.NewMemPool(cfg.MemPoolSize), memPool: mempool.NewMemPool(cfg.MemPoolSize),
keyCache: make(map[util.Uint160]map[string]*keys.PublicKey), keyCache: make(map[util.Uint160]map[string]*keys.PublicKey),
sbValidators: validators,
log: log, log: log,
events: make(chan bcEvent), events: make(chan bcEvent),
subCh: make(chan interface{}), subCh: make(chan interface{}),
@ -1235,8 +1242,10 @@ func (bc *Blockchain) PoolTx(t *transaction.Transaction) error {
} }
//GetStandByValidators returns validators from the configuration. //GetStandByValidators returns validators from the configuration.
func (bc *Blockchain) GetStandByValidators() (keys.PublicKeys, error) { func (bc *Blockchain) GetStandByValidators() keys.PublicKeys {
return getValidators(bc.config) res := make(keys.PublicKeys, len(bc.sbValidators))
copy(res, bc.sbValidators)
return res
} }
// GetValidators returns next block validators. // GetValidators returns next block validators.

View file

@ -37,7 +37,7 @@ type Blockchainer interface {
GetNEP5TransferLog(util.Uint160) *state.NEP5TransferLog GetNEP5TransferLog(util.Uint160) *state.NEP5TransferLog
GetNEP5Balances(util.Uint160) *state.NEP5Balances GetNEP5Balances(util.Uint160) *state.NEP5Balances
GetValidators() ([]*keys.PublicKey, error) GetValidators() ([]*keys.PublicKey, error)
GetStandByValidators() (keys.PublicKeys, error) GetStandByValidators() keys.PublicKeys
GetScriptHashesForVerifying(*transaction.Transaction) ([]util.Uint160, error) GetScriptHashesForVerifying(*transaction.Transaction) ([]util.Uint160, error)
GetStorageItem(id int32, key []byte) *state.StorageItem GetStorageItem(id int32, key []byte) *state.StorageItem
GetStorageItems(id int32) (map[string]*state.StorageItem, error) GetStorageItems(id int32) (map[string]*state.StorageItem, error)

View file

@ -32,7 +32,7 @@ type DAO interface {
GetHeaderHashes() ([]util.Uint256, error) GetHeaderHashes() ([]util.Uint256, error)
GetNEP5Balances(acc util.Uint160) (*state.NEP5Balances, error) GetNEP5Balances(acc util.Uint160) (*state.NEP5Balances, error)
GetNEP5TransferLog(acc util.Uint160, index uint32) (*state.NEP5TransferLog, error) GetNEP5TransferLog(acc util.Uint160, index uint32) (*state.NEP5TransferLog, error)
GetNextContractID() (int32, error) GetAndUpdateNextContractID() (int32, error)
GetStorageItem(id int32, key []byte) *state.StorageItem GetStorageItem(id int32, key []byte) *state.StorageItem
GetStorageItems(id int32) (map[string]*state.StorageItem, error) GetStorageItems(id int32) (map[string]*state.StorageItem, error)
GetStorageItemsWithPrefix(id int32, prefix []byte) (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 PutCurrentHeader(hashAndIndex []byte) error
PutNEP5Balances(acc util.Uint160, bs *state.NEP5Balances) error PutNEP5Balances(acc util.Uint160, bs *state.NEP5Balances) error
PutNEP5TransferLog(acc util.Uint160, index uint32, lg *state.NEP5TransferLog) error PutNEP5TransferLog(acc util.Uint160, index uint32, lg *state.NEP5TransferLog) error
PutNextContractID(id int32) error
PutStorageItem(id int32, key []byte, si *state.StorageItem) error PutStorageItem(id int32, key []byte, si *state.StorageItem) error
PutVersion(v string) error PutVersion(v string) error
StoreAsBlock(block *block.Block) error StoreAsBlock(block *block.Block) error
@ -173,25 +172,19 @@ func (dao *Simple) DeleteContractState(hash util.Uint160) error {
return dao.Store.Delete(key) return dao.Store.Delete(key)
} }
// GetNextContractID returns id for the next contract and increases stored id. // GetAndUpdateNextContractID returns id for the next contract and increases stored ID.
func (dao *Simple) GetNextContractID() (int32, error) { func (dao *Simple) GetAndUpdateNextContractID() (int32, error) {
var id int32
key := storage.SYSContractID.Bytes() key := storage.SYSContractID.Bytes()
data, err := dao.Store.Get(key) data, err := dao.Store.Get(key)
if err != nil { if err == nil {
if err == storage.ErrKeyNotFound { id = int32(binary.LittleEndian.Uint32(data))
err = nil } else if err != storage.ErrKeyNotFound {
}
return 0, err return 0, err
} }
return int32(binary.LittleEndian.Uint32(data)), nil data = make([]byte, 4)
} binary.LittleEndian.PutUint32(data, uint32(id+1))
return id, dao.Store.Put(key, data)
// 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)
} }
// -- end contracts. // -- end contracts.

View file

@ -84,15 +84,17 @@ func TestDeleteContractState(t *testing.T) {
require.Nil(t, gotContractState) require.Nil(t, gotContractState)
} }
func TestSimple_GetNextContractID(t *testing.T) { func TestSimple_GetAndUpdateNextContractID(t *testing.T) {
dao := NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet) dao := NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet)
id, err := dao.GetNextContractID() id, err := dao.GetAndUpdateNextContractID()
require.NoError(t, err) require.NoError(t, err)
require.EqualValues(t, 0, id) require.EqualValues(t, 0, id)
require.NoError(t, dao.PutNextContractID(10)) id, err = dao.GetAndUpdateNextContractID()
id, err = dao.GetNextContractID()
require.NoError(t, err) 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) { func TestPutGetAppExecResult(t *testing.T) {

View file

@ -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 { func newBlock(cfg config.ProtocolConfiguration, index uint32, prev util.Uint256, txs ...*transaction.Transaction) *block.Block {
validators, _ := getValidators(cfg) validators, _ := validatorsFromConfig(cfg)
vlen := len(validators) vlen := len(validators)
valScript, _ := smartcontract.CreateMultiSigRedeemScript( valScript, _ := smartcontract.CreateMultiSigRedeemScript(
vlen-(vlen-1)/3, vlen-(vlen-1)/3,
@ -210,7 +210,7 @@ func TestCreateBasicChain(t *testing.T) {
t.Logf("txMoveNeo: %s", txMoveNeo.Hash().StringLE()) t.Logf("txMoveNeo: %s", txMoveNeo.Hash().StringLE())
t.Logf("txMoveGas: %s", txMoveGas.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 // info for getblockheader rpc tests
t.Logf("header hash: %s", b.Hash().StringLE()) t.Logf("header hash: %s", b.Hash().StringLE())
buf := io.NewBufBinWriter() buf := io.NewBufBinWriter()
@ -399,10 +399,7 @@ func addCosigners(txs ...*transaction.Transaction) {
} }
func signTx(bc *Blockchain, txs ...*transaction.Transaction) error { func signTx(bc *Blockchain, txs ...*transaction.Transaction) error {
validators, err := getValidators(bc.config) validators := bc.GetStandByValidators()
if err != nil {
return errors.Wrap(err, "fail to sign tx")
}
rawScript, err := smartcontract.CreateMultiSigRedeemScript(len(bc.config.StandbyValidators)/2+1, validators) rawScript, err := smartcontract.CreateMultiSigRedeemScript(len(bc.config.StandbyValidators)/2+1, validators)
if err != nil { if err != nil {
return errors.Wrap(err, "fail to sign tx") return errors.Wrap(err, "fail to sign tx")

View file

@ -91,14 +91,11 @@ func contractCreate(ic *interop.Context, v *vm.VM) error {
if contract != nil { if contract != nil {
return errors.New("contract already exists") return errors.New("contract already exists")
} }
id, err := ic.DAO.GetNextContractID() id, err := ic.DAO.GetAndUpdateNextContractID()
if err != nil { if err != nil {
return err return err
} }
newcontract.ID = id newcontract.ID = id
if err := ic.DAO.PutNextContractID(id); err != nil {
return err
}
if err := ic.DAO.PutContractState(newcontract); err != nil { if err := ic.DAO.PutContractState(newcontract); err != nil {
return err return err
} }

View file

@ -289,7 +289,7 @@ func storageGet(ic *interop.Context, v *vm.VM) error {
if si != nil && si.Value != nil { if si != nil && si.Value != nil {
v.Estack().PushVal(si.Value) v.Estack().PushVal(si.Value)
} else { } else {
v.Estack().PushVal([]byte{}) v.Estack().PushVal(stackitem.Null{})
} }
return nil return nil
} }

View file

@ -58,7 +58,11 @@ func (g *GAS) increaseBalance(_ *interop.Context, _ util.Uint160, si *state.Stor
return errors.New("insufficient funds") return errors.New("insufficient funds")
} }
acc.Balance.Add(&acc.Balance, amount) 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 return nil
} }
@ -87,7 +91,7 @@ func (g *GAS) OnPersist(ic *interop.Context) error {
absAmount := big.NewInt(int64(tx.SystemFee + tx.NetworkFee)) absAmount := big.NewInt(int64(tx.SystemFee + tx.NetworkFee))
g.burn(ic, tx.Sender, absAmount) 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 { if err != nil {
return fmt.Errorf("cannot get block validators: %v", err) 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) { func getStandbyValidatorsHash(ic *interop.Context) (util.Uint160, []*keys.PublicKey, error) {
vs, err := ic.Chain.GetStandByValidators() vs := ic.Chain.GetStandByValidators()
if err != nil {
return util.Uint160{}, nil, err
}
s, err := smartcontract.CreateMultiSigRedeemScript(len(vs)/2+1, vs) s, err := smartcontract.CreateMultiSigRedeemScript(len(vs)/2+1, vs)
if err != nil { if err != nil {
return util.Uint160{}, nil, err return util.Uint160{}, nil, err

View file

@ -11,6 +11,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/interop/runtime" "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/core/state"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "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"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "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"
@ -112,8 +113,6 @@ func NewNEO() *NEO {
// Initialize initializes NEO contract. // Initialize initializes NEO contract.
func (n *NEO) Initialize(ic *interop.Context) error { func (n *NEO) Initialize(ic *interop.Context) error {
var si state.StorageItem
if err := n.nep5TokenNative.Initialize(ic); err != nil { if err := n.nep5TokenNative.Initialize(ic); err != nil {
return err return err
} }
@ -122,11 +121,6 @@ func (n *NEO) Initialize(ic *interop.Context) error {
return errors.New("already initialized") 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) h, vs, err := getStandbyValidatorsHash(ic)
if err != nil { if err != nil {
return err return err
@ -165,10 +159,11 @@ func (n *NEO) increaseBalance(ic *interop.Context, h util.Uint160, si *state.Sto
return err return err
} }
if amount.Sign() == 0 { if amount.Sign() == 0 {
si.Value = acc.Bytes()
return nil return nil
} }
if len(acc.Votes) > 0 { 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 return err
} }
siVC := ic.DAO.GetStorageItem(n.ContractID, validatorsCountKey) 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) 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 return nil
} }
@ -225,10 +224,8 @@ func (n *NEO) registerValidatorInternal(ic *interop.Context, pub *keys.PublicKey
return errors.New("already registered") return errors.New("already registered")
} }
si = new(state.StorageItem) si = new(state.StorageItem)
// It's the same simple counter, calling it `Votes` instead of `Balance` // Zero value.
// doesn't help a lot. si.Value = []byte{}
votes := state.NEP5BalanceState{}
si.Value = votes.Bytes()
return ic.DAO.PutStorageItem(n.ContractID, key, si) 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) newPubs = append(newPubs, pub)
} }
if lp, lv := len(newPubs), len(acc.Votes); lp != lv { 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 { if si == nil {
return errors.New("validators count uninitialized") // The first voter.
} si = new(state.StorageItem)
vc, err := ValidatorsCountFromBytes(si.Value) vc = new(ValidatorsCount)
if err != nil { } else {
return err vc, err = ValidatorsCountFromBytes(si.Value)
if err != nil {
return err
}
} }
if lv > 0 { if lv > 0 {
vc[lv-1].Sub(&vc[lv-1], &acc.Balance) 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 { if si == nil {
return errors.New("invalid validator") return errors.New("invalid validator")
} }
votes, err := state.NEP5BalanceStateFromBytes(si.Value) votes := bigint.FromBytes(si.Value)
if err != nil { votes.Add(votes, value)
return err si.Value = bigint.ToPreallocatedBytes(votes, si.Value[:0])
}
votes.Balance.Add(&votes.Balance, value)
si.Value = votes.Bytes()
if err := d.PutStorageItem(n.ContractID, key, si); err != nil { if err := d.PutStorageItem(n.ContractID, key, si); err != nil {
return err return err
} }
@ -339,11 +340,8 @@ func (n *NEO) getRegisteredValidators(d dao.DAO) ([]keyWithVotes, error) {
} }
arr := make([]keyWithVotes, 0, len(siMap)) arr := make([]keyWithVotes, 0, len(siMap))
for key, si := range siMap { for key, si := range siMap {
votes, err := state.NEP5BalanceStateFromBytes(si.Value) votes := bigint.FromBytes(si.Value)
if err != nil { arr = append(arr, keyWithVotes{key, votes})
return nil, err
}
arr = append(arr, keyWithVotes{key, &votes.Balance})
} }
sort.Slice(arr, func(i, j int) bool { return strings.Compare(arr[i].Key, arr[j].Key) == -1 }) sort.Slice(arr, func(i, j int) bool { return strings.Compare(arr[i].Key, arr[j].Key) == -1 })
return arr, nil return arr, nil
@ -384,9 +382,10 @@ func (n *NEO) getRegisteredValidatorsCall(ic *interop.Context, _ []stackitem.Ite
// GetValidatorsInternal returns a list of current validators. // GetValidatorsInternal returns a list of current validators.
func (n *NEO) GetValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) (keys.PublicKeys, error) { func (n *NEO) GetValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) (keys.PublicKeys, error) {
standByValidators := bc.GetStandByValidators()
si := d.GetStorageItem(n.ContractID, validatorsCountKey) si := d.GetStorageItem(n.ContractID, validatorsCountKey)
if si == nil { if si == nil {
return nil, errors.New("validators count uninitialized") return standByValidators, nil
} }
validatorsCount, err := ValidatorsCountFromBytes(si.Value) validatorsCount, err := ValidatorsCountFromBytes(si.Value)
if err != nil { if err != nil {
@ -407,10 +406,6 @@ func (n *NEO) GetValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) (ke
}) })
count := validatorsCount.GetWeightedAverage() count := validatorsCount.GetWeightedAverage()
standByValidators, err := bc.GetStandByValidators()
if err != nil {
return nil, err
}
if count < len(standByValidators) { if count < len(standByValidators) {
count = len(standByValidators) count = len(standByValidators)
} }

View file

@ -148,6 +148,28 @@ func (c *nep5TokenNative) emitTransfer(ic *interop.Context, from, to *util.Uint1
ic.Notifications = append(ic.Notifications, ne) 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 { func (c *nep5TokenNative) transfer(ic *interop.Context, from, to util.Uint160, amount *big.Int) error {
if amount.Sign() == -1 { if amount.Sign() == -1 {
return errors.New("negative amount") 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") 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 isEmpty := from.Equals(to) || amount.Sign() == 0
inc := amount inc := amount
if isEmpty { if isEmpty {
@ -177,23 +193,12 @@ func (c *nep5TokenNative) transfer(ic *interop.Context, from, to util.Uint160, a
} else { } else {
inc = new(big.Int).Neg(inc) inc = new(big.Int).Neg(inc)
} }
if err := c.incBalance(ic, from, siFrom, inc); err != nil { if err := c.updateAccBalance(ic, from, inc); err != nil {
return err
}
if err := ic.DAO.PutStorageItem(c.ContractID, keyFrom, siFrom); err != nil {
return err return err
} }
if !isEmpty { if !isEmpty {
keyTo := makeAccountKey(to) if err := c.updateAccBalance(ic, to, amount); err != nil {
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 {
return err return err
} }
} }

View file

@ -104,28 +104,20 @@ func (s *NEOBalanceState) DecodeBinary(r *io.BinReader) {
if r.Err != nil { if r.Err != nil {
return return
} }
s.fromStackItem(si) r.Err = s.fromStackItem(si)
} }
func (s *NEOBalanceState) toStackItem() stackitem.Item { func (s *NEOBalanceState) toStackItem() stackitem.Item {
result := s.NEP5BalanceState.toStackItem().(*stackitem.Struct) result := s.NEP5BalanceState.toStackItem().(*stackitem.Struct)
result.Append(stackitem.NewBigInteger(big.NewInt(int64(s.BalanceHeight)))) result.Append(stackitem.NewBigInteger(big.NewInt(int64(s.BalanceHeight))))
votes := make([]stackitem.Item, len(s.Votes)) result.Append(stackitem.NewByteArray(s.Votes.Bytes()))
for i, v := range s.Votes {
votes[i] = stackitem.NewByteArray(v.Bytes())
}
result.Append(stackitem.NewArray(votes))
return result return result
} }
func (s *NEOBalanceState) fromStackItem(item stackitem.Item) { func (s *NEOBalanceState) fromStackItem(item stackitem.Item) error {
structItem := item.Value().([]stackitem.Item) structItem := item.Value().([]stackitem.Item)
s.Balance = *structItem[0].Value().(*big.Int) s.Balance = *structItem[0].Value().(*big.Int)
s.BalanceHeight = uint32(structItem[1].Value().(*big.Int).Int64()) s.BalanceHeight = uint32(structItem[1].Value().(*big.Int).Int64())
votes := structItem[2].Value().([]stackitem.Item) s.Votes = make(keys.PublicKeys, 0)
s.Votes = make([]*keys.PublicKey, len(votes)) return s.Votes.DecodeBytes(structItem[2].Value().([]byte))
for i, v := range votes {
s.Votes[i] = new(keys.PublicKey)
s.Votes[i].DecodeBytes(v.Value().([]byte))
}
} }

View file

@ -284,7 +284,7 @@ func (t *Transaction) FeePerByte() util.Fixed8 {
// transactionJSON is a wrapper for Transaction and // transactionJSON is a wrapper for Transaction and
// used for correct marhalling of transaction.Data // used for correct marhalling of transaction.Data
type transactionJSON struct { type transactionJSON struct {
TxID util.Uint256 `json:"txid"` TxID util.Uint256 `json:"hash"`
Size int `json:"size"` Size int `json:"size"`
Version uint8 `json:"version"` Version uint8 `json:"version"`
Nonce uint32 `json:"nonce"` Nonce uint32 `json:"nonce"`

View file

@ -34,7 +34,7 @@ var (
// createGenesisBlock creates a genesis block based on the given configuration. // createGenesisBlock creates a genesis block based on the given configuration.
func createGenesisBlock(cfg config.ProtocolConfiguration) (*block.Block, error) { func createGenesisBlock(cfg config.ProtocolConfiguration) (*block.Block, error) {
validators, err := getValidators(cfg) validators, err := validatorsFromConfig(cfg)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -91,7 +91,7 @@ func deployNativeContracts(magic netmode.Magic) *transaction.Transaction {
return tx 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)) validators := make([]*keys.PublicKey, len(cfg.StandbyValidators))
for i, pubKeyStr := range cfg.StandbyValidators { for i, pubKeyStr := range cfg.StandbyValidators {
pubKey, err := keys.NewPublicKeyFromString(pubKeyStr) pubKey, err := keys.NewPublicKeyFromString(pubKeyStr)

View file

@ -34,7 +34,7 @@ func TestGetConsensusAddressMainNet(t *testing.T) {
cfg, err := config.Load("../../config", netmode.MainNet) cfg, err := config.Load("../../config", netmode.MainNet)
require.NoError(t, err) require.NoError(t, err)
validators, err := getValidators(cfg.ProtocolConfiguration) validators, err := validatorsFromConfig(cfg.ProtocolConfiguration)
require.NoError(t, err) require.NoError(t, err)
script, err := getNextConsensusAddress(validators) script, err := getNextConsensusAddress(validators)

View file

@ -38,9 +38,9 @@ func GetReadOnlyContext() Context { return Context{} }
func Put(ctx Context, key interface{}, value interface{}) {} func Put(ctx Context, key interface{}, value interface{}) {}
// Get retrieves value stored for the given key using given Context. See Put // Get retrieves value stored for the given key using given Context. See Put
// documentation on possible key and value types. This function uses // documentation on possible key and value types. If the value is not present in
// `System.Storage.Get` syscall. // the database it returns nil. This function uses `System.Storage.Get` syscall.
func Get(ctx Context, key interface{}) interface{} { return 0 } func Get(ctx Context, key interface{}) interface{} { return nil }
// Delete removes key-value pair from storage by the given key using given // Delete removes key-value pair from storage by the given key using given
// Context. See Put documentation on possible key types. This function uses // Context. See Put documentation on possible key types. This function uses

View file

@ -85,7 +85,7 @@ func (chain testChain) GetNEP5Balances(util.Uint160) *state.NEP5Balances {
func (chain testChain) GetValidators() ([]*keys.PublicKey, error) { func (chain testChain) GetValidators() ([]*keys.PublicKey, error) {
panic("TODO") panic("TODO")
} }
func (chain testChain) GetStandByValidators() (keys.PublicKeys, error) { func (chain testChain) GetStandByValidators() keys.PublicKeys {
panic("TODO") panic("TODO")
} }
func (chain testChain) GetEnrollments() ([]state.Validator, error) { func (chain testChain) GetEnrollments() ([]state.Validator, error) {

View file

@ -41,13 +41,13 @@ const hexB1 = "000000008aaab19c43c4ca2870c3e616b123f1b689866f44b138ae4d6a5352e54
const hexTxMoveNeo = "0002000000abec5362f11e75b6e02e407bb98d63675d14384100000000000000003e5f0d0000000000b00400000001abec5362f11e75b6e02e407bb98d63675d14384101590218ddf5050c14316e851039019d39dfc2c37d6c3fee19fd5809870c14abec5362f11e75b6e02e407bb98d63675d14384113c00c087472616e736665720c14897720d8cd76f4f00abfa37c0edd889c208fde9b41627d5b523801fd08010c40ae6fc04fe4b6c22218ca9617c98d607d9ec9b1faf8cfdc3391bec485ae76b11adc6cc6abeb31a50b536ea8073e674d62a5566fce5e0a0ceb0718cb971c1ae3d00c40603071b725a58d052cad7afd88e99b27baab931afd5bb50d16e224335aab450170aabe251d3c0c6ad3f31dd7e9b89b209baabe5a1e2fa588bd8118f9e2a6960f0c40d72afcf39e663dba2d70fb8c36a09d1a6a6ad0d2fd38c857a8e7dc71e2b98711324e0d2ec641fe6896ba63ba80d3ea341c1aad11e082fb188ee07e215b4031b10c409afb2808b60286a56343b7ffcef28bb2ab0c595603e7323b5e5b0b9c1c10edfa5c40754d921865cb6fd71668a206b37a1eb10c0029a9fcd3a856aed07742cd3f94130c2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e0c2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd620c2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc20c2103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699140b413073b3bb" 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 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 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. // getResultBlock1 returns data for block number 1 which is used by several tests.
func getResultBlock1() *result.Block { func getResultBlock1() *result.Block {

View file

@ -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":"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":"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":"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":[]}`, `{"jsonrpc":"2.0","method":"event_missed","params":[]}`,
} }
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {

View file

@ -50,8 +50,8 @@ type rpcTestCase struct {
check func(t *testing.T, e *executor, result interface{}) check func(t *testing.T, e *executor, result interface{})
} }
const testContractHash = "e65ff7b3a02d207b584a5c27057d4e9862ef01da" const testContractHash = "10e262ef80c76bdecca287a2c047841fc02c3129"
const deploymentTxHash = "b0428600383ec7f7b06734978a24dbe81edc87b781f58c0614f122c735d8cf6a" const deploymentTxHash = "ad8b149c799d4b2337162b0ad23e0ba8845cddb9cfca8a45587ee207015d2a7c"
var rpcTestCases = map[string][]rpcTestCase{ var rpcTestCases = map[string][]rpcTestCase{
"getapplicationlog": { "getapplicationlog": {
@ -147,7 +147,7 @@ var rpcTestCases = map[string][]rpcTestCase{
}, },
{ {
Asset: e.chain.UtilityTokenHash(), Asset: e.chain.UtilityTokenHash(),
Amount: "923.96937740", Amount: "918.01738700",
LastUpdated: 6, LastUpdated: 6,
}}, }},
Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(), Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(),
@ -229,7 +229,7 @@ var rpcTestCases = map[string][]rpcTestCase{
Timestamp: blockSendNEO.Timestamp, Timestamp: blockSendNEO.Timestamp,
Asset: e.chain.UtilityTokenHash(), Asset: e.chain.UtilityTokenHash(),
Address: "", // Minted GAS. Address: "", // Minted GAS.
Amount: "23.99976000", Amount: "17.99982000",
Index: 4, Index: 4,
NotifyIndex: 0, NotifyIndex: 0,
TxHash: txSendNEOHash, TxHash: txSendNEOHash,
@ -259,6 +259,7 @@ var rpcTestCases = map[string][]rpcTestCase{
// take burned gas into account // take burned gas into account
u := testchain.PrivateKeyByID(0).GetScriptHash() u := testchain.PrivateKeyByID(0).GetScriptHash()
for i := 0; i <= int(e.chain.BlockHeight()); i++ { for i := 0; i <= int(e.chain.BlockHeight()); i++ {
var netFee util.Fixed8
h := e.chain.GetHeaderHash(i) h := e.chain.GetHeaderHash(i)
b, err := e.chain.GetBlock(h) b, err := e.chain.GetBlock(h)
require.NoError(t, err) require.NoError(t, err)
@ -274,7 +275,19 @@ var rpcTestCases = map[string][]rpcTestCase{
TxHash: b.Hash(), 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.Equal(t, expected.Address, res.Address)
require.ElementsMatch(t, expected.Sent, res.Sent) 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{}) { check: func(t *testing.T, e *executor, resp interface{}) {
s, ok := resp.(*string) s, ok := resp.(*string)
require.True(t, ok) require.True(t, ok)
// Incorrect, to be fixed later. assert.Equal(t, "36000", *s)
assert.Equal(t, "48000", *s)
}, },
}, },
}, },
@ -565,8 +577,7 @@ var rpcTestCases = map[string][]rpcTestCase{
}, },
check: func(t *testing.T, e *executor, validators interface{}) { check: func(t *testing.T, e *executor, validators interface{}) {
var expected []result.Validator var expected []result.Validator
sBValidators, err := e.chain.GetStandByValidators() sBValidators := e.chain.GetStandByValidators()
require.NoError(t, err)
for _, sbValidator := range sBValidators { for _, sbValidator := range sBValidators {
expected = append(expected, result.Validator{ expected = append(expected, result.Validator{
PublicKey: *sbValidator, PublicKey: *sbValidator,

Binary file not shown.

View file

@ -32,7 +32,11 @@ func Main(operation string, args []interface{}) interface{} {
runtime.Log("invalid address") runtime.Log("invalid address")
return false 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) runtime.Notify("balanceOf", addr, amount)
return amount return amount
case "transfer": case "transfer":
@ -53,7 +57,11 @@ func Main(operation string, args []interface{}) interface{} {
return false 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 { if fromBalance < amount {
runtime.Log("insufficient funds") runtime.Log("insufficient funds")
return false return false
@ -61,7 +69,11 @@ func Main(operation string, args []interface{}) interface{} {
fromBalance -= amount fromBalance -= amount
storage.Put(ctx, from, fromBalance) 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 toBalance += amount
storage.Put(ctx, to, toBalance) storage.Put(ctx, to, toBalance)

Binary file not shown.

View file

@ -212,7 +212,7 @@ func (p *Parameter) EncodeBinary(w *io.BinWriter) {
w.WriteBytes(p.Value.(util.Uint160).BytesBE()) w.WriteBytes(p.Value.(util.Uint160).BytesBE())
case Hash256Type: case Hash256Type:
w.WriteBytes(p.Value.(util.Uint256).BytesBE()) w.WriteBytes(p.Value.(util.Uint256).BytesBE())
case InteropInterfaceType: case InteropInterfaceType, AnyType:
default: default:
w.Err = fmt.Errorf("unknown type: %x", p.Type) w.Err = fmt.Errorf("unknown type: %x", p.Type)
} }
@ -251,7 +251,7 @@ func (p *Parameter) DecodeBinary(r *io.BinReader) {
var u util.Uint256 var u util.Uint256
r.ReadBytes(u[:]) r.ReadBytes(u[:])
p.Value = u p.Value = u
case InteropInterfaceType: case InteropInterfaceType, AnyType:
default: default:
r.Err = fmt.Errorf("unknown type: %x", p.Type) r.Err = fmt.Errorf("unknown type: %x", p.Type)
} }

View file

@ -41,6 +41,9 @@ type Context struct {
// Script hash of the prog. // Script hash of the prog.
scriptHash util.Uint160 scriptHash util.Uint160
// Caller's contract script hash.
callingScriptHash util.Uint160
// Call flags this context was created with. // Call flags this context was created with.
callFlag smartcontract.CallFlag callFlag smartcontract.CallFlag
} }

View file

@ -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 // 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. // 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) { func (v *VM) LoadScriptWithHash(b []byte, hash util.Uint160, f smartcontract.CallFlag) {
shash := v.GetCurrentScriptHash()
v.LoadScriptWithFlags(b, f) v.LoadScriptWithFlags(b, f)
ctx := v.Context() ctx := v.Context()
ctx.scriptHash = hash ctx.scriptHash = hash
ctx.callingScriptHash = shash
} }
// Context returns the current executed context. Nil if there is no context, // 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 // GetCallingScriptHash implements ScriptHashGetter interface
func (v *VM) GetCallingScriptHash() util.Uint160 { func (v *VM) GetCallingScriptHash() util.Uint160 {
return v.getContextScriptHash(1) return v.Context().callingScriptHash
} }
// GetEntryScriptHash implements ScriptHashGetter interface // GetEntryScriptHash implements ScriptHashGetter interface