core: fix Storage.Get to return Null when there is no value

Match C# implementation and fix state inconsistency at block 249920 of
preview2 testnet. Make our Go Storage.Get return nil and adapt
examples/tests.
This commit is contained in:
Roman Khimov 2020-06-24 00:06:42 +03:00
parent 954c8ff8d6
commit d81d826bfc
9 changed files with 67 additions and 30 deletions

View file

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

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

View file

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

View file

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

View file

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

View file

@ -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": {

Binary file not shown.

View file

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

Binary file not shown.