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/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/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/rpc/server/server_test.go b/pkg/rpc/server/server_test.go index 4ab2361e4..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": { 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