mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2025-01-11 21:20:36 +00:00
Merge pull request #1610 from nspcc-dev/management-contract
Management contract and other recent changes
This commit is contained in:
commit
f009e531de
72 changed files with 1484 additions and 1290 deletions
|
@ -20,8 +20,8 @@ func TestRegisterCandidate(t *testing.T) {
|
||||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||||
"--wallet", validatorWallet,
|
"--wallet", validatorWallet,
|
||||||
"--from", validatorAddr,
|
"--from", validatorAddr,
|
||||||
"neo:"+validatorPriv.Address()+":10",
|
"NEO:"+validatorPriv.Address()+":10",
|
||||||
"gas:"+validatorPriv.Address()+":100")
|
"GAS:"+validatorPriv.Address()+":100")
|
||||||
e.checkTxPersisted(t)
|
e.checkTxPersisted(t)
|
||||||
|
|
||||||
e.In.WriteString("one\r")
|
e.In.WriteString("one\r")
|
||||||
|
|
|
@ -55,8 +55,8 @@ func TestSignMultisigTx(t *testing.T) {
|
||||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||||
"--wallet", validatorWallet,
|
"--wallet", validatorWallet,
|
||||||
"--from", validatorAddr,
|
"--from", validatorAddr,
|
||||||
"neo:"+multisigAddr+":4",
|
"NEO:"+multisigAddr+":4",
|
||||||
"gas:"+multisigAddr+":1")
|
"GAS:"+multisigAddr+":1")
|
||||||
e.checkTxPersisted(t)
|
e.checkTxPersisted(t)
|
||||||
|
|
||||||
// Sign and transfer funds to another account.
|
// Sign and transfer funds to another account.
|
||||||
|
@ -69,7 +69,7 @@ func TestSignMultisigTx(t *testing.T) {
|
||||||
e.Run(t, "neo-go", "wallet", "nep17", "transfer",
|
e.Run(t, "neo-go", "wallet", "nep17", "transfer",
|
||||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||||
"--wallet", wallet1Path, "--from", multisigAddr,
|
"--wallet", wallet1Path, "--from", multisigAddr,
|
||||||
"--to", priv.Address(), "--token", "neo", "--amount", "1",
|
"--to", priv.Address(), "--token", "NEO", "--amount", "1",
|
||||||
"--out", txPath)
|
"--out", txPath)
|
||||||
|
|
||||||
e.In.WriteString("pass\r")
|
e.In.WriteString("pass\r")
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
|
@ -28,13 +29,13 @@ func TestNEP17Balance(t *testing.T) {
|
||||||
b, index := e.Chain.GetGoverningTokenBalance(validatorHash)
|
b, index := e.Chain.GetGoverningTokenBalance(validatorHash)
|
||||||
checkResult := func(t *testing.T) {
|
checkResult := func(t *testing.T) {
|
||||||
e.checkNextLine(t, "^\\s*Account\\s+"+validatorAddr)
|
e.checkNextLine(t, "^\\s*Account\\s+"+validatorAddr)
|
||||||
e.checkNextLine(t, "^\\s*NEO:\\s+NEO \\("+e.Chain.GoverningTokenHash().StringLE()+"\\)")
|
e.checkNextLine(t, "^\\s*NEO:\\s+NeoToken \\("+e.Chain.GoverningTokenHash().StringLE()+"\\)")
|
||||||
e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+b.String())
|
e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+b.String())
|
||||||
e.checkNextLine(t, "^\\s*Updated\\s*:\\s*"+strconv.FormatUint(uint64(index), 10))
|
e.checkNextLine(t, "^\\s*Updated\\s*:\\s*"+strconv.FormatUint(uint64(index), 10))
|
||||||
e.checkEOF(t)
|
e.checkEOF(t)
|
||||||
}
|
}
|
||||||
t.Run("Alias", func(t *testing.T) {
|
t.Run("Alias", func(t *testing.T) {
|
||||||
e.Run(t, append(cmd, "--token", "neo")...)
|
e.Run(t, append(cmd, "--token", "NEO")...)
|
||||||
checkResult(t)
|
checkResult(t)
|
||||||
})
|
})
|
||||||
t.Run("Hash", func(t *testing.T) {
|
t.Run("Hash", func(t *testing.T) {
|
||||||
|
@ -43,9 +44,9 @@ func TestNEP17Balance(t *testing.T) {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
t.Run("GAS", func(t *testing.T) {
|
t.Run("GAS", func(t *testing.T) {
|
||||||
e.Run(t, append(cmd, "--token", "gas")...)
|
e.Run(t, append(cmd, "--token", "GAS")...)
|
||||||
e.checkNextLine(t, "^\\s*Account\\s+"+validatorAddr)
|
e.checkNextLine(t, "^\\s*Account\\s+"+validatorAddr)
|
||||||
e.checkNextLine(t, "^\\s*GAS:\\s+GAS \\("+e.Chain.UtilityTokenHash().StringLE()+"\\)")
|
e.checkNextLine(t, "^\\s*GAS:\\s+GasToken \\("+e.Chain.UtilityTokenHash().StringLE()+"\\)")
|
||||||
b := e.Chain.GetUtilityTokenBalance(validatorHash)
|
b := e.Chain.GetUtilityTokenBalance(validatorHash)
|
||||||
e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+fixedn.Fixed8(b.Int64()).String())
|
e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+fixedn.Fixed8(b.Int64()).String())
|
||||||
})
|
})
|
||||||
|
@ -54,7 +55,7 @@ func TestNEP17Balance(t *testing.T) {
|
||||||
addr1, err := address.StringToUint160("NbTiM6h8r99kpRtb428XcsUk1TzKed2gTc")
|
addr1, err := address.StringToUint160("NbTiM6h8r99kpRtb428XcsUk1TzKed2gTc")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
e.checkNextLine(t, "^Account "+address.Uint160ToString(addr1))
|
e.checkNextLine(t, "^Account "+address.Uint160ToString(addr1))
|
||||||
e.checkNextLine(t, "^\\s*GAS:\\s+GAS \\("+e.Chain.UtilityTokenHash().StringLE()+"\\)")
|
e.checkNextLine(t, "^\\s*GAS:\\s+GasToken \\("+e.Chain.UtilityTokenHash().StringLE()+"\\)")
|
||||||
balance := e.Chain.GetUtilityTokenBalance(addr1)
|
balance := e.Chain.GetUtilityTokenBalance(addr1)
|
||||||
e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+fixedn.Fixed8(balance.Int64()).String())
|
e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+fixedn.Fixed8(balance.Int64()).String())
|
||||||
e.checkNextLine(t, "^\\s*Updated:")
|
e.checkNextLine(t, "^\\s*Updated:")
|
||||||
|
@ -72,13 +73,13 @@ func TestNEP17Balance(t *testing.T) {
|
||||||
for i := 0; i < 2; i++ {
|
for i := 0; i < 2; i++ {
|
||||||
line := e.getNextLine(t)
|
line := e.getNextLine(t)
|
||||||
if strings.Contains(line, "GAS") {
|
if strings.Contains(line, "GAS") {
|
||||||
e.checkLine(t, line, "^\\s*GAS:\\s+GAS \\("+e.Chain.UtilityTokenHash().StringLE()+"\\)")
|
e.checkLine(t, line, "^\\s*GAS:\\s+GasToken \\("+e.Chain.UtilityTokenHash().StringLE()+"\\)")
|
||||||
balance = e.Chain.GetUtilityTokenBalance(addr3)
|
balance = e.Chain.GetUtilityTokenBalance(addr3)
|
||||||
e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+fixedn.Fixed8(balance.Int64()).String())
|
e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+fixedn.Fixed8(balance.Int64()).String())
|
||||||
e.checkNextLine(t, "^\\s*Updated:")
|
e.checkNextLine(t, "^\\s*Updated:")
|
||||||
} else {
|
} else {
|
||||||
balance, index := e.Chain.GetGoverningTokenBalance(validatorHash)
|
balance, index := e.Chain.GetGoverningTokenBalance(validatorHash)
|
||||||
e.checkLine(t, line, "^\\s*NEO:\\s+NEO \\("+e.Chain.GoverningTokenHash().StringLE()+"\\)")
|
e.checkLine(t, line, "^\\s*NEO:\\s+NeoToken \\("+e.Chain.GoverningTokenHash().StringLE()+"\\)")
|
||||||
e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+balance.String())
|
e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+balance.String())
|
||||||
e.checkNextLine(t, "^\\s*Updated\\s*:\\s*"+strconv.FormatUint(uint64(index), 10))
|
e.checkNextLine(t, "^\\s*Updated\\s*:\\s*"+strconv.FormatUint(uint64(index), 10))
|
||||||
}
|
}
|
||||||
|
@ -112,7 +113,7 @@ func TestNEP17Transfer(t *testing.T) {
|
||||||
"--wallet", validatorWallet,
|
"--wallet", validatorWallet,
|
||||||
"--from", validatorAddr,
|
"--from", validatorAddr,
|
||||||
"--to", w.Accounts[0].Address,
|
"--to", w.Accounts[0].Address,
|
||||||
"--token", "neo",
|
"--token", "NEO",
|
||||||
"--amount", "1",
|
"--amount", "1",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,14 +138,14 @@ func TestNEP17MultiTransfer(t *testing.T) {
|
||||||
|
|
||||||
e := newExecutor(t, true)
|
e := newExecutor(t, true)
|
||||||
defer e.Close(t)
|
defer e.Close(t)
|
||||||
neoContractHash, err := e.Chain.GetNativeContractScriptHash("neo")
|
neoContractHash, err := e.Chain.GetNativeContractScriptHash(nativenames.Neo)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
args := []string{
|
args := []string{
|
||||||
"neo-go", "wallet", "nep17", "multitransfer",
|
"neo-go", "wallet", "nep17", "multitransfer",
|
||||||
"--rpc-endpoint", "http://" + e.RPC.Addr,
|
"--rpc-endpoint", "http://" + e.RPC.Addr,
|
||||||
"--wallet", validatorWallet,
|
"--wallet", validatorWallet,
|
||||||
"--from", validatorAddr,
|
"--from", validatorAddr,
|
||||||
"neo:" + privs[0].Address() + ":42",
|
"NEO:" + privs[0].Address() + ":42",
|
||||||
"GAS:" + privs[1].Address() + ":7",
|
"GAS:" + privs[1].Address() + ":7",
|
||||||
neoContractHash.StringLE() + ":" + privs[2].Address() + ":13",
|
neoContractHash.StringLE() + ":" + privs[2].Address() + ":13",
|
||||||
}
|
}
|
||||||
|
@ -169,9 +170,9 @@ func TestNEP17ImportToken(t *testing.T) {
|
||||||
walletPath := path.Join(tmpDir, "walletForImport.json")
|
walletPath := path.Join(tmpDir, "walletForImport.json")
|
||||||
defer os.Remove(walletPath)
|
defer os.Remove(walletPath)
|
||||||
|
|
||||||
neoContractHash, err := e.Chain.GetNativeContractScriptHash("neo")
|
neoContractHash, err := e.Chain.GetNativeContractScriptHash(nativenames.Neo)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
gasContractHash, err := e.Chain.GetNativeContractScriptHash("gas")
|
gasContractHash, err := e.Chain.GetNativeContractScriptHash(nativenames.Gas)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
e.Run(t, "neo-go", "wallet", "init", "--wallet", walletPath)
|
e.Run(t, "neo-go", "wallet", "init", "--wallet", walletPath)
|
||||||
e.Run(t, "neo-go", "wallet", "nep17", "import",
|
e.Run(t, "neo-go", "wallet", "nep17", "import",
|
||||||
|
@ -185,8 +186,8 @@ func TestNEP17ImportToken(t *testing.T) {
|
||||||
|
|
||||||
t.Run("Info", func(t *testing.T) {
|
t.Run("Info", func(t *testing.T) {
|
||||||
checkGASInfo := func(t *testing.T) {
|
checkGASInfo := func(t *testing.T) {
|
||||||
e.checkNextLine(t, "^Name:\\s*GAS")
|
e.checkNextLine(t, "^Name:\\s*GasToken")
|
||||||
e.checkNextLine(t, "^Symbol:\\s*gas")
|
e.checkNextLine(t, "^Symbol:\\s*GAS")
|
||||||
e.checkNextLine(t, "^Hash:\\s*"+gasContractHash.StringLE())
|
e.checkNextLine(t, "^Hash:\\s*"+gasContractHash.StringLE())
|
||||||
e.checkNextLine(t, "^Decimals:\\s*8")
|
e.checkNextLine(t, "^Decimals:\\s*8")
|
||||||
e.checkNextLine(t, "^Address:\\s*"+address.Uint160ToString(gasContractHash))
|
e.checkNextLine(t, "^Address:\\s*"+address.Uint160ToString(gasContractHash))
|
||||||
|
@ -202,8 +203,8 @@ func TestNEP17ImportToken(t *testing.T) {
|
||||||
checkGASInfo(t)
|
checkGASInfo(t)
|
||||||
_, err := e.Out.ReadString('\n')
|
_, err := e.Out.ReadString('\n')
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
e.checkNextLine(t, "^Name:\\s*NEO")
|
e.checkNextLine(t, "^Name:\\s*NeoToken")
|
||||||
e.checkNextLine(t, "^Symbol:\\s*neo")
|
e.checkNextLine(t, "^Symbol:\\s*NEO")
|
||||||
e.checkNextLine(t, "^Hash:\\s*"+neoContractHash.StringLE())
|
e.checkNextLine(t, "^Hash:\\s*"+neoContractHash.StringLE())
|
||||||
e.checkNextLine(t, "^Decimals:\\s*0")
|
e.checkNextLine(t, "^Decimals:\\s*0")
|
||||||
e.checkNextLine(t, "^Address:\\s*"+address.Uint160ToString(neoContractHash))
|
e.checkNextLine(t, "^Address:\\s*"+address.Uint160ToString(neoContractHash))
|
||||||
|
|
|
@ -16,17 +16,19 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/cli/options"
|
"github.com/nspcc-dev/neo-go/cli/options"
|
||||||
"github.com/nspcc-dev/neo-go/cli/paramcontext"
|
"github.com/nspcc-dev/neo-go/cli/paramcontext"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
"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/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpc/request"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
|
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/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/smartcontract/nef"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
|
@ -770,10 +772,16 @@ func contractDeploy(ctx *cli.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
txScript, err := request.CreateDeploymentScript(&nefFile, m)
|
mgmtHash, err := c.GetNativeContractHash(nativenames.Management)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.NewExitError(fmt.Errorf("failed to create deployment script: %w", err), 1)
|
return cli.NewExitError(fmt.Errorf("failed to get management contract's hash: %w", err), 1)
|
||||||
}
|
}
|
||||||
|
buf := io.NewBufBinWriter()
|
||||||
|
emit.AppCallWithOperationAndArgs(buf.BinWriter, mgmtHash, "deploy", f, manifestBytes)
|
||||||
|
if buf.Err != nil {
|
||||||
|
return cli.NewExitError(fmt.Errorf("failed to create deployment script: %w", buf.Err), 1)
|
||||||
|
}
|
||||||
|
txScript := buf.Bytes()
|
||||||
// It doesn't require any signers.
|
// It doesn't require any signers.
|
||||||
invRes, err := c.InvokeScript(txScript, nil)
|
invRes, err := c.InvokeScript(txScript, nil)
|
||||||
if err == nil && invRes.FaultException != "" {
|
if err == nil && invRes.FaultException != "" {
|
||||||
|
|
BIN
cli/testdata/chain50x2.acc
vendored
BIN
cli/testdata/chain50x2.acc
vendored
Binary file not shown.
16
cli/testdata/deploy/main.go
vendored
16
cli/testdata/deploy/main.go
vendored
|
@ -2,18 +2,28 @@ package deploy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/nspcc-dev/neo-go/cli/testdata/deploy/sub"
|
"github.com/nspcc-dev/neo-go/cli/testdata/deploy/sub"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
|
"github.com/nspcc-dev/neo-go/pkg/interop/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
var key = "key"
|
var key = "key"
|
||||||
|
|
||||||
|
const mgmtKey = "mgmt"
|
||||||
|
|
||||||
func _deploy(isUpdate bool) {
|
func _deploy(isUpdate bool) {
|
||||||
|
var value string
|
||||||
|
|
||||||
ctx := storage.GetContext()
|
ctx := storage.GetContext()
|
||||||
value := "on create"
|
|
||||||
if isUpdate {
|
if isUpdate {
|
||||||
value = "on update"
|
value = "on update"
|
||||||
|
} else {
|
||||||
|
value = "on create"
|
||||||
|
sh := runtime.GetCallingScriptHash()
|
||||||
|
storage.Put(ctx, mgmtKey, sh)
|
||||||
}
|
}
|
||||||
|
|
||||||
storage.Put(ctx, key, value)
|
storage.Put(ctx, key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,7 +34,9 @@ func Fail() {
|
||||||
|
|
||||||
// Update updates contract with the new one.
|
// Update updates contract with the new one.
|
||||||
func Update(script, manifest []byte) {
|
func Update(script, manifest []byte) {
|
||||||
contract.Update(script, manifest)
|
ctx := storage.GetReadOnlyContext()
|
||||||
|
mgmt := storage.Get(ctx, mgmtKey).(interop.Hash160)
|
||||||
|
contract.Call(mgmt, "update", script, manifest)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetValue returns stored value.
|
// GetValue returns stored value.
|
||||||
|
|
|
@ -197,7 +197,7 @@ func getNEP17Balance(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
tokenSymbol = "UNKNOWN"
|
tokenSymbol = "UNKNOWN"
|
||||||
}
|
}
|
||||||
fmt.Fprintf(ctx.App.Writer, "%s: %s (%s)\n", strings.ToUpper(tokenSymbol), tokenName, asset.StringLE())
|
fmt.Fprintf(ctx.App.Writer, "%s: %s (%s)\n", tokenSymbol, tokenName, asset.StringLE())
|
||||||
fmt.Fprintf(ctx.App.Writer, "\tAmount : %s\n", balances.Balances[i].Amount)
|
fmt.Fprintf(ctx.App.Writer, "\tAmount : %s\n", balances.Balances[i].Amount)
|
||||||
fmt.Fprintf(ctx.App.Writer, "\tUpdated: %d\n", balances.Balances[i].LastUpdated)
|
fmt.Fprintf(ctx.App.Writer, "\tUpdated: %d\n", balances.Balances[i].LastUpdated)
|
||||||
}
|
}
|
||||||
|
@ -228,7 +228,7 @@ func getMatchingTokenAux(ctx *cli.Context, get func(i int) *wallet.Token, n int,
|
||||||
var count int
|
var count int
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
t := get(i)
|
t := get(i)
|
||||||
if t != nil && (t.Name == name || t.Symbol == name || t.Address() == name || t.Hash.StringLE() == name) {
|
if t != nil && (t.Hash.StringLE() == name || t.Address() == name || t.Symbol == name || t.Name == name) {
|
||||||
if count == 1 {
|
if count == 1 {
|
||||||
printTokenInfo(ctx, token)
|
printTokenInfo(ctx, token)
|
||||||
printTokenInfo(ctx, t)
|
printTokenInfo(ctx, t)
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/cli/flags"
|
"github.com/nspcc-dev/neo-go/cli/flags"
|
||||||
"github.com/nspcc-dev/neo-go/cli/input"
|
"github.com/nspcc-dev/neo-go/cli/input"
|
||||||
"github.com/nspcc-dev/neo-go/cli/options"
|
"github.com/nspcc-dev/neo-go/cli/options"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
|
@ -99,7 +100,7 @@ func handleCandidate(ctx *cli.Context, method string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
gas := flags.Fixed8FromContext(ctx, "gas")
|
gas := flags.Fixed8FromContext(ctx, "gas")
|
||||||
neoContractHash, err := c.GetNativeContractHash("neo")
|
neoContractHash, err := c.GetNativeContractHash(nativenames.Neo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -161,7 +162,7 @@ func handleVote(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
gas := flags.Fixed8FromContext(ctx, "gas")
|
gas := flags.Fixed8FromContext(ctx, "gas")
|
||||||
neoContractHash, err := c.GetNativeContractHash("neo")
|
neoContractHash, err := c.GetNativeContractHash(nativenames.Neo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.NewExitError(err, 1)
|
return cli.NewExitError(err, 1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/cli/flags"
|
"github.com/nspcc-dev/neo-go/cli/flags"
|
||||||
"github.com/nspcc-dev/neo-go/cli/input"
|
"github.com/nspcc-dev/neo-go/cli/input"
|
||||||
"github.com/nspcc-dev/neo-go/cli/options"
|
"github.com/nspcc-dev/neo-go/cli/options"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
"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/address"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
|
@ -233,7 +234,7 @@ func claimGas(ctx *cli.Context) error {
|
||||||
return cli.NewExitError(err, 1)
|
return cli.NewExitError(err, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
neoContractHash, err := c.GetNativeContractHash("neo")
|
neoContractHash, err := c.GetNativeContractHash(nativenames.Neo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.NewExitError(err, 1)
|
return cli.NewExitError(err, 1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -182,8 +182,8 @@ func TestClaimGas(t *testing.T) {
|
||||||
"--rpc-endpoint", "http://" + e.RPC.Addr,
|
"--rpc-endpoint", "http://" + e.RPC.Addr,
|
||||||
"--wallet", validatorWallet,
|
"--wallet", validatorWallet,
|
||||||
"--from", validatorAddr,
|
"--from", validatorAddr,
|
||||||
"neo:" + w.Accounts[0].Address + ":1000",
|
"NEO:" + w.Accounts[0].Address + ":1000",
|
||||||
"gas:" + w.Accounts[0].Address + ":1000", // for tx send
|
"GAS:" + w.Accounts[0].Address + ":1000", // for tx send
|
||||||
}
|
}
|
||||||
e.In.WriteString("one\r")
|
e.In.WriteString("one\r")
|
||||||
e.Run(t, args...)
|
e.Run(t, args...)
|
||||||
|
@ -261,8 +261,8 @@ func TestImportDeployed(t *testing.T) {
|
||||||
e.Run(t, "neo-go", "wallet", "nep17", "multitransfer",
|
e.Run(t, "neo-go", "wallet", "nep17", "multitransfer",
|
||||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||||
"--wallet", validatorWallet, "--from", validatorAddr,
|
"--wallet", validatorWallet, "--from", validatorAddr,
|
||||||
"neo:"+contractAddr+":10",
|
"NEO:"+contractAddr+":10",
|
||||||
"gas:"+contractAddr+":10")
|
"GAS:"+contractAddr+":10")
|
||||||
e.checkTxPersisted(t)
|
e.checkTxPersisted(t)
|
||||||
|
|
||||||
privTo, err := keys.NewPrivateKey()
|
privTo, err := keys.NewPrivateKey()
|
||||||
|
@ -272,7 +272,7 @@ func TestImportDeployed(t *testing.T) {
|
||||||
e.Run(t, "neo-go", "wallet", "nep17", "transfer",
|
e.Run(t, "neo-go", "wallet", "nep17", "transfer",
|
||||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||||
"--wallet", walletPath, "--from", contractAddr,
|
"--wallet", walletPath, "--from", contractAddr,
|
||||||
"--to", privTo.Address(), "--token", "neo", "--amount", "1")
|
"--to", privTo.Address(), "--token", "NEO", "--amount", "1")
|
||||||
e.checkTxPersisted(t)
|
e.checkTxPersisted(t)
|
||||||
|
|
||||||
b, _ := e.Chain.GetGoverningTokenBalance(h)
|
b, _ := e.Chain.GetGoverningTokenBalance(h)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package timer
|
package timer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/interop"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/binary"
|
"github.com/nspcc-dev/neo-go/pkg/interop/binary"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
||||||
|
@ -9,6 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultTicks = 3
|
const defaultTicks = 3
|
||||||
|
const mgmtKey = "mgmt"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// ctx holds storage context for contract methods
|
// ctx holds storage context for contract methods
|
||||||
|
@ -30,6 +32,8 @@ func _deploy(isUpdate bool) {
|
||||||
runtime.Log("One more tick is added.")
|
runtime.Log("One more tick is added.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
sh := runtime.GetCallingScriptHash()
|
||||||
|
storage.Put(ctx, mgmtKey, sh)
|
||||||
storage.Put(ctx, ticksKey, defaultTicks)
|
storage.Put(ctx, ticksKey, defaultTicks)
|
||||||
i := binary.Itoa(defaultTicks, 10)
|
i := binary.Itoa(defaultTicks, 10)
|
||||||
runtime.Log("Timer set to " + i + " ticks.")
|
runtime.Log("Timer set to " + i + " ticks.")
|
||||||
|
@ -41,7 +45,8 @@ func Migrate(script []byte, manifest []byte) bool {
|
||||||
runtime.Log("Only owner is allowed to update the contract.")
|
runtime.Log("Only owner is allowed to update the contract.")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
contract.Update(script, manifest)
|
mgmt := storage.Get(ctx, mgmtKey).(interop.Hash160)
|
||||||
|
contract.Call(mgmt, "update", script, manifest)
|
||||||
runtime.Log("Contract updated.")
|
runtime.Log("Contract updated.")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -67,7 +72,8 @@ func SelfDestroy() bool {
|
||||||
runtime.Log("Only owner or the contract itself are allowed to destroy the contract.")
|
runtime.Log("Only owner or the contract itself are allowed to destroy the contract.")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
contract.Destroy()
|
mgmt := storage.Get(ctx, mgmtKey).(interop.Hash160)
|
||||||
|
contract.Call(mgmt, "destroy")
|
||||||
runtime.Log("Destroyed.")
|
runtime.Log("Destroyed.")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package testchain
|
package testchain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
gio "io"
|
gio "io"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
||||||
|
@ -12,7 +13,6 @@ import (
|
||||||
"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/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpc/request"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
|
@ -48,7 +48,7 @@ func NewTransferFromOwner(bc blockchainer.Blockchainer, contractHash, to util.Ui
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDeployTx returns new deployment tx for contract with name with Go code read from r.
|
// NewDeployTx returns new deployment tx for contract with name with Go code read from r.
|
||||||
func NewDeployTx(name string, sender util.Uint160, r gio.Reader) (*transaction.Transaction, util.Uint160, error) {
|
func NewDeployTx(bc blockchainer.Blockchainer, name string, sender util.Uint160, r gio.Reader) (*transaction.Transaction, util.Uint160, error) {
|
||||||
// nef.NewFile() cares about version a lot.
|
// nef.NewFile() cares about version a lot.
|
||||||
config.Version = "0.90.0-test"
|
config.Version = "0.90.0-test"
|
||||||
|
|
||||||
|
@ -67,12 +67,21 @@ func NewDeployTx(name string, sender util.Uint160, r gio.Reader) (*transaction.T
|
||||||
return nil, util.Uint160{}, err
|
return nil, util.Uint160{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
txScript, err := request.CreateDeploymentScript(ne, m)
|
rawManifest, err := json.Marshal(m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, util.Uint160{}, err
|
return nil, util.Uint160{}, err
|
||||||
}
|
}
|
||||||
|
neb, err := ne.Bytes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, util.Uint160{}, err
|
||||||
|
}
|
||||||
|
buf := io.NewBufBinWriter()
|
||||||
|
emit.AppCallWithOperationAndArgs(buf.BinWriter, bc.ManagementContractHash(), "deploy", neb, rawManifest)
|
||||||
|
if buf.Err != nil {
|
||||||
|
return nil, util.Uint160{}, buf.Err
|
||||||
|
}
|
||||||
|
|
||||||
tx := transaction.New(Network(), txScript, 100*native.GASFactor)
|
tx := transaction.New(Network(), buf.Bytes(), 100*native.GASFactor)
|
||||||
tx.Signers = []transaction.Signer{{Account: sender}}
|
tx.Signers = []transaction.Signer{{Account: sender}}
|
||||||
h := state.CreateContractHash(tx.Sender(), avm)
|
h := state.CreateContractHash(tx.Sender(), avm)
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,6 @@ func TestCodeGen_DebugInfo(t *testing.T) {
|
||||||
import "github.com/nspcc-dev/neo-go/pkg/interop"
|
import "github.com/nspcc-dev/neo-go/pkg/interop"
|
||||||
import "github.com/nspcc-dev/neo-go/pkg/interop/storage"
|
import "github.com/nspcc-dev/neo-go/pkg/interop/storage"
|
||||||
import "github.com/nspcc-dev/neo-go/pkg/interop/blockchain"
|
import "github.com/nspcc-dev/neo-go/pkg/interop/blockchain"
|
||||||
import "github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
|
||||||
func Main(op string) bool {
|
func Main(op string) bool {
|
||||||
var s string
|
var s string
|
||||||
_ = s
|
_ = s
|
||||||
|
@ -47,7 +46,7 @@ func MethodStruct() struct{} { return struct{}{} }
|
||||||
func unexportedMethod() int { return 1 }
|
func unexportedMethod() int { return 1 }
|
||||||
func MethodParams(addr interop.Hash160, h interop.Hash256,
|
func MethodParams(addr interop.Hash160, h interop.Hash256,
|
||||||
sig interop.Signature, pub interop.PublicKey,
|
sig interop.Signature, pub interop.PublicKey,
|
||||||
inter interop.Interface, ctr contract.Contract,
|
inter interop.Interface,
|
||||||
ctx storage.Context, tx blockchain.Transaction) bool {
|
ctx storage.Context, tx blockchain.Transaction) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -238,7 +237,6 @@ func _deploy(isUpdate bool) {}
|
||||||
manifest.NewParameter("sig", smartcontract.SignatureType),
|
manifest.NewParameter("sig", smartcontract.SignatureType),
|
||||||
manifest.NewParameter("pub", smartcontract.PublicKeyType),
|
manifest.NewParameter("pub", smartcontract.PublicKeyType),
|
||||||
manifest.NewParameter("inter", smartcontract.InteropInterfaceType),
|
manifest.NewParameter("inter", smartcontract.InteropInterfaceType),
|
||||||
manifest.NewParameter("ctr", smartcontract.ArrayType),
|
|
||||||
manifest.NewParameter("ctx", smartcontract.InteropInterfaceType),
|
manifest.NewParameter("ctx", smartcontract.InteropInterfaceType),
|
||||||
manifest.NewParameter("tx", smartcontract.ArrayType),
|
manifest.NewParameter("tx", smartcontract.ArrayType),
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package compiler_test
|
package compiler_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -18,6 +19,7 @@ import (
|
||||||
cinterop "github.com/nspcc-dev/neo-go/pkg/interop"
|
cinterop "github.com/nspcc-dev/neo-go/pkg/interop"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -132,12 +134,6 @@ func TestAppCall(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
barH := hash.Hash160(barCtr)
|
barH := hash.Hash160(barCtr)
|
||||||
ic := interop.NewContext(trigger.Application, nil, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet, false), nil, nil, nil, zaptest.NewLogger(t))
|
|
||||||
require.NoError(t, ic.DAO.PutContractState(&state.Contract{
|
|
||||||
Hash: barH,
|
|
||||||
Script: barCtr,
|
|
||||||
Manifest: *mBar,
|
|
||||||
}))
|
|
||||||
|
|
||||||
srcInner := `package foo
|
srcInner := `package foo
|
||||||
import "github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
import "github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
||||||
|
@ -164,11 +160,24 @@ func TestAppCall(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
ih := hash.Hash160(inner)
|
ih := hash.Hash160(inner)
|
||||||
require.NoError(t, ic.DAO.PutContractState(&state.Contract{
|
var contractGetter = func(_ dao.DAO, h util.Uint160) (*state.Contract, error) {
|
||||||
Hash: ih,
|
if h.Equals(ih) {
|
||||||
Script: inner,
|
return &state.Contract{
|
||||||
Manifest: *m,
|
Hash: ih,
|
||||||
}))
|
Script: inner,
|
||||||
|
Manifest: *m,
|
||||||
|
}, nil
|
||||||
|
} else if h.Equals(barH) {
|
||||||
|
return &state.Contract{
|
||||||
|
Hash: barH,
|
||||||
|
Script: barCtr,
|
||||||
|
Manifest: *mBar,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
return nil, errors.New("not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
ic := interop.NewContext(trigger.Application, nil, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet, false), contractGetter, nil, nil, nil, zaptest.NewLogger(t))
|
||||||
|
|
||||||
t.Run("valid script", func(t *testing.T) {
|
t.Run("valid script", func(t *testing.T) {
|
||||||
src := getAppCallScript(fmt.Sprintf("%#v", ih.BytesBE()))
|
src := getAppCallScript(fmt.Sprintf("%#v", ih.BytesBE()))
|
||||||
|
|
|
@ -16,7 +16,6 @@ var syscalls = map[string]map[string]string{
|
||||||
},
|
},
|
||||||
"blockchain": {
|
"blockchain": {
|
||||||
"GetBlock": interopnames.SystemBlockchainGetBlock,
|
"GetBlock": interopnames.SystemBlockchainGetBlock,
|
||||||
"GetContract": interopnames.SystemBlockchainGetContract,
|
|
||||||
"GetHeight": interopnames.SystemBlockchainGetHeight,
|
"GetHeight": interopnames.SystemBlockchainGetHeight,
|
||||||
"GetTransaction": interopnames.SystemBlockchainGetTransaction,
|
"GetTransaction": interopnames.SystemBlockchainGetTransaction,
|
||||||
"GetTransactionFromBlock": interopnames.SystemBlockchainGetTransactionFromBlock,
|
"GetTransactionFromBlock": interopnames.SystemBlockchainGetTransactionFromBlock,
|
||||||
|
@ -25,12 +24,9 @@ var syscalls = map[string]map[string]string{
|
||||||
"contract": {
|
"contract": {
|
||||||
"Call": interopnames.SystemContractCall,
|
"Call": interopnames.SystemContractCall,
|
||||||
"CallEx": interopnames.SystemContractCallEx,
|
"CallEx": interopnames.SystemContractCallEx,
|
||||||
"Create": interopnames.SystemContractCreate,
|
|
||||||
"CreateStandardAccount": interopnames.SystemContractCreateStandardAccount,
|
"CreateStandardAccount": interopnames.SystemContractCreateStandardAccount,
|
||||||
"Destroy": interopnames.SystemContractDestroy,
|
|
||||||
"IsStandard": interopnames.SystemContractIsStandard,
|
"IsStandard": interopnames.SystemContractIsStandard,
|
||||||
"GetCallFlags": interopnames.SystemContractGetCallFlags,
|
"GetCallFlags": interopnames.SystemContractGetCallFlags,
|
||||||
"Update": interopnames.SystemContractUpdate,
|
|
||||||
},
|
},
|
||||||
"crypto": {
|
"crypto": {
|
||||||
"ECDsaSecp256k1Verify": interopnames.NeoCryptoVerifyWithECDsaSecp256k1,
|
"ECDsaSecp256k1Verify": interopnames.NeoCryptoVerifyWithECDsaSecp256k1,
|
||||||
|
|
|
@ -600,18 +600,16 @@ func (bc *Blockchain) storeBlock(block *block.Block, txpool *mempool.Pool) error
|
||||||
}
|
}
|
||||||
writeBuf.Reset()
|
writeBuf.Reset()
|
||||||
|
|
||||||
if block.Index > 0 {
|
aer, err := bc.runPersist(bc.contracts.GetPersistScript(), block, cache, trigger.OnPersist)
|
||||||
aer, err := bc.runPersist(bc.contracts.GetPersistScript(), block, cache, trigger.OnPersist)
|
if err != nil {
|
||||||
if err != nil {
|
return fmt.Errorf("onPersist failed: %w", err)
|
||||||
return fmt.Errorf("onPersist failed: %w", err)
|
|
||||||
}
|
|
||||||
appExecResults = append(appExecResults, aer)
|
|
||||||
err = cache.PutAppExecResult(aer, writeBuf)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to store onPersist exec result: %w", err)
|
|
||||||
}
|
|
||||||
writeBuf.Reset()
|
|
||||||
}
|
}
|
||||||
|
appExecResults = append(appExecResults, aer)
|
||||||
|
err = cache.PutAppExecResult(aer, writeBuf)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to store onPersist exec result: %w", err)
|
||||||
|
}
|
||||||
|
writeBuf.Reset()
|
||||||
|
|
||||||
for _, tx := range block.Transactions {
|
for _, tx := range block.Transactions {
|
||||||
if err := cache.StoreAsTransaction(tx, block.Index, writeBuf); err != nil {
|
if err := cache.StoreAsTransaction(tx, block.Index, writeBuf); err != nil {
|
||||||
|
@ -673,7 +671,7 @@ func (bc *Blockchain) storeBlock(block *block.Block, txpool *mempool.Pool) error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
aer, err := bc.runPersist(bc.contracts.GetPostPersistScript(), block, cache, trigger.PostPersist)
|
aer, err = bc.runPersist(bc.contracts.GetPostPersistScript(), block, cache, trigger.PostPersist)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("postPersist failed: %w", err)
|
return fmt.Errorf("postPersist failed: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -726,21 +724,6 @@ func (bc *Blockchain) storeBlock(block *block.Block, txpool *mempool.Pool) error
|
||||||
bc.lock.Unlock()
|
bc.lock.Unlock()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := bc.contracts.Policy.OnPersistEnd(bc.dao); err != nil {
|
|
||||||
bc.lock.Unlock()
|
|
||||||
return fmt.Errorf("failed to call OnPersistEnd for Policy native contract: %w", err)
|
|
||||||
}
|
|
||||||
if bc.P2PSigExtensionsEnabled() {
|
|
||||||
err := bc.contracts.Notary.OnPersistEnd(bc.dao)
|
|
||||||
if err != nil {
|
|
||||||
bc.lock.Unlock()
|
|
||||||
return fmt.Errorf("failed to call OnPersistEnd for Notary native contract: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := bc.contracts.Designate.OnPersistEnd(bc.dao); err != nil {
|
|
||||||
bc.lock.Unlock()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
bc.dao.MPT.Flush()
|
bc.dao.MPT.Flush()
|
||||||
// Every persist cycle we also compact our in-memory MPT.
|
// Every persist cycle we also compact our in-memory MPT.
|
||||||
persistedHeight := atomic.LoadUint32(&bc.persistedHeight)
|
persistedHeight := atomic.LoadUint32(&bc.persistedHeight)
|
||||||
|
@ -843,7 +826,7 @@ func (bc *Blockchain) processNEP17Transfer(cache *dao.Cached, h util.Uint256, b
|
||||||
if nativeContract != nil {
|
if nativeContract != nil {
|
||||||
id = nativeContract.Metadata().ContractID
|
id = nativeContract.Metadata().ContractID
|
||||||
} else {
|
} else {
|
||||||
assetContract, err := cache.GetContractState(sc)
|
assetContract, err := bc.contracts.Management.GetContract(cache, sc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1143,7 +1126,7 @@ func (bc *Blockchain) HeaderHeight() uint32 {
|
||||||
|
|
||||||
// GetContractState returns contract by its script hash.
|
// GetContractState returns contract by its script hash.
|
||||||
func (bc *Blockchain) GetContractState(hash util.Uint160) *state.Contract {
|
func (bc *Blockchain) GetContractState(hash util.Uint160) *state.Contract {
|
||||||
contract, err := bc.dao.GetContractState(hash)
|
contract, err := bc.contracts.Management.GetContract(bc.dao, hash)
|
||||||
if contract == nil && err != storage.ErrKeyNotFound {
|
if contract == nil && err != storage.ErrKeyNotFound {
|
||||||
bc.log.Warn("failed to get contract state", zap.Error(err))
|
bc.log.Warn("failed to get contract state", zap.Error(err))
|
||||||
}
|
}
|
||||||
|
@ -1665,7 +1648,7 @@ func (bc *Blockchain) initVerificationVM(ic *interop.Context, hash util.Uint160,
|
||||||
}
|
}
|
||||||
v.LoadScriptWithFlags(witness.VerificationScript, smartcontract.NoneFlag)
|
v.LoadScriptWithFlags(witness.VerificationScript, smartcontract.NoneFlag)
|
||||||
} else {
|
} else {
|
||||||
cs, err := ic.DAO.GetContractState(hash)
|
cs, err := ic.GetContract(hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ErrUnknownVerificationContract
|
return ErrUnknownVerificationContract
|
||||||
}
|
}
|
||||||
|
@ -1674,10 +1657,10 @@ func (bc *Blockchain) initVerificationVM(ic *interop.Context, hash util.Uint160,
|
||||||
return ErrInvalidVerificationContract
|
return ErrInvalidVerificationContract
|
||||||
}
|
}
|
||||||
initMD := cs.Manifest.ABI.GetMethod(manifest.MethodInit)
|
initMD := cs.Manifest.ABI.GetMethod(manifest.MethodInit)
|
||||||
v.LoadScriptWithHash(cs.Script, hash, smartcontract.ReadStates|smartcontract.AllowCall)
|
v.LoadScriptWithHash(cs.Script, hash, smartcontract.ReadStates)
|
||||||
v.Jump(v.Context(), md.Offset)
|
v.Jump(v.Context(), md.Offset)
|
||||||
|
|
||||||
if cs.ID < 0 {
|
if cs.ID <= 0 {
|
||||||
w := io.NewBufBinWriter()
|
w := io.NewBufBinWriter()
|
||||||
emit.Opcodes(w.BinWriter, opcode.DEPTH, opcode.PACK)
|
emit.Opcodes(w.BinWriter, opcode.DEPTH, opcode.PACK)
|
||||||
emit.String(w.BinWriter, manifest.MethodVerify)
|
emit.String(w.BinWriter, manifest.MethodVerify)
|
||||||
|
@ -1788,6 +1771,11 @@ func (bc *Blockchain) UtilityTokenHash() util.Uint160 {
|
||||||
return bc.contracts.GAS.Hash
|
return bc.contracts.GAS.Hash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ManagementContractHash returns management contract's hash.
|
||||||
|
func (bc *Blockchain) ManagementContractHash() util.Uint160 {
|
||||||
|
return bc.contracts.Management.Hash
|
||||||
|
}
|
||||||
|
|
||||||
func hashAndIndexToBytes(h util.Uint256, index uint32) []byte {
|
func hashAndIndexToBytes(h util.Uint256, index uint32) []byte {
|
||||||
buf := io.NewBufBinWriter()
|
buf := io.NewBufBinWriter()
|
||||||
buf.WriteBytes(h.BytesLE())
|
buf.WriteBytes(h.BytesLE())
|
||||||
|
@ -1796,7 +1784,7 @@ func hashAndIndexToBytes(h util.Uint256, index uint32) []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bc *Blockchain) newInteropContext(trigger trigger.Type, d dao.DAO, block *block.Block, tx *transaction.Transaction) *interop.Context {
|
func (bc *Blockchain) newInteropContext(trigger trigger.Type, d dao.DAO, block *block.Block, tx *transaction.Transaction) *interop.Context {
|
||||||
ic := interop.NewContext(trigger, bc, d, bc.contracts.Contracts, block, tx, bc.log)
|
ic := interop.NewContext(trigger, bc, d, bc.contracts.Management.GetContract, bc.contracts.Contracts, block, tx, bc.log)
|
||||||
ic.Functions = [][]interop.Function{systemInterops, neoInterops}
|
ic.Functions = [][]interop.Function{systemInterops, neoInterops}
|
||||||
switch {
|
switch {
|
||||||
case tx != nil:
|
case tx != nil:
|
||||||
|
|
|
@ -540,7 +540,6 @@ func TestVerifyTx(t *testing.T) {
|
||||||
ic.SpawnVM()
|
ic.SpawnVM()
|
||||||
ic.VM.LoadScript([]byte{byte(opcode.RET)})
|
ic.VM.LoadScript([]byte{byte(opcode.RET)})
|
||||||
require.NoError(t, bc.contracts.Designate.DesignateAsRole(ic, native.RoleOracle, oraclePubs))
|
require.NoError(t, bc.contracts.Designate.DesignateAsRole(ic, native.RoleOracle, oraclePubs))
|
||||||
require.NoError(t, bc.contracts.Designate.OnPersistEnd(ic.DAO))
|
|
||||||
_, err = ic.DAO.Persist()
|
_, err = ic.DAO.Persist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -713,11 +712,18 @@ func TestVerifyTx(t *testing.T) {
|
||||||
require.True(t, errors.Is(bc.VerifyTx(tx), ErrHasConflicts))
|
require.True(t, errors.Is(bc.VerifyTx(tx), ErrHasConflicts))
|
||||||
})
|
})
|
||||||
t.Run("attribute on-chain conflict", func(t *testing.T) {
|
t.Run("attribute on-chain conflict", func(t *testing.T) {
|
||||||
b, err := bc.GetBlock(bc.GetHeaderHash(0))
|
tx := transaction.New(netmode.UnitTestNet, []byte{byte(opcode.PUSH1)}, 0)
|
||||||
require.NoError(t, err)
|
tx.ValidUntilBlock = 4242
|
||||||
conflictsHash := b.Transactions[0].Hash()
|
tx.Signers = []transaction.Signer{{
|
||||||
tx := getConflictsTx(conflictsHash)
|
Account: testchain.MultisigScriptHash(),
|
||||||
require.Error(t, bc.VerifyTx(tx))
|
Scopes: transaction.None,
|
||||||
|
}}
|
||||||
|
require.NoError(t, testchain.SignTx(bc, tx))
|
||||||
|
b := bc.newBlock(tx)
|
||||||
|
|
||||||
|
require.NoError(t, bc.AddBlock(b))
|
||||||
|
txConflict := getConflictsTx(tx.Hash())
|
||||||
|
require.Error(t, bc.VerifyTx(txConflict))
|
||||||
})
|
})
|
||||||
t.Run("positive", func(t *testing.T) {
|
t.Run("positive", func(t *testing.T) {
|
||||||
tx := getConflictsTx(random.Uint256())
|
tx := getConflictsTx(random.Uint256())
|
||||||
|
@ -740,7 +746,6 @@ func TestVerifyTx(t *testing.T) {
|
||||||
ic.SpawnVM()
|
ic.SpawnVM()
|
||||||
ic.VM.LoadScript([]byte{byte(opcode.RET)})
|
ic.VM.LoadScript([]byte{byte(opcode.RET)})
|
||||||
require.NoError(t, bc.contracts.Designate.DesignateAsRole(ic, native.RoleP2PNotary, keys.PublicKeys{notary.PrivateKey().PublicKey()}))
|
require.NoError(t, bc.contracts.Designate.DesignateAsRole(ic, native.RoleP2PNotary, keys.PublicKeys{notary.PrivateKey().PublicKey()}))
|
||||||
require.NoError(t, bc.contracts.Designate.OnPersistEnd(ic.DAO))
|
|
||||||
_, err = ic.DAO.Persist()
|
_, err = ic.DAO.Persist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
getNotaryAssistedTx := func(signaturesCount uint8, serviceFee int64) *transaction.Transaction {
|
getNotaryAssistedTx := func(signaturesCount uint8, serviceFee int64) *transaction.Transaction {
|
||||||
|
@ -1023,10 +1028,10 @@ func TestVerifyHashAgainstScript(t *testing.T) {
|
||||||
bc := newTestChain(t)
|
bc := newTestChain(t)
|
||||||
defer bc.Close()
|
defer bc.Close()
|
||||||
|
|
||||||
cs, csInvalid := getTestContractState()
|
cs, csInvalid := getTestContractState(bc)
|
||||||
ic := bc.newInteropContext(trigger.Verification, bc.dao, nil, nil)
|
ic := bc.newInteropContext(trigger.Verification, bc.dao, nil, nil)
|
||||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
|
||||||
require.NoError(t, ic.DAO.PutContractState(csInvalid))
|
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, csInvalid))
|
||||||
|
|
||||||
gas := bc.contracts.Policy.GetMaxVerificationGas(ic.DAO)
|
gas := bc.contracts.Policy.GetMaxVerificationGas(ic.DAO)
|
||||||
t.Run("Contract", func(t *testing.T) {
|
t.Run("Contract", func(t *testing.T) {
|
||||||
|
@ -1162,7 +1167,7 @@ func TestIsTxStillRelevant(t *testing.T) {
|
||||||
currentHeight := blockchain.GetHeight()
|
currentHeight := blockchain.GetHeight()
|
||||||
return currentHeight < %d
|
return currentHeight < %d
|
||||||
}`, bc.BlockHeight()+2) // deploy + next block
|
}`, bc.BlockHeight()+2) // deploy + next block
|
||||||
txDeploy, h, err := testchain.NewDeployTx("TestVerify", neoOwner, strings.NewReader(src))
|
txDeploy, h, err := testchain.NewDeployTx(bc, "TestVerify", neoOwner, strings.NewReader(src))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
txDeploy.ValidUntilBlock = bc.BlockHeight() + 1
|
txDeploy.ValidUntilBlock = bc.BlockHeight() + 1
|
||||||
addSigners(txDeploy)
|
addSigners(txDeploy)
|
||||||
|
|
|
@ -58,6 +58,7 @@ type Blockchainer interface {
|
||||||
GetTestVM(tx *transaction.Transaction, b *block.Block) *vm.VM
|
GetTestVM(tx *transaction.Transaction, b *block.Block) *vm.VM
|
||||||
GetTransaction(util.Uint256) (*transaction.Transaction, uint32, error)
|
GetTransaction(util.Uint256) (*transaction.Transaction, uint32, error)
|
||||||
mempool.Feer // fee interface
|
mempool.Feer // fee interface
|
||||||
|
ManagementContractHash() util.Uint160
|
||||||
PoolTx(t *transaction.Transaction, pools ...*mempool.Pool) error
|
PoolTx(t *transaction.Transaction, pools ...*mempool.Pool) error
|
||||||
PoolTxWithData(t *transaction.Transaction, data interface{}, mp *mempool.Pool, feer mempool.Feer, verificationFunction func(bc Blockchainer, t *transaction.Transaction, data interface{}) error) error
|
PoolTxWithData(t *transaction.Transaction, data interface{}, mp *mempool.Pool, feer mempool.Feer, verificationFunction func(bc Blockchainer, t *transaction.Transaction, data interface{}) error) error
|
||||||
RegisterPostBlock(f func(Blockchainer, *mempool.Pool, *block.Block))
|
RegisterPostBlock(f func(Blockchainer, *mempool.Pool, *block.Block))
|
||||||
|
|
|
@ -9,11 +9,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Cached is a data access object that mimics DAO, but has a write cache
|
// Cached is a data access object that mimics DAO, but has a write cache
|
||||||
// for accounts and read cache for contracts. These are the most frequently used
|
// for accounts and NEP17 transfer data. These are the most frequently used
|
||||||
// objects in the storeBlock().
|
// objects in the storeBlock().
|
||||||
type Cached struct {
|
type Cached struct {
|
||||||
DAO
|
DAO
|
||||||
contracts map[util.Uint160]*state.Contract
|
|
||||||
balances map[util.Uint160]*state.NEP17Balances
|
balances map[util.Uint160]*state.NEP17Balances
|
||||||
transfers map[util.Uint160]map[uint32]*state.NEP17TransferLog
|
transfers map[util.Uint160]map[uint32]*state.NEP17TransferLog
|
||||||
|
|
||||||
|
@ -22,34 +21,9 @@ type Cached struct {
|
||||||
|
|
||||||
// NewCached returns new Cached wrapping around given backing store.
|
// NewCached returns new Cached wrapping around given backing store.
|
||||||
func NewCached(d DAO) *Cached {
|
func NewCached(d DAO) *Cached {
|
||||||
ctrs := make(map[util.Uint160]*state.Contract)
|
|
||||||
balances := make(map[util.Uint160]*state.NEP17Balances)
|
balances := make(map[util.Uint160]*state.NEP17Balances)
|
||||||
transfers := make(map[util.Uint160]map[uint32]*state.NEP17TransferLog)
|
transfers := make(map[util.Uint160]map[uint32]*state.NEP17TransferLog)
|
||||||
return &Cached{d.GetWrapped(), ctrs, balances, transfers, false}
|
return &Cached{d.GetWrapped(), balances, transfers, false}
|
||||||
}
|
|
||||||
|
|
||||||
// GetContractState returns contract state from cache or underlying store.
|
|
||||||
func (cd *Cached) GetContractState(hash util.Uint160) (*state.Contract, error) {
|
|
||||||
if cd.contracts[hash] != nil {
|
|
||||||
return cd.contracts[hash], nil
|
|
||||||
}
|
|
||||||
cs, err := cd.DAO.GetContractState(hash)
|
|
||||||
if err == nil {
|
|
||||||
cd.contracts[hash] = cs
|
|
||||||
}
|
|
||||||
return cs, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// PutContractState puts given contract state into the given store.
|
|
||||||
func (cd *Cached) PutContractState(cs *state.Contract) error {
|
|
||||||
cd.contracts[cs.Hash] = cs
|
|
||||||
return cd.DAO.PutContractState(cs)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteContractState deletes given contract state in cache and backing store.
|
|
||||||
func (cd *Cached) DeleteContractState(hash util.Uint160) error {
|
|
||||||
cd.contracts[hash] = nil
|
|
||||||
return cd.DAO.DeleteContractState(hash)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNEP17Balances retrieves NEP17Balances for the acc.
|
// GetNEP17Balances retrieves NEP17Balances for the acc.
|
||||||
|
@ -105,7 +79,7 @@ func (cd *Cached) Persist() (int, error) {
|
||||||
// If the lower DAO is Cached, we only need to flush the MemCached DB.
|
// If the lower DAO is Cached, we only need to flush the MemCached DB.
|
||||||
// This actually breaks DAO interface incapsulation, but for our current
|
// This actually breaks DAO interface incapsulation, but for our current
|
||||||
// usage scenario it should be good enough if cd doesn't modify object
|
// usage scenario it should be good enough if cd doesn't modify object
|
||||||
// caches (accounts/contracts/etc) in any way.
|
// caches (accounts/transfer data) in any way.
|
||||||
if ok {
|
if ok {
|
||||||
if cd.dropNEP17Cache {
|
if cd.dropNEP17Cache {
|
||||||
lowerCache.balances = make(map[util.Uint160]*state.NEP17Balances)
|
lowerCache.balances = make(map[util.Uint160]*state.NEP17Balances)
|
||||||
|
@ -145,7 +119,6 @@ func (cd *Cached) Persist() (int, error) {
|
||||||
// GetWrapped implements DAO interface.
|
// GetWrapped implements DAO interface.
|
||||||
func (cd *Cached) GetWrapped() DAO {
|
func (cd *Cached) GetWrapped() DAO {
|
||||||
return &Cached{cd.DAO.GetWrapped(),
|
return &Cached{cd.DAO.GetWrapped(),
|
||||||
cd.contracts,
|
|
||||||
cd.balances,
|
cd.balances,
|
||||||
cd.transfers,
|
cd.transfers,
|
||||||
false,
|
false,
|
||||||
|
|
|
@ -7,51 +7,10 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
||||||
"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/core/storage"
|
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCachedDaoContracts(t *testing.T) {
|
|
||||||
store := storage.NewMemoryStore()
|
|
||||||
pdao := NewSimple(store, netmode.UnitTestNet, false)
|
|
||||||
dao := NewCached(pdao)
|
|
||||||
|
|
||||||
script := []byte{0xde, 0xad, 0xbe, 0xef}
|
|
||||||
sh := hash.Hash160(script)
|
|
||||||
_, err := dao.GetContractState(sh)
|
|
||||||
require.NotNil(t, err)
|
|
||||||
|
|
||||||
m := manifest.NewManifest("Test")
|
|
||||||
|
|
||||||
cs := &state.Contract{
|
|
||||||
ID: 123,
|
|
||||||
Hash: sh,
|
|
||||||
Script: script,
|
|
||||||
Manifest: *m,
|
|
||||||
}
|
|
||||||
|
|
||||||
require.NoError(t, dao.PutContractState(cs))
|
|
||||||
cs2, err := dao.GetContractState(sh)
|
|
||||||
require.Nil(t, err)
|
|
||||||
require.Equal(t, cs, cs2)
|
|
||||||
|
|
||||||
_, err = dao.Persist()
|
|
||||||
require.Nil(t, err)
|
|
||||||
dao2 := NewCached(pdao)
|
|
||||||
cs2, err = dao2.GetContractState(sh)
|
|
||||||
require.Nil(t, err)
|
|
||||||
require.Equal(t, cs, cs2)
|
|
||||||
|
|
||||||
require.NoError(t, dao.DeleteContractState(sh))
|
|
||||||
cs2, err = dao2.GetContractState(sh)
|
|
||||||
require.Nil(t, err)
|
|
||||||
require.Equal(t, cs, cs2)
|
|
||||||
_, err = dao.GetContractState(sh)
|
|
||||||
require.NotNil(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCachedCachedDao(t *testing.T) {
|
func TestCachedCachedDao(t *testing.T) {
|
||||||
store := storage.NewMemoryStore()
|
store := storage.NewMemoryStore()
|
||||||
// Persistent DAO to check for backing storage.
|
// Persistent DAO to check for backing storage.
|
||||||
|
|
|
@ -33,13 +33,12 @@ type DAO interface {
|
||||||
AppendAppExecResult(aer *state.AppExecResult, buf *io.BufBinWriter) error
|
AppendAppExecResult(aer *state.AppExecResult, buf *io.BufBinWriter) error
|
||||||
AppendNEP17Transfer(acc util.Uint160, index uint32, tr *state.NEP17Transfer) (bool, error)
|
AppendNEP17Transfer(acc util.Uint160, index uint32, tr *state.NEP17Transfer) (bool, error)
|
||||||
DeleteBlock(h util.Uint256, buf *io.BufBinWriter) error
|
DeleteBlock(h util.Uint256, buf *io.BufBinWriter) error
|
||||||
DeleteContractState(hash util.Uint160) error
|
DeleteContractID(id int32) error
|
||||||
DeleteStorageItem(id int32, key []byte) error
|
DeleteStorageItem(id int32, key []byte) error
|
||||||
GetAndDecode(entity io.Serializable, key []byte) error
|
GetAndDecode(entity io.Serializable, key []byte) error
|
||||||
GetAppExecResults(hash util.Uint256, trig trigger.Type) ([]state.AppExecResult, error)
|
GetAppExecResults(hash util.Uint256, trig trigger.Type) ([]state.AppExecResult, error)
|
||||||
GetBatch() *storage.MemBatch
|
GetBatch() *storage.MemBatch
|
||||||
GetBlock(hash util.Uint256) (*block.Block, error)
|
GetBlock(hash util.Uint256) (*block.Block, error)
|
||||||
GetContractState(hash util.Uint160) (*state.Contract, error)
|
|
||||||
GetContractScriptHash(id int32) (util.Uint160, error)
|
GetContractScriptHash(id int32) (util.Uint160, error)
|
||||||
GetCurrentBlockHeight() (uint32, error)
|
GetCurrentBlockHeight() (uint32, error)
|
||||||
GetCurrentHeaderHeight() (i uint32, h util.Uint256, err error)
|
GetCurrentHeaderHeight() (i uint32, h util.Uint256, err error)
|
||||||
|
@ -47,7 +46,6 @@ type DAO interface {
|
||||||
GetHeaderHashes() ([]util.Uint256, error)
|
GetHeaderHashes() ([]util.Uint256, error)
|
||||||
GetNEP17Balances(acc util.Uint160) (*state.NEP17Balances, error)
|
GetNEP17Balances(acc util.Uint160) (*state.NEP17Balances, error)
|
||||||
GetNEP17TransferLog(acc util.Uint160, index uint32) (*state.NEP17TransferLog, error)
|
GetNEP17TransferLog(acc util.Uint160, index uint32) (*state.NEP17TransferLog, error)
|
||||||
GetAndUpdateNextContractID() (int32, error)
|
|
||||||
GetStateRoot(height uint32) (*state.MPTRootState, error)
|
GetStateRoot(height uint32) (*state.MPTRootState, error)
|
||||||
PutStateRoot(root *state.MPTRootState) error
|
PutStateRoot(root *state.MPTRootState) error
|
||||||
GetStorageItem(id int32, key []byte) *state.StorageItem
|
GetStorageItem(id int32, key []byte) *state.StorageItem
|
||||||
|
@ -59,7 +57,7 @@ type DAO interface {
|
||||||
HasTransaction(hash util.Uint256) error
|
HasTransaction(hash util.Uint256) error
|
||||||
Persist() (int, error)
|
Persist() (int, error)
|
||||||
PutAppExecResult(aer *state.AppExecResult, buf *io.BufBinWriter) error
|
PutAppExecResult(aer *state.AppExecResult, buf *io.BufBinWriter) error
|
||||||
PutContractState(cs *state.Contract) error
|
PutContractID(id int32, hash util.Uint160) error
|
||||||
PutCurrentHeader(hashAndIndex []byte) error
|
PutCurrentHeader(hashAndIndex []byte) error
|
||||||
PutNEP17Balances(acc util.Uint160, bs *state.NEP17Balances) error
|
PutNEP17Balances(acc util.Uint160, bs *state.NEP17Balances) error
|
||||||
PutNEP17TransferLog(acc util.Uint160, index uint32, lg *state.NEP17TransferLog) error
|
PutNEP17TransferLog(acc util.Uint160, index uint32, lg *state.NEP17TransferLog) error
|
||||||
|
@ -125,72 +123,32 @@ func (dao *Simple) putWithBuffer(entity io.Serializable, key []byte, buf *io.Buf
|
||||||
return dao.Store.Put(key, buf.Bytes())
|
return dao.Store.Put(key, buf.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- start contracts.
|
func makeContractIDKey(id int32) []byte {
|
||||||
|
|
||||||
// GetContractState returns contract state as recorded in the given
|
|
||||||
// store by the given script hash.
|
|
||||||
func (dao *Simple) GetContractState(hash util.Uint160) (*state.Contract, error) {
|
|
||||||
contract := &state.Contract{}
|
|
||||||
key := storage.AppendPrefix(storage.STContract, hash.BytesBE())
|
|
||||||
err := dao.GetAndDecode(contract, key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return contract, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// PutContractState puts given contract state into the given store.
|
|
||||||
func (dao *Simple) PutContractState(cs *state.Contract) error {
|
|
||||||
key := storage.AppendPrefix(storage.STContract, cs.Hash.BytesBE())
|
|
||||||
if err := dao.Put(cs, key); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if cs.UpdateCounter != 0 { // Update.
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
key = key[:5]
|
|
||||||
key[0] = byte(storage.STContractID)
|
|
||||||
binary.LittleEndian.PutUint32(key[1:], uint32(cs.ID))
|
|
||||||
return dao.Store.Put(key, cs.Hash.BytesBE())
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteContractState deletes given contract state in the given store.
|
|
||||||
func (dao *Simple) DeleteContractState(hash util.Uint160) error {
|
|
||||||
key := storage.AppendPrefix(storage.STContract, hash.BytesBE())
|
|
||||||
return dao.Store.Delete(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 {
|
|
||||||
id = int32(binary.LittleEndian.Uint32(data))
|
|
||||||
} else if err != storage.ErrKeyNotFound {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
data = make([]byte, 4)
|
|
||||||
binary.LittleEndian.PutUint32(data, uint32(id+1))
|
|
||||||
return id, dao.Store.Put(key, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetContractScriptHash returns script hash of the contract with the specified ID.
|
|
||||||
// Contract with the script hash may be destroyed.
|
|
||||||
func (dao *Simple) GetContractScriptHash(id int32) (util.Uint160, error) {
|
|
||||||
key := make([]byte, 5)
|
key := make([]byte, 5)
|
||||||
key[0] = byte(storage.STContractID)
|
key[0] = byte(storage.STContractID)
|
||||||
binary.LittleEndian.PutUint32(key[1:], uint32(id))
|
binary.LittleEndian.PutUint32(key[1:], uint32(id))
|
||||||
data := &util.Uint160{}
|
return key
|
||||||
if err := dao.GetAndDecode(data, key); err != nil {
|
}
|
||||||
|
|
||||||
|
// DeleteContractID deletes contract's id to hash mapping.
|
||||||
|
func (dao *Simple) DeleteContractID(id int32) error {
|
||||||
|
return dao.Store.Delete(makeContractIDKey(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
// PutContractID adds a mapping from contract's ID to its hash.
|
||||||
|
func (dao *Simple) PutContractID(id int32, hash util.Uint160) error {
|
||||||
|
return dao.Store.Put(makeContractIDKey(id), hash.BytesBE())
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetContractScriptHash retrieves contract's hash given its ID.
|
||||||
|
func (dao *Simple) GetContractScriptHash(id int32) (util.Uint160, error) {
|
||||||
|
var data = new(util.Uint160)
|
||||||
|
if err := dao.GetAndDecode(data, makeContractIDKey(id)); err != nil {
|
||||||
return *data, err
|
return *data, err
|
||||||
}
|
}
|
||||||
return *data, nil
|
return *data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- end contracts.
|
|
||||||
|
|
||||||
// -- start nep17 balances.
|
// -- start nep17 balances.
|
||||||
|
|
||||||
// GetNEP17Balances retrieves nep17 balances from the cache.
|
// GetNEP17Balances retrieves nep17 balances from the cache.
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"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/core/storage"
|
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
|
@ -43,45 +42,6 @@ func (t *TestSerializable) DecodeBinary(reader *io.BinReader) {
|
||||||
t.field = reader.ReadString()
|
t.field = reader.ReadString()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPutAndGetContractState(t *testing.T) {
|
|
||||||
dao := NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet, false)
|
|
||||||
script := []byte{}
|
|
||||||
h := hash.Hash160(script)
|
|
||||||
contractState := &state.Contract{Hash: h, Script: script}
|
|
||||||
err := dao.PutContractState(contractState)
|
|
||||||
require.NoError(t, err)
|
|
||||||
gotContractState, err := dao.GetContractState(contractState.Hash)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, contractState, gotContractState)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeleteContractState(t *testing.T) {
|
|
||||||
dao := NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet, false)
|
|
||||||
script := []byte{}
|
|
||||||
h := hash.Hash160(script)
|
|
||||||
contractState := &state.Contract{Hash: h, Script: script}
|
|
||||||
err := dao.PutContractState(contractState)
|
|
||||||
require.NoError(t, err)
|
|
||||||
err = dao.DeleteContractState(h)
|
|
||||||
require.NoError(t, err)
|
|
||||||
gotContractState, err := dao.GetContractState(h)
|
|
||||||
require.Error(t, err)
|
|
||||||
require.Nil(t, gotContractState)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSimple_GetAndUpdateNextContractID(t *testing.T) {
|
|
||||||
dao := NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet, false)
|
|
||||||
id, err := dao.GetAndUpdateNextContractID()
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.EqualValues(t, 0, id)
|
|
||||||
id, err = dao.GetAndUpdateNextContractID()
|
|
||||||
require.NoError(t, err)
|
|
||||||
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) {
|
||||||
dao := NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet, false)
|
dao := NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet, false)
|
||||||
hash := random.Uint256()
|
hash := random.Uint256()
|
||||||
|
|
|
@ -6,9 +6,6 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StoragePrice is a price for storing 1 byte of storage.
|
|
||||||
const StoragePrice = 100000
|
|
||||||
|
|
||||||
// getPrice returns a price for executing op with the provided parameter.
|
// getPrice returns a price for executing op with the provided parameter.
|
||||||
func getPrice(v *vm.VM, op opcode.Opcode, parameter []byte) int64 {
|
func getPrice(v *vm.VM, op opcode.Opcode, parameter []byte) int64 {
|
||||||
return fee.Opcode(op)
|
return fee.Opcode(op)
|
||||||
|
|
|
@ -213,6 +213,7 @@ func TestCreateBasicChain(t *testing.T) {
|
||||||
bw := io.NewBufBinWriter()
|
bw := io.NewBufBinWriter()
|
||||||
txSendRaw.EncodeBinary(bw.BinWriter)
|
txSendRaw.EncodeBinary(bw.BinWriter)
|
||||||
t.Logf("sendrawtransaction: %s", hex.EncodeToString(bw.Bytes()))
|
t.Logf("sendrawtransaction: %s", hex.EncodeToString(bw.Bytes()))
|
||||||
|
require.False(t, saveChain)
|
||||||
}
|
}
|
||||||
|
|
||||||
func initBasicChain(t *testing.T, bc *Blockchain) {
|
func initBasicChain(t *testing.T, bc *Blockchain) {
|
||||||
|
@ -271,7 +272,7 @@ func initBasicChain(t *testing.T, bc *Blockchain) {
|
||||||
acc0 := wallet.NewAccountFromPrivateKey(priv0)
|
acc0 := wallet.NewAccountFromPrivateKey(priv0)
|
||||||
|
|
||||||
// Push some contract into the chain.
|
// Push some contract into the chain.
|
||||||
txDeploy, cHash := newDeployTx(t, priv0ScriptHash, prefix+"test_contract.go", "Rubl")
|
txDeploy, cHash := newDeployTx(t, bc, priv0ScriptHash, prefix+"test_contract.go", "Rubl")
|
||||||
txDeploy.Nonce = getNextNonce()
|
txDeploy.Nonce = getNextNonce()
|
||||||
txDeploy.ValidUntilBlock = validUntilBlock
|
txDeploy.ValidUntilBlock = validUntilBlock
|
||||||
require.NoError(t, addNetworkFee(bc, txDeploy, acc0))
|
require.NoError(t, addNetworkFee(bc, txDeploy, acc0))
|
||||||
|
@ -357,7 +358,7 @@ func initBasicChain(t *testing.T, bc *Blockchain) {
|
||||||
t.Logf("sendRublesTx: %v", transferTx.Hash().StringLE())
|
t.Logf("sendRublesTx: %v", transferTx.Hash().StringLE())
|
||||||
|
|
||||||
// Push verification contract into the chain.
|
// Push verification contract into the chain.
|
||||||
txDeploy2, _ := newDeployTx(t, priv0ScriptHash, prefix+"verification_contract.go", "Verify")
|
txDeploy2, _ := newDeployTx(t, bc, priv0ScriptHash, prefix+"verification_contract.go", "Verify")
|
||||||
txDeploy2.Nonce = getNextNonce()
|
txDeploy2.Nonce = getNextNonce()
|
||||||
txDeploy2.ValidUntilBlock = validUntilBlock
|
txDeploy2.ValidUntilBlock = validUntilBlock
|
||||||
require.NoError(t, addNetworkFee(bc, txDeploy2, acc0))
|
require.NoError(t, addNetworkFee(bc, txDeploy2, acc0))
|
||||||
|
@ -375,10 +376,10 @@ func newNEP17Transfer(sc, from, to util.Uint160, amount int64, additionalArgs ..
|
||||||
return transaction.New(testchain.Network(), script, 10000000)
|
return transaction.New(testchain.Network(), script, 10000000)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDeployTx(t *testing.T, sender util.Uint160, name, ctrName string) (*transaction.Transaction, util.Uint160) {
|
func newDeployTx(t *testing.T, bc *Blockchain, sender util.Uint160, name, ctrName string) (*transaction.Transaction, util.Uint160) {
|
||||||
c, err := ioutil.ReadFile(name)
|
c, err := ioutil.ReadFile(name)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
tx, h, err := testchain.NewDeployTx(ctrName, sender, bytes.NewReader(c))
|
tx, h, err := testchain.NewDeployTx(bc, ctrName, sender, bytes.NewReader(c))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
t.Logf("contractHash (%s): %s", name, h.StringLE())
|
t.Logf("contractHash (%s): %s", name, h.StringLE())
|
||||||
return tx, h
|
return tx, h
|
||||||
|
|
|
@ -2,6 +2,7 @@ package callback
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
|
@ -39,15 +40,15 @@ func CreateFromMethod(ic *interop.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cs, err := ic.DAO.GetContractState(h)
|
cs, err := ic.GetContract(h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("contract not found")
|
return fmt.Errorf("contract not found: %w", err)
|
||||||
}
|
}
|
||||||
method := string(ic.VM.Estack().Pop().Bytes())
|
method := string(ic.VM.Estack().Pop().Bytes())
|
||||||
if strings.HasPrefix(method, "_") {
|
if strings.HasPrefix(method, "_") {
|
||||||
return errors.New("invalid method name")
|
return errors.New("invalid method name")
|
||||||
}
|
}
|
||||||
currCs, err := ic.DAO.GetContractState(ic.VM.GetCurrentScriptHash())
|
currCs, err := ic.GetContract(ic.VM.GetCurrentScriptHash())
|
||||||
if err == nil && !currCs.Manifest.CanCall(h, &cs.Manifest, method) {
|
if err == nil && !currCs.Manifest.CanCall(h, &cs.Manifest, method) {
|
||||||
return errors.New("method call is not allowed")
|
return errors.New("method call is not allowed")
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,10 +37,13 @@ type Context struct {
|
||||||
Log *zap.Logger
|
Log *zap.Logger
|
||||||
VM *vm.VM
|
VM *vm.VM
|
||||||
Functions [][]Function
|
Functions [][]Function
|
||||||
|
getContract func(dao.DAO, util.Uint160) (*state.Contract, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewContext returns new interop context.
|
// NewContext returns new interop context.
|
||||||
func NewContext(trigger trigger.Type, bc blockchainer.Blockchainer, d dao.DAO, natives []Contract, block *block.Block, tx *transaction.Transaction, log *zap.Logger) *Context {
|
func NewContext(trigger trigger.Type, bc blockchainer.Blockchainer, d dao.DAO,
|
||||||
|
getContract func(dao.DAO, util.Uint160) (*state.Contract, error), natives []Contract,
|
||||||
|
block *block.Block, tx *transaction.Transaction, log *zap.Logger) *Context {
|
||||||
dao := dao.NewCached(d)
|
dao := dao.NewCached(d)
|
||||||
nes := make([]state.NotificationEvent, 0)
|
nes := make([]state.NotificationEvent, 0)
|
||||||
return &Context{
|
return &Context{
|
||||||
|
@ -53,7 +56,8 @@ func NewContext(trigger trigger.Type, bc blockchainer.Blockchainer, d dao.DAO, n
|
||||||
Notifications: nes,
|
Notifications: nes,
|
||||||
Log: log,
|
Log: log,
|
||||||
// Functions is a slice of slices of interops sorted by ID.
|
// Functions is a slice of slices of interops sorted by ID.
|
||||||
Functions: [][]Function{},
|
Functions: [][]Function{},
|
||||||
|
getContract: getContract,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,6 +93,8 @@ type MethodAndPrice struct {
|
||||||
type Contract interface {
|
type Contract interface {
|
||||||
Initialize(*Context) error
|
Initialize(*Context) error
|
||||||
Metadata() *ContractMD
|
Metadata() *ContractMD
|
||||||
|
OnPersist(*Context) error
|
||||||
|
PostPersist(*Context) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContractMD represents native contract instance.
|
// ContractMD represents native contract instance.
|
||||||
|
@ -110,7 +116,7 @@ func NewContractMD(name string) *ContractMD {
|
||||||
|
|
||||||
w := io.NewBufBinWriter()
|
w := io.NewBufBinWriter()
|
||||||
emit.String(w.BinWriter, c.Name)
|
emit.String(w.BinWriter, c.Name)
|
||||||
emit.Syscall(w.BinWriter, interopnames.NeoNativeCall)
|
emit.Syscall(w.BinWriter, interopnames.SystemContractCallNative)
|
||||||
|
|
||||||
c.Script = w.Bytes()
|
c.Script = w.Bytes()
|
||||||
c.Hash = hash.Hash160(c.Script)
|
c.Hash = hash.Hash160(c.Script)
|
||||||
|
@ -140,6 +146,11 @@ func Sort(fs []Function) {
|
||||||
sort.Slice(fs, func(i, j int) bool { return fs[i].ID < fs[j].ID })
|
sort.Slice(fs, func(i, j int) bool { return fs[i].ID < fs[j].ID })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetContract returns contract by its hash in current interop context.
|
||||||
|
func (ic *Context) GetContract(hash util.Uint160) (*state.Contract, error) {
|
||||||
|
return ic.getContract(ic.DAO, hash)
|
||||||
|
}
|
||||||
|
|
||||||
// GetFunction returns metadata for interop with the specified id.
|
// GetFunction returns metadata for interop with the specified id.
|
||||||
func (ic *Context) GetFunction(id uint32) *Function {
|
func (ic *Context) GetFunction(id uint32) *Function {
|
||||||
for _, slice := range ic.Functions {
|
for _, slice := range ic.Functions {
|
||||||
|
|
|
@ -39,9 +39,9 @@ func callExInternal(ic *interop.Context, h []byte, name string, args []stackitem
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("invalid contract hash")
|
return errors.New("invalid contract hash")
|
||||||
}
|
}
|
||||||
cs, err := ic.DAO.GetContractState(u)
|
cs, err := ic.GetContract(u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("contract not found")
|
return fmt.Errorf("contract not found: %w", err)
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(name, "_") {
|
if strings.HasPrefix(name, "_") {
|
||||||
return errors.New("invalid method name (starts with '_')")
|
return errors.New("invalid method name (starts with '_')")
|
||||||
|
@ -53,7 +53,7 @@ func callExInternal(ic *interop.Context, h []byte, name string, args []stackitem
|
||||||
if md.Safe {
|
if md.Safe {
|
||||||
f &^= smartcontract.WriteStates
|
f &^= smartcontract.WriteStates
|
||||||
} else if ctx := ic.VM.Context(); ctx != nil && ctx.IsDeployed() {
|
} else if ctx := ic.VM.Context(); ctx != nil && ctx.IsDeployed() {
|
||||||
curr, err := ic.DAO.GetContractState(ic.VM.GetCurrentScriptHash())
|
curr, err := ic.GetContract(ic.VM.GetCurrentScriptHash())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if !curr.Manifest.CanCall(u, &cs.Manifest, name) {
|
if !curr.Manifest.CanCall(u, &cs.Manifest, name) {
|
||||||
return errors.New("disallowed method call")
|
return errors.New("disallowed method call")
|
||||||
|
|
|
@ -11,7 +11,6 @@ const (
|
||||||
SystemBinaryItoa = "System.Binary.Itoa"
|
SystemBinaryItoa = "System.Binary.Itoa"
|
||||||
SystemBinarySerialize = "System.Binary.Serialize"
|
SystemBinarySerialize = "System.Binary.Serialize"
|
||||||
SystemBlockchainGetBlock = "System.Blockchain.GetBlock"
|
SystemBlockchainGetBlock = "System.Blockchain.GetBlock"
|
||||||
SystemBlockchainGetContract = "System.Blockchain.GetContract"
|
|
||||||
SystemBlockchainGetHeight = "System.Blockchain.GetHeight"
|
SystemBlockchainGetHeight = "System.Blockchain.GetHeight"
|
||||||
SystemBlockchainGetTransaction = "System.Blockchain.GetTransaction"
|
SystemBlockchainGetTransaction = "System.Blockchain.GetTransaction"
|
||||||
SystemBlockchainGetTransactionFromBlock = "System.Blockchain.GetTransactionFromBlock"
|
SystemBlockchainGetTransactionFromBlock = "System.Blockchain.GetTransactionFromBlock"
|
||||||
|
@ -22,12 +21,12 @@ const (
|
||||||
SystemCallbackInvoke = "System.Callback.Invoke"
|
SystemCallbackInvoke = "System.Callback.Invoke"
|
||||||
SystemContractCall = "System.Contract.Call"
|
SystemContractCall = "System.Contract.Call"
|
||||||
SystemContractCallEx = "System.Contract.CallEx"
|
SystemContractCallEx = "System.Contract.CallEx"
|
||||||
SystemContractCreate = "System.Contract.Create"
|
SystemContractCallNative = "System.Contract.CallNative"
|
||||||
SystemContractCreateStandardAccount = "System.Contract.CreateStandardAccount"
|
SystemContractCreateStandardAccount = "System.Contract.CreateStandardAccount"
|
||||||
SystemContractDestroy = "System.Contract.Destroy"
|
|
||||||
SystemContractIsStandard = "System.Contract.IsStandard"
|
SystemContractIsStandard = "System.Contract.IsStandard"
|
||||||
SystemContractGetCallFlags = "System.Contract.GetCallFlags"
|
SystemContractGetCallFlags = "System.Contract.GetCallFlags"
|
||||||
SystemContractUpdate = "System.Contract.Update"
|
SystemContractNativeOnPersist = "System.Contract.NativeOnPersist"
|
||||||
|
SystemContractNativePostPersist = "System.Contract.NativePostPersist"
|
||||||
SystemEnumeratorConcat = "System.Enumerator.Concat"
|
SystemEnumeratorConcat = "System.Enumerator.Concat"
|
||||||
SystemEnumeratorCreate = "System.Enumerator.Create"
|
SystemEnumeratorCreate = "System.Enumerator.Create"
|
||||||
SystemEnumeratorNext = "System.Enumerator.Next"
|
SystemEnumeratorNext = "System.Enumerator.Next"
|
||||||
|
@ -66,8 +65,6 @@ const (
|
||||||
NeoCryptoCheckMultisigWithECDsaSecp256k1 = "Neo.Crypto.CheckMultisigWithECDsaSecp256k1"
|
NeoCryptoCheckMultisigWithECDsaSecp256k1 = "Neo.Crypto.CheckMultisigWithECDsaSecp256k1"
|
||||||
NeoCryptoSHA256 = "Neo.Crypto.SHA256"
|
NeoCryptoSHA256 = "Neo.Crypto.SHA256"
|
||||||
NeoCryptoRIPEMD160 = "Neo.Crypto.RIPEMD160"
|
NeoCryptoRIPEMD160 = "Neo.Crypto.RIPEMD160"
|
||||||
NeoNativeCall = "Neo.Native.Call"
|
|
||||||
NeoNativeDeploy = "Neo.Native.Deploy"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var names = []string{
|
var names = []string{
|
||||||
|
@ -80,7 +77,6 @@ var names = []string{
|
||||||
SystemBinaryItoa,
|
SystemBinaryItoa,
|
||||||
SystemBinarySerialize,
|
SystemBinarySerialize,
|
||||||
SystemBlockchainGetBlock,
|
SystemBlockchainGetBlock,
|
||||||
SystemBlockchainGetContract,
|
|
||||||
SystemBlockchainGetHeight,
|
SystemBlockchainGetHeight,
|
||||||
SystemBlockchainGetTransaction,
|
SystemBlockchainGetTransaction,
|
||||||
SystemBlockchainGetTransactionFromBlock,
|
SystemBlockchainGetTransactionFromBlock,
|
||||||
|
@ -91,12 +87,12 @@ var names = []string{
|
||||||
SystemCallbackInvoke,
|
SystemCallbackInvoke,
|
||||||
SystemContractCall,
|
SystemContractCall,
|
||||||
SystemContractCallEx,
|
SystemContractCallEx,
|
||||||
SystemContractCreate,
|
SystemContractCallNative,
|
||||||
SystemContractCreateStandardAccount,
|
SystemContractCreateStandardAccount,
|
||||||
SystemContractDestroy,
|
|
||||||
SystemContractIsStandard,
|
SystemContractIsStandard,
|
||||||
SystemContractGetCallFlags,
|
SystemContractGetCallFlags,
|
||||||
SystemContractUpdate,
|
SystemContractNativeOnPersist,
|
||||||
|
SystemContractNativePostPersist,
|
||||||
SystemEnumeratorConcat,
|
SystemEnumeratorConcat,
|
||||||
SystemEnumeratorCreate,
|
SystemEnumeratorCreate,
|
||||||
SystemEnumeratorNext,
|
SystemEnumeratorNext,
|
||||||
|
@ -135,6 +131,4 @@ var names = []string{
|
||||||
NeoCryptoCheckMultisigWithECDsaSecp256k1,
|
NeoCryptoCheckMultisigWithECDsaSecp256k1,
|
||||||
NeoCryptoSHA256,
|
NeoCryptoSHA256,
|
||||||
NeoCryptoRIPEMD160,
|
NeoCryptoRIPEMD160,
|
||||||
NeoNativeCall,
|
|
||||||
NeoNativeDeploy,
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
@ -22,13 +21,13 @@ func CheckHashedWitness(ic *interop.Context, hash util.Uint160) (bool, error) {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
if tx, ok := ic.Container.(*transaction.Transaction); ok {
|
if tx, ok := ic.Container.(*transaction.Transaction); ok {
|
||||||
return checkScope(ic.DAO, tx, ic.VM, hash)
|
return checkScope(ic, tx, ic.VM, hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
return false, errors.New("script container is not a transaction")
|
return false, errors.New("script container is not a transaction")
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkScope(d dao.DAO, tx *transaction.Transaction, v *vm.VM, hash util.Uint160) (bool, error) {
|
func checkScope(ic *interop.Context, tx *transaction.Transaction, v *vm.VM, hash util.Uint160) (bool, error) {
|
||||||
for _, c := range tx.Signers {
|
for _, c := range tx.Signers {
|
||||||
if c.Account == hash {
|
if c.Account == hash {
|
||||||
if c.Scopes == transaction.Global {
|
if c.Scopes == transaction.Global {
|
||||||
|
@ -57,9 +56,9 @@ func checkScope(d dao.DAO, tx *transaction.Transaction, v *vm.VM, hash util.Uint
|
||||||
if !v.Context().GetCallFlags().Has(smartcontract.ReadStates) {
|
if !v.Context().GetCallFlags().Has(smartcontract.ReadStates) {
|
||||||
return false, errors.New("missing ReadStates call flag")
|
return false, errors.New("missing ReadStates call flag")
|
||||||
}
|
}
|
||||||
cs, err := d.GetContractState(callingScriptHash)
|
cs, err := ic.GetContract(callingScriptHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, fmt.Errorf("unable to find calling script: %w", err)
|
||||||
}
|
}
|
||||||
// check if the current group is the required one
|
// check if the current group is the required one
|
||||||
for _, allowedGroup := range c.AllowedGroups {
|
for _, allowedGroup := range c.AllowedGroups {
|
||||||
|
|
|
@ -2,33 +2,15 @@ package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/contract"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
|
||||||
"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/nef"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
// MaxContractDescriptionLen is the maximum length for contract description.
|
|
||||||
MaxContractDescriptionLen = 65536
|
|
||||||
// MaxContractScriptSize is the maximum script size for a contract.
|
|
||||||
MaxContractScriptSize = 1024 * 1024
|
|
||||||
// MaxContractParametersNum is the maximum number of parameters for a contract.
|
|
||||||
MaxContractParametersNum = 252
|
|
||||||
// MaxContractStringLen is the maximum length for contract metadata strings.
|
|
||||||
MaxContractStringLen = 252
|
|
||||||
)
|
|
||||||
|
|
||||||
var errGasLimitExceeded = errors.New("gas limit exceeded")
|
var errGasLimitExceeded = errors.New("gas limit exceeded")
|
||||||
|
|
||||||
// storageFind finds stored key-value pair.
|
// storageFind finds stored key-value pair.
|
||||||
|
@ -58,136 +40,3 @@ func storageFind(ic *interop.Context) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getNefAndManifestFromVM pops NEF and manifest from the VM's evaluation stack,
|
|
||||||
// does a lot of checks and returns deserialized structures if succeeds.
|
|
||||||
func getNefAndManifestFromVM(v *vm.VM) (*nef.File, *manifest.Manifest, error) {
|
|
||||||
// Always pop both elements.
|
|
||||||
nefBytes := v.Estack().Pop().BytesOrNil()
|
|
||||||
manifestBytes := v.Estack().Pop().BytesOrNil()
|
|
||||||
|
|
||||||
if err := checkNonEmpty(nefBytes, math.MaxInt32); err != nil { // Upper limits are checked during NEF deserialization.
|
|
||||||
return nil, nil, fmt.Errorf("invalid NEF file: %w", err)
|
|
||||||
}
|
|
||||||
if err := checkNonEmpty(manifestBytes, manifest.MaxManifestSize); err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("invalid manifest: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !v.AddGas(int64(StoragePrice * (len(nefBytes) + len(manifestBytes)))) {
|
|
||||||
return nil, nil, errGasLimitExceeded
|
|
||||||
}
|
|
||||||
var resManifest *manifest.Manifest
|
|
||||||
var resNef *nef.File
|
|
||||||
if nefBytes != nil {
|
|
||||||
nf, err := nef.FileFromBytes(nefBytes)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("invalid NEF file: %w", err)
|
|
||||||
}
|
|
||||||
resNef = &nf
|
|
||||||
}
|
|
||||||
if manifestBytes != nil {
|
|
||||||
resManifest = new(manifest.Manifest)
|
|
||||||
err := json.Unmarshal(manifestBytes, resManifest)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, fmt.Errorf("invalid manifest: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return resNef, resManifest, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// contractCreate creates a contract.
|
|
||||||
func contractCreate(ic *interop.Context) error {
|
|
||||||
neff, manif, err := getNefAndManifestFromVM(ic.VM)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if neff == nil {
|
|
||||||
return errors.New("no valid NEF provided")
|
|
||||||
}
|
|
||||||
if manif == nil {
|
|
||||||
return errors.New("no valid manifest provided")
|
|
||||||
}
|
|
||||||
if ic.Tx == nil {
|
|
||||||
return errors.New("no transaction provided")
|
|
||||||
}
|
|
||||||
h := state.CreateContractHash(ic.Tx.Sender(), neff.Script)
|
|
||||||
contract, err := ic.DAO.GetContractState(h)
|
|
||||||
if contract != nil && err == nil {
|
|
||||||
return errors.New("contract already exists")
|
|
||||||
}
|
|
||||||
if !manif.IsValid(h) {
|
|
||||||
return errors.New("failed to check contract script hash against manifest")
|
|
||||||
}
|
|
||||||
id, err := ic.DAO.GetAndUpdateNextContractID()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
newcontract := &state.Contract{
|
|
||||||
ID: id,
|
|
||||||
Hash: h,
|
|
||||||
Script: neff.Script,
|
|
||||||
Manifest: *manif,
|
|
||||||
}
|
|
||||||
if err := ic.DAO.PutContractState(newcontract); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
cs, err := contractToStackItem(newcontract)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot convert contract to stack item: %w", err)
|
|
||||||
}
|
|
||||||
ic.VM.Estack().PushVal(cs)
|
|
||||||
return callDeploy(ic, newcontract, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkNonEmpty(b []byte, max int) error {
|
|
||||||
if b != nil {
|
|
||||||
if l := len(b); l == 0 {
|
|
||||||
return errors.New("empty")
|
|
||||||
} else if l > max {
|
|
||||||
return fmt.Errorf("len is %d (max %d)", l, max)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// contractUpdate migrates a contract. This method assumes that Manifest and Script
|
|
||||||
// of the contract can be updated independently.
|
|
||||||
func contractUpdate(ic *interop.Context) error {
|
|
||||||
neff, manif, err := getNefAndManifestFromVM(ic.VM)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if neff == nil && manif == nil {
|
|
||||||
return errors.New("both NEF and manifest are nil")
|
|
||||||
}
|
|
||||||
contract, _ := ic.DAO.GetContractState(ic.VM.GetCurrentScriptHash())
|
|
||||||
if contract == nil {
|
|
||||||
return errors.New("contract doesn't exist")
|
|
||||||
}
|
|
||||||
// if NEF was provided, update the contract script
|
|
||||||
if neff != nil {
|
|
||||||
contract.Script = neff.Script
|
|
||||||
}
|
|
||||||
// if manifest was provided, update the contract manifest
|
|
||||||
if manif != nil {
|
|
||||||
contract.Manifest = *manif
|
|
||||||
if !contract.Manifest.IsValid(contract.Hash) {
|
|
||||||
return errors.New("failed to check contract script hash against new manifest")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
contract.UpdateCounter++
|
|
||||||
|
|
||||||
if err := ic.DAO.PutContractState(contract); err != nil {
|
|
||||||
return fmt.Errorf("failed to update contract: %w", err)
|
|
||||||
}
|
|
||||||
return callDeploy(ic, contract, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func callDeploy(ic *interop.Context, cs *state.Contract, isUpdate bool) error {
|
|
||||||
md := cs.Manifest.ABI.GetMethod(manifest.MethodDeploy)
|
|
||||||
if md != nil {
|
|
||||||
return contract.CallExInternal(ic, cs, manifest.MethodDeploy,
|
|
||||||
[]stackitem.Item{stackitem.NewBool(isUpdate)}, smartcontract.All, vm.EnsureIsEmpty)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ func TestStorageFind(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
require.NoError(t, context.DAO.PutContractState(contractState))
|
require.NoError(t, chain.contracts.Management.PutContractState(chain.dao, contractState))
|
||||||
|
|
||||||
id := contractState.ID
|
id := contractState.ID
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@ package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
@ -12,6 +11,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer"
|
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
||||||
"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/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
@ -93,41 +93,6 @@ func bcGetBlock(ic *interop.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// contractToStackItem converts state.Contract to stackitem.Item
|
|
||||||
func contractToStackItem(cs *state.Contract) (stackitem.Item, error) {
|
|
||||||
manifest, err := json.Marshal(cs.Manifest)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return stackitem.NewArray([]stackitem.Item{
|
|
||||||
stackitem.Make(cs.ID),
|
|
||||||
stackitem.Make(cs.UpdateCounter),
|
|
||||||
stackitem.NewByteArray(cs.Hash.BytesBE()),
|
|
||||||
stackitem.NewByteArray(cs.Script),
|
|
||||||
stackitem.NewByteArray(manifest),
|
|
||||||
}), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// bcGetContract returns contract.
|
|
||||||
func bcGetContract(ic *interop.Context) error {
|
|
||||||
hashbytes := ic.VM.Estack().Pop().Bytes()
|
|
||||||
hash, err := util.Uint160DecodeBytesBE(hashbytes)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
cs, err := ic.DAO.GetContractState(hash)
|
|
||||||
if err != nil {
|
|
||||||
ic.VM.Estack().PushVal(stackitem.Null{})
|
|
||||||
} else {
|
|
||||||
item, err := contractToStackItem(cs)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
ic.VM.Estack().PushVal(item)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// bcGetHeight returns blockchain height.
|
// bcGetHeight returns blockchain height.
|
||||||
func bcGetHeight(ic *interop.Context) error {
|
func bcGetHeight(ic *interop.Context) error {
|
||||||
ic.VM.Estack().PushVal(ic.Chain.BlockHeight())
|
ic.VM.Estack().PushVal(ic.Chain.BlockHeight())
|
||||||
|
@ -274,7 +239,7 @@ func storageGetReadOnlyContext(ic *interop.Context) error {
|
||||||
// storageGetContextInternal is internal version of storageGetContext and
|
// storageGetContextInternal is internal version of storageGetContext and
|
||||||
// storageGetReadOnlyContext which allows to specify ReadOnly context flag.
|
// storageGetReadOnlyContext which allows to specify ReadOnly context flag.
|
||||||
func storageGetContextInternal(ic *interop.Context, isReadOnly bool) error {
|
func storageGetContextInternal(ic *interop.Context, isReadOnly bool) error {
|
||||||
contract, err := ic.DAO.GetContractState(ic.VM.GetCurrentScriptHash())
|
contract, err := ic.GetContract(ic.VM.GetCurrentScriptHash())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -311,7 +276,7 @@ func putWithContextAndFlags(ic *interop.Context, stc *StorageContext, key []byte
|
||||||
sizeInc = (len(si.Value)-1)/4 + 1 + len(value) - len(si.Value)
|
sizeInc = (len(si.Value)-1)/4 + 1 + len(value) - len(si.Value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !ic.VM.AddGas(int64(sizeInc) * StoragePrice) {
|
if !ic.VM.AddGas(int64(sizeInc) * native.StoragePrice) {
|
||||||
return errGasLimitExceeded
|
return errGasLimitExceeded
|
||||||
}
|
}
|
||||||
si.Value = value
|
si.Value = value
|
||||||
|
@ -363,27 +328,6 @@ func storageContextAsReadOnly(ic *interop.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// contractDestroy destroys a contract.
|
|
||||||
func contractDestroy(ic *interop.Context) error {
|
|
||||||
hash := ic.VM.GetCurrentScriptHash()
|
|
||||||
cs, err := ic.DAO.GetContractState(hash)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
err = ic.DAO.DeleteContractState(hash)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
siMap, err := ic.DAO.GetStorageItems(cs.ID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for k := range siMap {
|
|
||||||
_ = ic.DAO.DeleteStorageItem(cs.ID, []byte(k))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// contractIsStandard checks if contract is standard (sig or multisig) contract.
|
// contractIsStandard checks if contract is standard (sig or multisig) contract.
|
||||||
func contractIsStandard(ic *interop.Context) error {
|
func contractIsStandard(ic *interop.Context) error {
|
||||||
h := ic.VM.Estack().Pop().Bytes()
|
h := ic.VM.Estack().Pop().Bytes()
|
||||||
|
@ -392,7 +336,7 @@ func contractIsStandard(ic *interop.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var result bool
|
var result bool
|
||||||
cs, _ := ic.DAO.GetContractState(u)
|
cs, _ := ic.GetContract(u)
|
||||||
if cs != nil {
|
if cs != nil {
|
||||||
result = vm.IsStandardContract(cs.Script)
|
result = vm.IsStandardContract(cs.Script)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,20 +1,19 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/nspcc-dev/dbft/crypto"
|
"github.com/nspcc-dev/dbft/crypto"
|
||||||
"github.com/nspcc-dev/neo-go/internal/random"
|
"github.com/nspcc-dev/neo-go/internal/random"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/callback"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/callback"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/contract"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/contract"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
|
||||||
"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/native"
|
||||||
"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/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
|
@ -22,7 +21,6 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"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/smartcontract/nef"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
|
@ -228,7 +226,7 @@ func TestContractIsStandard(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
pub := priv.PublicKey()
|
pub := priv.PublicKey()
|
||||||
err = ic.DAO.PutContractState(&state.Contract{ID: 42, Hash: pub.GetScriptHash(), Script: pub.GetVerificationScript()})
|
err = chain.contracts.Management.PutContractState(ic.DAO, &state.Contract{ID: 42, Hash: pub.GetScriptHash(), Script: pub.GetVerificationScript()})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
v.Estack().PushVal(pub.GetScriptHash().BytesBE())
|
v.Estack().PushVal(pub.GetScriptHash().BytesBE())
|
||||||
|
@ -237,7 +235,7 @@ func TestContractIsStandard(t *testing.T) {
|
||||||
})
|
})
|
||||||
t.Run("contract stored, false", func(t *testing.T) {
|
t.Run("contract stored, false", func(t *testing.T) {
|
||||||
script := []byte{byte(opcode.PUSHT)}
|
script := []byte{byte(opcode.PUSHT)}
|
||||||
require.NoError(t, ic.DAO.PutContractState(&state.Contract{ID: 24, Hash: hash.Hash160(script), Script: script}))
|
require.NoError(t, chain.contracts.Management.PutContractState(ic.DAO, &state.Contract{ID: 24, Hash: hash.Hash160(script), Script: script}))
|
||||||
|
|
||||||
v.Estack().PushVal(crypto.Hash160(script).BytesBE())
|
v.Estack().PushVal(crypto.Hash160(script).BytesBE())
|
||||||
require.NoError(t, contractIsStandard(ic))
|
require.NoError(t, contractIsStandard(ic))
|
||||||
|
@ -266,25 +264,74 @@ func TestContractCreateAccount(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBlockchainGetContractState(t *testing.T) {
|
func TestRuntimeGasLeft(t *testing.T) {
|
||||||
v, cs, ic, bc := createVMAndContractState(t)
|
v, ic, chain := createVM(t)
|
||||||
defer bc.Close()
|
defer chain.Close()
|
||||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
|
||||||
|
|
||||||
t.Run("positive", func(t *testing.T) {
|
v.GasLimit = 100
|
||||||
v.Estack().PushVal(cs.Hash.BytesBE())
|
v.AddGas(58)
|
||||||
require.NoError(t, bcGetContract(ic))
|
require.NoError(t, runtime.GasLeft(ic))
|
||||||
|
require.EqualValues(t, 42, v.Estack().Pop().BigInt().Int64())
|
||||||
|
}
|
||||||
|
|
||||||
actual := v.Estack().Pop().Item()
|
func TestRuntimeGetNotifications(t *testing.T) {
|
||||||
compareContractStates(t, cs, actual)
|
v, ic, chain := createVM(t)
|
||||||
|
defer chain.Close()
|
||||||
|
|
||||||
|
ic.Notifications = []state.NotificationEvent{
|
||||||
|
{ScriptHash: util.Uint160{1}, Name: "Event1", Item: stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray([]byte{11})})},
|
||||||
|
{ScriptHash: util.Uint160{2}, Name: "Event2", Item: stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray([]byte{22})})},
|
||||||
|
{ScriptHash: util.Uint160{1}, Name: "Event1", Item: stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray([]byte{33})})},
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("NoFilter", func(t *testing.T) {
|
||||||
|
v.Estack().PushVal(stackitem.Null{})
|
||||||
|
require.NoError(t, runtime.GetNotifications(ic))
|
||||||
|
|
||||||
|
arr := v.Estack().Pop().Array()
|
||||||
|
require.Equal(t, len(ic.Notifications), len(arr))
|
||||||
|
for i := range arr {
|
||||||
|
elem := arr[i].Value().([]stackitem.Item)
|
||||||
|
require.Equal(t, ic.Notifications[i].ScriptHash.BytesBE(), elem[0].Value())
|
||||||
|
name, err := stackitem.ToString(elem[1])
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, ic.Notifications[i].Name, name)
|
||||||
|
require.Equal(t, ic.Notifications[i].Item, elem[2])
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("uncknown contract state", func(t *testing.T) {
|
t.Run("WithFilter", func(t *testing.T) {
|
||||||
v.Estack().PushVal(util.Uint160{1, 2, 3}.BytesBE())
|
h := util.Uint160{2}.BytesBE()
|
||||||
require.NoError(t, bcGetContract(ic))
|
v.Estack().PushVal(h)
|
||||||
|
require.NoError(t, runtime.GetNotifications(ic))
|
||||||
|
|
||||||
actual := v.Estack().Pop().Item()
|
arr := v.Estack().Pop().Array()
|
||||||
require.Equal(t, stackitem.Null{}, actual)
|
require.Equal(t, 1, len(arr))
|
||||||
|
elem := arr[0].Value().([]stackitem.Item)
|
||||||
|
require.Equal(t, h, elem[0].Value())
|
||||||
|
name, err := stackitem.ToString(elem[1])
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, ic.Notifications[1].Name, name)
|
||||||
|
require.Equal(t, ic.Notifications[1].Item, elem[2])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRuntimeGetInvocationCounter(t *testing.T) {
|
||||||
|
v, ic, chain := createVM(t)
|
||||||
|
defer chain.Close()
|
||||||
|
|
||||||
|
ic.VM.Invocations[hash.Hash160([]byte{2})] = 42
|
||||||
|
|
||||||
|
t.Run("No invocations", func(t *testing.T) {
|
||||||
|
v.LoadScript([]byte{1})
|
||||||
|
// do not return an error in this case.
|
||||||
|
require.NoError(t, runtime.GetInvocationCounter(ic))
|
||||||
|
require.EqualValues(t, 1, v.Estack().Pop().BigInt().Int64())
|
||||||
|
})
|
||||||
|
t.Run("NonZero", func(t *testing.T) {
|
||||||
|
v.LoadScript([]byte{2})
|
||||||
|
require.NoError(t, runtime.GetInvocationCounter(ic))
|
||||||
|
require.EqualValues(t, 42, v.Estack().Pop().BigInt().Int64())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,7 +339,7 @@ func TestStoragePut(t *testing.T) {
|
||||||
_, cs, ic, bc := createVMAndContractState(t)
|
_, cs, ic, bc := createVMAndContractState(t)
|
||||||
defer bc.Close()
|
defer bc.Close()
|
||||||
|
|
||||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
require.NoError(t, bc.contracts.Management.PutContractState(ic.DAO, cs))
|
||||||
|
|
||||||
initVM := func(t *testing.T, key, value []byte, gas int64) {
|
initVM := func(t *testing.T, key, value []byte, gas int64) {
|
||||||
v := ic.SpawnVM()
|
v := ic.SpawnVM()
|
||||||
|
@ -304,23 +351,23 @@ func TestStoragePut(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("create, not enough gas", func(t *testing.T) {
|
t.Run("create, not enough gas", func(t *testing.T) {
|
||||||
initVM(t, []byte{1}, []byte{2, 3}, 2*StoragePrice)
|
initVM(t, []byte{1}, []byte{2, 3}, 2*native.StoragePrice)
|
||||||
err := storagePut(ic)
|
err := storagePut(ic)
|
||||||
require.True(t, errors.Is(err, errGasLimitExceeded), "got: %v", err)
|
require.True(t, errors.Is(err, errGasLimitExceeded), "got: %v", err)
|
||||||
})
|
})
|
||||||
|
|
||||||
initVM(t, []byte{4}, []byte{5, 6}, 3*StoragePrice)
|
initVM(t, []byte{4}, []byte{5, 6}, 3*native.StoragePrice)
|
||||||
require.NoError(t, storagePut(ic))
|
require.NoError(t, storagePut(ic))
|
||||||
|
|
||||||
t.Run("update", func(t *testing.T) {
|
t.Run("update", func(t *testing.T) {
|
||||||
t.Run("not enough gas", func(t *testing.T) {
|
t.Run("not enough gas", func(t *testing.T) {
|
||||||
initVM(t, []byte{4}, []byte{5, 6, 7, 8}, StoragePrice)
|
initVM(t, []byte{4}, []byte{5, 6, 7, 8}, native.StoragePrice)
|
||||||
err := storagePut(ic)
|
err := storagePut(ic)
|
||||||
require.True(t, errors.Is(err, errGasLimitExceeded), "got: %v", err)
|
require.True(t, errors.Is(err, errGasLimitExceeded), "got: %v", err)
|
||||||
})
|
})
|
||||||
initVM(t, []byte{4}, []byte{5, 6, 7, 8}, 3*StoragePrice)
|
initVM(t, []byte{4}, []byte{5, 6, 7, 8}, 3*native.StoragePrice)
|
||||||
require.NoError(t, storagePut(ic))
|
require.NoError(t, storagePut(ic))
|
||||||
initVM(t, []byte{4}, []byte{5, 6}, StoragePrice)
|
initVM(t, []byte{4}, []byte{5, 6}, native.StoragePrice)
|
||||||
require.NoError(t, storagePut(ic))
|
require.NoError(t, storagePut(ic))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -365,7 +412,7 @@ func TestStorageDelete(t *testing.T) {
|
||||||
v, cs, ic, bc := createVMAndContractState(t)
|
v, cs, ic, bc := createVMAndContractState(t)
|
||||||
defer bc.Close()
|
defer bc.Close()
|
||||||
|
|
||||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
require.NoError(t, bc.contracts.Management.PutContractState(ic.DAO, cs))
|
||||||
v.LoadScriptWithHash(cs.Script, cs.Hash, smartcontract.All)
|
v.LoadScriptWithHash(cs.Script, cs.Hash, smartcontract.All)
|
||||||
put := func(key, value string, flag int) {
|
put := func(key, value string, flag int) {
|
||||||
v.Estack().PushVal(flag)
|
v.Estack().PushVal(flag)
|
||||||
|
@ -403,7 +450,9 @@ func TestStorageDelete(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// getTestContractState returns 2 contracts second of which is allowed to call the first.
|
// getTestContractState returns 2 contracts second of which is allowed to call the first.
|
||||||
func getTestContractState() (*state.Contract, *state.Contract) {
|
func getTestContractState(bc *Blockchain) (*state.Contract, *state.Contract) {
|
||||||
|
mgmtHash := bc.ManagementContractHash()
|
||||||
|
|
||||||
w := io.NewBufBinWriter()
|
w := io.NewBufBinWriter()
|
||||||
emit.Opcodes(w.BinWriter, opcode.ABORT)
|
emit.Opcodes(w.BinWriter, opcode.ABORT)
|
||||||
addOff := w.Len()
|
addOff := w.Len()
|
||||||
|
@ -446,6 +495,17 @@ func getTestContractState() (*state.Contract, *state.Contract) {
|
||||||
emit.String(w.BinWriter, "LastPayment")
|
emit.String(w.BinWriter, "LastPayment")
|
||||||
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeNotify)
|
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeNotify)
|
||||||
emit.Opcodes(w.BinWriter, opcode.RET)
|
emit.Opcodes(w.BinWriter, opcode.RET)
|
||||||
|
updateOff := w.Len()
|
||||||
|
emit.Int(w.BinWriter, 2)
|
||||||
|
emit.Opcodes(w.BinWriter, opcode.PACK)
|
||||||
|
emit.String(w.BinWriter, "update")
|
||||||
|
emit.AppCall(w.BinWriter, mgmtHash)
|
||||||
|
emit.Opcodes(w.BinWriter, opcode.RET)
|
||||||
|
destroyOff := w.Len()
|
||||||
|
emit.Opcodes(w.BinWriter, opcode.NEWARRAY0)
|
||||||
|
emit.String(w.BinWriter, "destroy")
|
||||||
|
emit.AppCall(w.BinWriter, mgmtHash)
|
||||||
|
emit.Opcodes(w.BinWriter, opcode.RET)
|
||||||
|
|
||||||
script := w.Bytes()
|
script := w.Bytes()
|
||||||
h := hash.Hash160(script)
|
h := hash.Hash160(script)
|
||||||
|
@ -530,6 +590,20 @@ func getTestContractState() (*state.Contract, *state.Contract) {
|
||||||
},
|
},
|
||||||
ReturnType: smartcontract.VoidType,
|
ReturnType: smartcontract.VoidType,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "update",
|
||||||
|
Offset: updateOff,
|
||||||
|
Parameters: []manifest.Parameter{
|
||||||
|
manifest.NewParameter("nef", smartcontract.ByteArrayType),
|
||||||
|
manifest.NewParameter("manifest", smartcontract.ByteArrayType),
|
||||||
|
},
|
||||||
|
ReturnType: smartcontract.VoidType,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "destroy",
|
||||||
|
Offset: destroyOff,
|
||||||
|
ReturnType: smartcontract.VoidType,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
cs := &state.Contract{
|
cs := &state.Contract{
|
||||||
Script: script,
|
Script: script,
|
||||||
|
@ -579,9 +653,9 @@ func TestContractCall(t *testing.T) {
|
||||||
_, ic, bc := createVM(t)
|
_, ic, bc := createVM(t)
|
||||||
defer bc.Close()
|
defer bc.Close()
|
||||||
|
|
||||||
cs, currCs := getTestContractState()
|
cs, currCs := getTestContractState(bc)
|
||||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
require.NoError(t, bc.contracts.Management.PutContractState(ic.DAO, cs))
|
||||||
require.NoError(t, ic.DAO.PutContractState(currCs))
|
require.NoError(t, bc.contracts.Management.PutContractState(ic.DAO, currCs))
|
||||||
|
|
||||||
currScript := currCs.Script
|
currScript := currCs.Script
|
||||||
h := hash.Hash160(cs.Script)
|
h := hash.Hash160(cs.Script)
|
||||||
|
@ -682,325 +756,6 @@ func TestContractCall(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContractCreate(t *testing.T) {
|
|
||||||
v, cs, ic, bc := createVMAndContractState(t)
|
|
||||||
v.GasLimit = -1
|
|
||||||
defer bc.Close()
|
|
||||||
|
|
||||||
// nef.NewFile() cares about version a lot.
|
|
||||||
config.Version = "0.90.0-test"
|
|
||||||
|
|
||||||
ne, err := nef.NewFile(cs.Script)
|
|
||||||
require.NoError(t, err)
|
|
||||||
neb, err := ne.Bytes()
|
|
||||||
require.NoError(t, err)
|
|
||||||
priv, err := keys.NewPrivateKey()
|
|
||||||
require.NoError(t, err)
|
|
||||||
sender := util.Uint160{1, 2, 3}
|
|
||||||
h := state.CreateContractHash(sender, ne.Script)
|
|
||||||
sig := priv.Sign(h.BytesBE())
|
|
||||||
cs.Manifest.Groups = []manifest.Group{{
|
|
||||||
PublicKey: priv.PublicKey(),
|
|
||||||
Signature: sig,
|
|
||||||
}}
|
|
||||||
m, err := json.Marshal(cs.Manifest)
|
|
||||||
require.NoError(t, err)
|
|
||||||
putArgsOnStack := func() {
|
|
||||||
v.Estack().PushVal(m)
|
|
||||||
v.Estack().PushVal(neb)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("no tx", func(t *testing.T) {
|
|
||||||
putArgsOnStack()
|
|
||||||
|
|
||||||
require.Error(t, contractCreate(ic))
|
|
||||||
})
|
|
||||||
|
|
||||||
ic.Tx = transaction.New(netmode.UnitTestNet, []byte{1}, 0)
|
|
||||||
ic.Tx.Signers = append(ic.Tx.Signers, transaction.Signer{Account: sender})
|
|
||||||
cs.ID = 0
|
|
||||||
cs.Hash = state.CreateContractHash(sender, cs.Script)
|
|
||||||
|
|
||||||
t.Run("missing NEF", func(t *testing.T) {
|
|
||||||
v.Estack().PushVal(m)
|
|
||||||
v.Estack().PushVal(stackitem.Null{})
|
|
||||||
require.Error(t, contractCreate(ic))
|
|
||||||
})
|
|
||||||
t.Run("missing manifest", func(t *testing.T) {
|
|
||||||
v.Estack().PushVal(stackitem.Null{})
|
|
||||||
v.Estack().PushVal(neb)
|
|
||||||
require.Error(t, contractCreate(ic))
|
|
||||||
})
|
|
||||||
t.Run("invalid manifest (empty)", func(t *testing.T) {
|
|
||||||
v.Estack().PushVal([]byte{})
|
|
||||||
v.Estack().PushVal(neb)
|
|
||||||
require.Error(t, contractCreate(ic))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("invalid manifest (group signature)", func(t *testing.T) {
|
|
||||||
cs.Manifest.Groups[0].Signature = make([]byte, 11)
|
|
||||||
rawManif, err := json.Marshal(cs.Manifest)
|
|
||||||
require.NoError(t, err)
|
|
||||||
v.Estack().PushVal(rawManif)
|
|
||||||
v.Estack().PushVal(neb)
|
|
||||||
require.Error(t, contractCreate(ic))
|
|
||||||
})
|
|
||||||
|
|
||||||
cs.Manifest.Groups[0].Signature = sig
|
|
||||||
t.Run("positive", func(t *testing.T) {
|
|
||||||
putArgsOnStack()
|
|
||||||
|
|
||||||
require.NoError(t, contractCreate(ic))
|
|
||||||
actual := v.Estack().Pop().Item()
|
|
||||||
compareContractStates(t, cs, actual)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("contract already exists", func(t *testing.T) {
|
|
||||||
putArgsOnStack()
|
|
||||||
|
|
||||||
require.Error(t, contractCreate(ic))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func compareContractStates(t *testing.T, expected *state.Contract, actual stackitem.Item) {
|
|
||||||
act, ok := actual.Value().([]stackitem.Item)
|
|
||||||
require.True(t, ok)
|
|
||||||
|
|
||||||
expectedManifest, err := json.Marshal(expected.Manifest)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
require.Equal(t, 5, len(act))
|
|
||||||
require.Equal(t, expected.ID, int32(act[0].Value().(*big.Int).Int64()))
|
|
||||||
require.Equal(t, expected.UpdateCounter, uint16(act[1].Value().(*big.Int).Int64()))
|
|
||||||
require.Equal(t, expected.Hash.BytesBE(), act[2].Value().([]byte))
|
|
||||||
require.Equal(t, expected.Script, act[3].Value().([]byte))
|
|
||||||
require.Equal(t, expectedManifest, act[4].Value().([]byte))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContractUpdate(t *testing.T) {
|
|
||||||
v, cs, ic, bc := createVMAndContractState(t)
|
|
||||||
defer bc.Close()
|
|
||||||
v.GasLimit = -1
|
|
||||||
|
|
||||||
putArgsOnStack := func(script, manifest interface{}) {
|
|
||||||
v.Estack().PushVal(manifest)
|
|
||||||
b, ok := script.([]byte)
|
|
||||||
if ok {
|
|
||||||
ne, err := nef.NewFile(b)
|
|
||||||
require.NoError(t, err)
|
|
||||||
script, err = ne.Bytes()
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
v.Estack().PushVal(script)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("no args", func(t *testing.T) {
|
|
||||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
|
||||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.Hash, smartcontract.All)
|
|
||||||
putArgsOnStack(stackitem.Null{}, stackitem.Null{})
|
|
||||||
require.Error(t, contractUpdate(ic))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("no contract", func(t *testing.T) {
|
|
||||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
|
||||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, util.Uint160{8, 9, 7}, smartcontract.All)
|
|
||||||
putArgsOnStack([]byte{1}, stackitem.Null{})
|
|
||||||
require.Error(t, contractUpdate(ic))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("too large script", func(t *testing.T) {
|
|
||||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
|
||||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.Hash, smartcontract.All)
|
|
||||||
putArgsOnStack(make([]byte, MaxContractScriptSize+1), stackitem.Null{})
|
|
||||||
require.Error(t, contractUpdate(ic))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("too large manifest", func(t *testing.T) {
|
|
||||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
|
||||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.Hash, smartcontract.All)
|
|
||||||
putArgsOnStack(stackitem.Null{}, make([]byte, manifest.MaxManifestSize+1))
|
|
||||||
require.Error(t, contractUpdate(ic))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("gas limit exceeded", func(t *testing.T) {
|
|
||||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
|
||||||
v.GasLimit = 0
|
|
||||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.Hash, smartcontract.All)
|
|
||||||
putArgsOnStack([]byte{1}, []byte{2})
|
|
||||||
require.Error(t, contractUpdate(ic))
|
|
||||||
})
|
|
||||||
|
|
||||||
v.GasLimit = -1
|
|
||||||
t.Run("update script, positive", func(t *testing.T) {
|
|
||||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
|
||||||
t.Run("empty manifest", func(t *testing.T) {
|
|
||||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.Hash, smartcontract.All)
|
|
||||||
newScript := []byte{9, 8, 7, 6, 5}
|
|
||||||
putArgsOnStack(newScript, []byte{})
|
|
||||||
require.Error(t, contractUpdate(ic))
|
|
||||||
})
|
|
||||||
|
|
||||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.Hash, smartcontract.All)
|
|
||||||
newScript := []byte{9, 8, 7, 6, 5}
|
|
||||||
putArgsOnStack(newScript, stackitem.Null{})
|
|
||||||
|
|
||||||
require.NoError(t, contractUpdate(ic))
|
|
||||||
|
|
||||||
// updated contract should have the same scripthash
|
|
||||||
actual, err := ic.DAO.GetContractState(cs.Hash)
|
|
||||||
require.NoError(t, err)
|
|
||||||
expected := &state.Contract{
|
|
||||||
ID: cs.ID,
|
|
||||||
UpdateCounter: 1,
|
|
||||||
Hash: cs.Hash,
|
|
||||||
Script: newScript,
|
|
||||||
Manifest: cs.Manifest,
|
|
||||||
}
|
|
||||||
require.Equal(t, expected, actual)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("update manifest, bad manifest", func(t *testing.T) {
|
|
||||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
|
||||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.Hash, smartcontract.All)
|
|
||||||
putArgsOnStack(stackitem.Null{}, []byte{1, 2, 3})
|
|
||||||
|
|
||||||
require.Error(t, contractUpdate(ic))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("update manifest, positive", func(t *testing.T) {
|
|
||||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
|
||||||
manifest := &manifest.Manifest{
|
|
||||||
ABI: manifest.ABI{},
|
|
||||||
}
|
|
||||||
manifestBytes, err := json.Marshal(manifest)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
t.Run("empty script", func(t *testing.T) {
|
|
||||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.Hash, smartcontract.All)
|
|
||||||
putArgsOnStack([]byte{}, manifestBytes)
|
|
||||||
require.Error(t, contractUpdate(ic))
|
|
||||||
})
|
|
||||||
|
|
||||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.Hash, smartcontract.All)
|
|
||||||
putArgsOnStack(stackitem.Null{}, manifestBytes)
|
|
||||||
require.NoError(t, contractUpdate(ic))
|
|
||||||
|
|
||||||
// updated contract should have old scripthash
|
|
||||||
actual, err := ic.DAO.GetContractState(cs.Hash)
|
|
||||||
require.NoError(t, err)
|
|
||||||
expected := &state.Contract{
|
|
||||||
ID: cs.ID,
|
|
||||||
UpdateCounter: 2,
|
|
||||||
Hash: cs.Hash,
|
|
||||||
Script: cs.Script,
|
|
||||||
Manifest: *manifest,
|
|
||||||
}
|
|
||||||
require.Equal(t, expected, actual)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("update both script and manifest", func(t *testing.T) {
|
|
||||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
|
||||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.Hash, smartcontract.All)
|
|
||||||
newScript := []byte{12, 13, 14}
|
|
||||||
newManifest := manifest.Manifest{
|
|
||||||
ABI: manifest.ABI{},
|
|
||||||
}
|
|
||||||
newManifestBytes, err := json.Marshal(newManifest)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
putArgsOnStack(newScript, newManifestBytes)
|
|
||||||
|
|
||||||
require.NoError(t, contractUpdate(ic))
|
|
||||||
|
|
||||||
// updated contract should have new script and manifest
|
|
||||||
actual, err := ic.DAO.GetContractState(cs.Hash)
|
|
||||||
require.NoError(t, err)
|
|
||||||
expected := &state.Contract{
|
|
||||||
ID: cs.ID,
|
|
||||||
UpdateCounter: 3,
|
|
||||||
Hash: cs.Hash,
|
|
||||||
Script: newScript,
|
|
||||||
Manifest: newManifest,
|
|
||||||
}
|
|
||||||
require.Equal(t, expected, actual)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContractDestroy(t *testing.T) {
|
|
||||||
v, cs, ic, bc := createVMAndContractState(t)
|
|
||||||
defer bc.Close()
|
|
||||||
|
|
||||||
v.LoadScriptWithHash(cs.Script, cs.Hash, smartcontract.All)
|
|
||||||
require.NoError(t, contractDestroy(ic)) // silent error when contract is missing
|
|
||||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
|
||||||
|
|
||||||
v.Estack().PushVal("value")
|
|
||||||
v.Estack().PushVal("key")
|
|
||||||
require.NoError(t, storageGetContext(ic))
|
|
||||||
require.NoError(t, storagePut(ic))
|
|
||||||
require.NotNil(t, ic.DAO.GetStorageItem(cs.ID, []byte("key")))
|
|
||||||
require.NoError(t, contractDestroy(ic))
|
|
||||||
require.Nil(t, ic.DAO.GetStorageItem(cs.ID, []byte("key")))
|
|
||||||
require.Error(t, storageGetContext(ic))
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestContractCreateDeploy checks that `_deploy` method was called
|
|
||||||
// during contract creation or update.
|
|
||||||
func TestContractCreateDeploy(t *testing.T) {
|
|
||||||
v, ic, bc := createVM(t)
|
|
||||||
defer bc.Close()
|
|
||||||
v.GasLimit = -1
|
|
||||||
|
|
||||||
putArgs := func(cs *state.Contract) {
|
|
||||||
rawManifest, err := json.Marshal(cs.Manifest)
|
|
||||||
require.NoError(t, err)
|
|
||||||
v.Estack().PushVal(rawManifest)
|
|
||||||
ne, err := nef.NewFile(cs.Script)
|
|
||||||
require.NoError(t, err)
|
|
||||||
b, err := ne.Bytes()
|
|
||||||
require.NoError(t, err)
|
|
||||||
v.Estack().PushVal(b)
|
|
||||||
}
|
|
||||||
cs, currCs := getTestContractState()
|
|
||||||
|
|
||||||
ic.Tx = transaction.New(netmode.UnitTestNet, []byte{1}, 0)
|
|
||||||
var sender = util.Uint160{1, 2, 3}
|
|
||||||
ic.Tx.Signers = append(ic.Tx.Signers, transaction.Signer{Account: sender})
|
|
||||||
v.LoadScriptWithFlags([]byte{byte(opcode.RET)}, smartcontract.All)
|
|
||||||
putArgs(cs)
|
|
||||||
require.NoError(t, contractCreate(ic))
|
|
||||||
require.NoError(t, ic.VM.Run())
|
|
||||||
|
|
||||||
cs.Hash = state.CreateContractHash(sender, cs.Script)
|
|
||||||
v.LoadScriptWithHash(currCs.Script, cs.Hash, smartcontract.All)
|
|
||||||
err := contract.CallExInternal(ic, cs, "getValue", nil, smartcontract.All, vm.EnsureNotEmpty)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NoError(t, v.Run())
|
|
||||||
require.Equal(t, "create", v.Estack().Pop().String())
|
|
||||||
|
|
||||||
v.LoadScriptWithHash(cs.Script, cs.Hash, smartcontract.All)
|
|
||||||
md := cs.Manifest.ABI.GetMethod("justReturn")
|
|
||||||
v.Jump(v.Context(), md.Offset)
|
|
||||||
|
|
||||||
t.Run("Update", func(t *testing.T) {
|
|
||||||
newCs := &state.Contract{
|
|
||||||
ID: cs.ID,
|
|
||||||
Hash: cs.Hash,
|
|
||||||
Script: append(cs.Script, byte(opcode.RET)),
|
|
||||||
Manifest: cs.Manifest,
|
|
||||||
}
|
|
||||||
putArgs(newCs)
|
|
||||||
require.NoError(t, contractUpdate(ic))
|
|
||||||
require.NoError(t, v.Run())
|
|
||||||
|
|
||||||
v.LoadScriptWithHash(currCs.Script, cs.Hash, smartcontract.All)
|
|
||||||
err = contract.CallExInternal(ic, newCs, "getValue", nil, smartcontract.All, vm.EnsureNotEmpty)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NoError(t, v.Run())
|
|
||||||
require.Equal(t, "update", v.Estack().Pop().String())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContractGetCallFlags(t *testing.T) {
|
func TestContractGetCallFlags(t *testing.T) {
|
||||||
v, ic, bc := createVM(t)
|
v, ic, bc := createVM(t)
|
||||||
defer bc.Close()
|
defer bc.Close()
|
||||||
|
@ -1049,12 +804,12 @@ func TestMethodCallback(t *testing.T) {
|
||||||
_, ic, bc := createVM(t)
|
_, ic, bc := createVM(t)
|
||||||
defer bc.Close()
|
defer bc.Close()
|
||||||
|
|
||||||
cs, currCs := getTestContractState()
|
cs, currCs := getTestContractState(bc)
|
||||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
require.NoError(t, bc.contracts.Management.PutContractState(ic.DAO, cs))
|
||||||
require.NoError(t, ic.DAO.PutContractState(currCs))
|
require.NoError(t, bc.contracts.Management.PutContractState(ic.DAO, currCs))
|
||||||
|
|
||||||
ic.Functions = append(ic.Functions, systemInterops)
|
ic.Functions = append(ic.Functions, systemInterops)
|
||||||
rawHash := hash.Hash160(cs.Script).BytesBE()
|
rawHash := cs.Hash.BytesBE()
|
||||||
|
|
||||||
t.Run("Invalid", func(t *testing.T) {
|
t.Run("Invalid", func(t *testing.T) {
|
||||||
runInvalid := func(args ...interface{}) func(t *testing.T) {
|
runInvalid := func(args ...interface{}) func(t *testing.T) {
|
||||||
|
@ -1318,7 +1073,7 @@ func TestRuntimeCheckWitness(t *testing.T) {
|
||||||
Groups: []manifest.Group{{PublicKey: pk.PublicKey()}},
|
Groups: []manifest.Group{{PublicKey: pk.PublicKey()}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
require.NoError(t, ic.DAO.PutContractState(contractState))
|
require.NoError(t, bc.contracts.Management.PutContractState(ic.DAO, contractState))
|
||||||
loadScriptWithHashAndFlags(ic, contractScript, contractScriptHash, smartcontract.All)
|
loadScriptWithHashAndFlags(ic, contractScript, contractScriptHash, smartcontract.All)
|
||||||
ic.VM.LoadScriptWithHash([]byte{0x1}, random.Uint160(), smartcontract.ReadStates)
|
ic.VM.LoadScriptWithHash([]byte{0x1}, random.Uint160(), smartcontract.ReadStates)
|
||||||
ic.Container = tx
|
ic.Container = tx
|
||||||
|
|
|
@ -43,8 +43,6 @@ var systemInterops = []interop.Function{
|
||||||
{Name: interopnames.SystemBinarySerialize, Func: binary.Serialize, Price: 100000, ParamCount: 1},
|
{Name: interopnames.SystemBinarySerialize, Func: binary.Serialize, Price: 100000, ParamCount: 1},
|
||||||
{Name: interopnames.SystemBlockchainGetBlock, Func: bcGetBlock, Price: 2500000,
|
{Name: interopnames.SystemBlockchainGetBlock, Func: bcGetBlock, Price: 2500000,
|
||||||
RequiredFlags: smartcontract.ReadStates, ParamCount: 1},
|
RequiredFlags: smartcontract.ReadStates, ParamCount: 1},
|
||||||
{Name: interopnames.SystemBlockchainGetContract, Func: bcGetContract, Price: 1000000,
|
|
||||||
RequiredFlags: smartcontract.ReadStates, ParamCount: 1},
|
|
||||||
{Name: interopnames.SystemBlockchainGetHeight, Func: bcGetHeight, Price: 400,
|
{Name: interopnames.SystemBlockchainGetHeight, Func: bcGetHeight, Price: 400,
|
||||||
RequiredFlags: smartcontract.ReadStates},
|
RequiredFlags: smartcontract.ReadStates},
|
||||||
{Name: interopnames.SystemBlockchainGetTransaction, Func: bcGetTransaction, Price: 1000000,
|
{Name: interopnames.SystemBlockchainGetTransaction, Func: bcGetTransaction, Price: 1000000,
|
||||||
|
@ -61,14 +59,12 @@ var systemInterops = []interop.Function{
|
||||||
RequiredFlags: smartcontract.AllowCall, ParamCount: 3, DisallowCallback: true},
|
RequiredFlags: smartcontract.AllowCall, ParamCount: 3, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemContractCallEx, Func: contract.CallEx, Price: 1000000,
|
{Name: interopnames.SystemContractCallEx, Func: contract.CallEx, Price: 1000000,
|
||||||
RequiredFlags: smartcontract.AllowCall, ParamCount: 4, DisallowCallback: true},
|
RequiredFlags: smartcontract.AllowCall, ParamCount: 4, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemContractCreate, Func: contractCreate, Price: 0,
|
{Name: interopnames.SystemContractCallNative, Func: native.Call, Price: 0, ParamCount: 1, DisallowCallback: true},
|
||||||
RequiredFlags: smartcontract.WriteStates, ParamCount: 2, DisallowCallback: true},
|
|
||||||
{Name: interopnames.SystemContractCreateStandardAccount, Func: contractCreateStandardAccount, Price: 10000, ParamCount: 1, DisallowCallback: true},
|
{Name: interopnames.SystemContractCreateStandardAccount, Func: contractCreateStandardAccount, Price: 10000, ParamCount: 1, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemContractDestroy, Func: contractDestroy, Price: 1000000, RequiredFlags: smartcontract.WriteStates, DisallowCallback: true},
|
|
||||||
{Name: interopnames.SystemContractIsStandard, Func: contractIsStandard, Price: 30000, RequiredFlags: smartcontract.ReadStates, ParamCount: 1},
|
{Name: interopnames.SystemContractIsStandard, Func: contractIsStandard, Price: 30000, RequiredFlags: smartcontract.ReadStates, ParamCount: 1},
|
||||||
{Name: interopnames.SystemContractGetCallFlags, Func: contractGetCallFlags, Price: 30000, DisallowCallback: true},
|
{Name: interopnames.SystemContractGetCallFlags, Func: contractGetCallFlags, Price: 30000, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemContractUpdate, Func: contractUpdate, Price: 0,
|
{Name: interopnames.SystemContractNativeOnPersist, Func: native.OnPersist, Price: 0, DisallowCallback: true},
|
||||||
RequiredFlags: smartcontract.WriteStates, ParamCount: 2, DisallowCallback: true},
|
{Name: interopnames.SystemContractNativePostPersist, Func: native.PostPersist, Price: 0, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemEnumeratorConcat, Func: enumerator.Concat, Price: 400, ParamCount: 2, DisallowCallback: true},
|
{Name: interopnames.SystemEnumeratorConcat, Func: enumerator.Concat, Price: 400, ParamCount: 2, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemEnumeratorCreate, Func: enumerator.Create, Price: 400, ParamCount: 1, DisallowCallback: true},
|
{Name: interopnames.SystemEnumeratorCreate, Func: enumerator.Create, Price: 400, ParamCount: 1, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemEnumeratorNext, Func: enumerator.Next, Price: 1000000, ParamCount: 1, DisallowCallback: true},
|
{Name: interopnames.SystemEnumeratorNext, Func: enumerator.Next, Price: 1000000, ParamCount: 1, DisallowCallback: true},
|
||||||
|
@ -96,7 +92,7 @@ var systemInterops = []interop.Function{
|
||||||
{Name: interopnames.SystemRuntimeNotify, Func: runtime.Notify, Price: 1000000, RequiredFlags: smartcontract.AllowNotify,
|
{Name: interopnames.SystemRuntimeNotify, Func: runtime.Notify, Price: 1000000, RequiredFlags: smartcontract.AllowNotify,
|
||||||
ParamCount: 2, DisallowCallback: true},
|
ParamCount: 2, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemRuntimePlatform, Func: runtime.Platform, Price: 250},
|
{Name: interopnames.SystemRuntimePlatform, Func: runtime.Platform, Price: 250},
|
||||||
{Name: interopnames.SystemStorageDelete, Func: storageDelete, Price: StoragePrice,
|
{Name: interopnames.SystemStorageDelete, Func: storageDelete, Price: native.StoragePrice,
|
||||||
RequiredFlags: smartcontract.WriteStates, ParamCount: 2, DisallowCallback: true},
|
RequiredFlags: smartcontract.WriteStates, ParamCount: 2, DisallowCallback: true},
|
||||||
{Name: interopnames.SystemStorageFind, Func: storageFind, Price: 1000000, RequiredFlags: smartcontract.ReadStates,
|
{Name: interopnames.SystemStorageFind, Func: storageFind, Price: 1000000, RequiredFlags: smartcontract.ReadStates,
|
||||||
ParamCount: 2, DisallowCallback: true},
|
ParamCount: 2, DisallowCallback: true},
|
||||||
|
@ -123,8 +119,6 @@ var neoInterops = []interop.Function{
|
||||||
{Name: interopnames.NeoCryptoCheckMultisigWithECDsaSecp256k1, Func: crypto.ECDSASecp256k1CheckMultisig, Price: 0, ParamCount: 3},
|
{Name: interopnames.NeoCryptoCheckMultisigWithECDsaSecp256k1, Func: crypto.ECDSASecp256k1CheckMultisig, Price: 0, ParamCount: 3},
|
||||||
{Name: interopnames.NeoCryptoSHA256, Func: crypto.Sha256, Price: 1000000, ParamCount: 1},
|
{Name: interopnames.NeoCryptoSHA256, Func: crypto.Sha256, Price: 1000000, ParamCount: 1},
|
||||||
{Name: interopnames.NeoCryptoRIPEMD160, Func: crypto.RipeMD160, Price: 1000000, ParamCount: 1},
|
{Name: interopnames.NeoCryptoRIPEMD160, Func: crypto.RipeMD160, Price: 1000000, ParamCount: 1},
|
||||||
{Name: interopnames.NeoNativeCall, Func: native.Call, Price: 0, RequiredFlags: smartcontract.AllowCall, ParamCount: 1, DisallowCallback: true},
|
|
||||||
{Name: interopnames.NeoNativeDeploy, Func: native.Deploy, Price: 0, RequiredFlags: smartcontract.WriteStates, DisallowCallback: true},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// initIDinInteropsSlice initializes IDs from names in one given
|
// initIDinInteropsSlice initializes IDs from names in one given
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
package native
|
package native
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// reservedContractID represents the upper bound of the reserved IDs for native contracts.
|
// reservedContractID represents the upper bound of the reserved IDs for native contracts.
|
||||||
|
@ -17,13 +15,14 @@ const reservedContractID = -100
|
||||||
|
|
||||||
// Contracts is a set of registered native contracts.
|
// Contracts is a set of registered native contracts.
|
||||||
type Contracts struct {
|
type Contracts struct {
|
||||||
NEO *NEO
|
Management *Management
|
||||||
GAS *GAS
|
NEO *NEO
|
||||||
Policy *Policy
|
GAS *GAS
|
||||||
Oracle *Oracle
|
Policy *Policy
|
||||||
Designate *Designate
|
Oracle *Oracle
|
||||||
Notary *Notary
|
Designate *Designate
|
||||||
Contracts []interop.Contract
|
Notary *Notary
|
||||||
|
Contracts []interop.Contract
|
||||||
// persistScript is vm script which executes "onPersist" method of every native contract.
|
// persistScript is vm script which executes "onPersist" method of every native contract.
|
||||||
persistScript []byte
|
persistScript []byte
|
||||||
// postPersistScript is vm script which executes "postPersist" method of every native contract.
|
// postPersistScript is vm script which executes "postPersist" method of every native contract.
|
||||||
|
@ -56,15 +55,19 @@ func (cs *Contracts) ByName(name string) interop.Contract {
|
||||||
func NewContracts(p2pSigExtensionsEnabled bool) *Contracts {
|
func NewContracts(p2pSigExtensionsEnabled bool) *Contracts {
|
||||||
cs := new(Contracts)
|
cs := new(Contracts)
|
||||||
|
|
||||||
|
mgmt := newManagement()
|
||||||
|
cs.Management = mgmt
|
||||||
|
cs.Contracts = append(cs.Contracts, mgmt)
|
||||||
|
|
||||||
gas := newGAS()
|
gas := newGAS()
|
||||||
neo := newNEO()
|
neo := newNEO()
|
||||||
neo.GAS = gas
|
neo.GAS = gas
|
||||||
gas.NEO = neo
|
gas.NEO = neo
|
||||||
|
|
||||||
cs.GAS = gas
|
cs.GAS = gas
|
||||||
cs.Contracts = append(cs.Contracts, gas)
|
|
||||||
cs.NEO = neo
|
cs.NEO = neo
|
||||||
cs.Contracts = append(cs.Contracts, neo)
|
cs.Contracts = append(cs.Contracts, neo)
|
||||||
|
cs.Contracts = append(cs.Contracts, gas)
|
||||||
|
|
||||||
policy := newPolicy()
|
policy := newPolicy()
|
||||||
cs.Policy = policy
|
cs.Policy = policy
|
||||||
|
@ -93,62 +96,24 @@ func NewContracts(p2pSigExtensionsEnabled bool) *Contracts {
|
||||||
return cs
|
return cs
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPersistScript returns VM script calling "onPersist" method of every native contract.
|
// GetPersistScript returns VM script calling "onPersist" syscall for native contracts.
|
||||||
func (cs *Contracts) GetPersistScript() []byte {
|
func (cs *Contracts) GetPersistScript() []byte {
|
||||||
if cs.persistScript != nil {
|
if cs.persistScript != nil {
|
||||||
return cs.persistScript
|
return cs.persistScript
|
||||||
}
|
}
|
||||||
w := io.NewBufBinWriter()
|
w := io.NewBufBinWriter()
|
||||||
for i := range cs.Contracts {
|
emit.Syscall(w.BinWriter, interopnames.SystemContractNativeOnPersist)
|
||||||
md := cs.Contracts[i].Metadata()
|
|
||||||
// Not every contract is persisted:
|
|
||||||
// https://github.com/neo-project/neo/blob/master/src/neo/Ledger/Blockchain.cs#L90
|
|
||||||
if md.ContractID == policyContractID || md.ContractID == oracleContractID || md.ContractID == designateContractID {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
emit.Int(w.BinWriter, 0)
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.NEWARRAY)
|
|
||||||
emit.String(w.BinWriter, "onPersist")
|
|
||||||
emit.AppCall(w.BinWriter, md.Hash)
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.DROP)
|
|
||||||
}
|
|
||||||
cs.persistScript = w.Bytes()
|
cs.persistScript = w.Bytes()
|
||||||
return cs.persistScript
|
return cs.persistScript
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPostPersistScript returns VM script calling "postPersist" method of some native contracts.
|
// GetPostPersistScript returns VM script calling "postPersist" syscall for native contracts.
|
||||||
func (cs *Contracts) GetPostPersistScript() []byte {
|
func (cs *Contracts) GetPostPersistScript() []byte {
|
||||||
if cs.postPersistScript != nil {
|
if cs.postPersistScript != nil {
|
||||||
return cs.postPersistScript
|
return cs.postPersistScript
|
||||||
}
|
}
|
||||||
w := io.NewBufBinWriter()
|
w := io.NewBufBinWriter()
|
||||||
for i := range cs.Contracts {
|
emit.Syscall(w.BinWriter, interopnames.SystemContractNativePostPersist)
|
||||||
md := cs.Contracts[i].Metadata()
|
|
||||||
// Not every contract is persisted:
|
|
||||||
// https://github.com/neo-project/neo/blob/master/src/neo/Ledger/Blockchain.cs#L103
|
|
||||||
if md.ContractID == policyContractID || md.ContractID == gasContractID || md.ContractID == designateContractID || md.ContractID == notaryContractID {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
emit.Int(w.BinWriter, 0)
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.NEWARRAY)
|
|
||||||
emit.String(w.BinWriter, "postPersist")
|
|
||||||
emit.AppCall(w.BinWriter, md.Hash)
|
|
||||||
emit.Opcodes(w.BinWriter, opcode.DROP)
|
|
||||||
}
|
|
||||||
cs.postPersistScript = w.Bytes()
|
cs.postPersistScript = w.Bytes()
|
||||||
return cs.postPersistScript
|
return cs.postPersistScript
|
||||||
}
|
}
|
||||||
|
|
||||||
func postPersistBase(ic *interop.Context) error {
|
|
||||||
if ic.Trigger != trigger.PostPersist {
|
|
||||||
return errors.New("postPersist must be trigered by system")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func onPersistBase(ic *interop.Context) error {
|
|
||||||
if ic.Trigger != trigger.OnPersist {
|
|
||||||
return errors.New("onPersist must be trigered by system")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/runtime"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/runtime"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
"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/hash"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
@ -40,7 +41,6 @@ type oraclesData struct {
|
||||||
|
|
||||||
const (
|
const (
|
||||||
designateContractID = -5
|
designateContractID = -5
|
||||||
designateName = "Designation"
|
|
||||||
|
|
||||||
// maxNodeCount is the maximum number of nodes to set the role for.
|
// maxNodeCount is the maximum number of nodes to set the role for.
|
||||||
maxNodeCount = 32
|
maxNodeCount = 32
|
||||||
|
@ -71,7 +71,7 @@ func (s *Designate) isValidRole(r Role) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDesignate(p2pSigExtensionsEnabled bool) *Designate {
|
func newDesignate(p2pSigExtensionsEnabled bool) *Designate {
|
||||||
s := &Designate{ContractMD: *interop.NewContractMD(designateName)}
|
s := &Designate{ContractMD: *interop.NewContractMD(nativenames.Designation)}
|
||||||
s.ContractID = designateContractID
|
s.ContractID = designateContractID
|
||||||
s.p2pSigExtensionsEnabled = p2pSigExtensionsEnabled
|
s.p2pSigExtensionsEnabled = p2pSigExtensionsEnabled
|
||||||
|
|
||||||
|
@ -87,14 +87,6 @@ func newDesignate(p2pSigExtensionsEnabled bool) *Designate {
|
||||||
md = newMethodAndPrice(s.designateAsRole, 0, smartcontract.WriteStates)
|
md = newMethodAndPrice(s.designateAsRole, 0, smartcontract.WriteStates)
|
||||||
s.AddMethod(md, desc)
|
s.AddMethod(md, desc)
|
||||||
|
|
||||||
desc = newDescriptor("onPersist", smartcontract.VoidType)
|
|
||||||
md = newMethodAndPrice(getOnPersistWrapper(onPersistBase), 0, smartcontract.WriteStates)
|
|
||||||
s.AddMethod(md, desc)
|
|
||||||
|
|
||||||
desc = newDescriptor("postPersist", smartcontract.VoidType)
|
|
||||||
md = newMethodAndPrice(getOnPersistWrapper(postPersistBase), 0, smartcontract.WriteStates)
|
|
||||||
s.AddMethod(md, desc)
|
|
||||||
|
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,13 +95,18 @@ func (s *Designate) Initialize(ic *interop.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnPersistEnd updates cached values if they've been changed.
|
// OnPersist implements Contract interface.
|
||||||
func (s *Designate) OnPersistEnd(d dao.DAO) error {
|
func (s *Designate) OnPersist(ic *interop.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostPersist implements Contract interface.
|
||||||
|
func (s *Designate) PostPersist(ic *interop.Context) error {
|
||||||
if !s.rolesChanged() {
|
if !s.rolesChanged() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeKeys, height, err := s.GetDesignatedByRole(d, RoleOracle, math.MaxUint32)
|
nodeKeys, height, err := s.GetDesignatedByRole(ic.DAO, RoleOracle, math.MaxUint32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,35 +5,10 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Deploy deploys native contract.
|
|
||||||
func Deploy(ic *interop.Context) error {
|
|
||||||
if ic.Block == nil || ic.Block.Index != 0 {
|
|
||||||
return errors.New("native contracts can be deployed only at 0 block")
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, native := range ic.Natives {
|
|
||||||
md := native.Metadata()
|
|
||||||
|
|
||||||
cs := &state.Contract{
|
|
||||||
ID: md.ContractID,
|
|
||||||
Hash: md.Hash,
|
|
||||||
Script: md.Script,
|
|
||||||
Manifest: md.Manifest,
|
|
||||||
}
|
|
||||||
if err := ic.DAO.PutContractState(cs); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := native.Initialize(ic); err != nil {
|
|
||||||
return fmt.Errorf("initializing %s native contract: %w", md.Name, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call calls specified native contract method.
|
// Call calls specified native contract method.
|
||||||
func Call(ic *interop.Context) error {
|
func Call(ic *interop.Context) error {
|
||||||
name := ic.VM.Estack().Pop().String()
|
name := ic.VM.Estack().Pop().String()
|
||||||
|
@ -70,3 +45,31 @@ func Call(ic *interop.Context) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OnPersist calls OnPersist methods for all native contracts.
|
||||||
|
func OnPersist(ic *interop.Context) error {
|
||||||
|
if ic.Trigger != trigger.OnPersist {
|
||||||
|
return errors.New("onPersist must be trigered by system")
|
||||||
|
}
|
||||||
|
for _, c := range ic.Natives {
|
||||||
|
err := c.OnPersist(ic)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostPersist calls PostPersist methods for all native contracts.
|
||||||
|
func PostPersist(ic *interop.Context) error {
|
||||||
|
if ic.Trigger != trigger.PostPersist {
|
||||||
|
return errors.New("postPersist must be trigered by system")
|
||||||
|
}
|
||||||
|
for _, c := range ic.Natives {
|
||||||
|
err := c.PostPersist(ic)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
377
pkg/core/native/management.go
Normal file
377
pkg/core/native/management.go
Normal file
|
@ -0,0 +1,377 @@
|
||||||
|
package native
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/contract"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Management is contract-managing native contract.
|
||||||
|
type Management struct {
|
||||||
|
interop.ContractMD
|
||||||
|
}
|
||||||
|
|
||||||
|
// StoragePrice is the price to pay for 1 byte of storage.
|
||||||
|
const StoragePrice = 100000
|
||||||
|
|
||||||
|
const (
|
||||||
|
prefixContract = 8
|
||||||
|
)
|
||||||
|
|
||||||
|
var errGasLimitExceeded = errors.New("gas limit exceeded")
|
||||||
|
var keyNextAvailableID = []byte{15}
|
||||||
|
|
||||||
|
// makeContractKey creates a key from account script hash.
|
||||||
|
func makeContractKey(h util.Uint160) []byte {
|
||||||
|
return makeUint160Key(prefixContract, h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// newManagement creates new Management native contract.
|
||||||
|
func newManagement() *Management {
|
||||||
|
var m = &Management{ContractMD: *interop.NewContractMD(nativenames.Management)}
|
||||||
|
|
||||||
|
desc := newDescriptor("getContract", smartcontract.ArrayType,
|
||||||
|
manifest.NewParameter("hash", smartcontract.Hash160Type))
|
||||||
|
md := newMethodAndPrice(m.getContract, 1000000, smartcontract.ReadStates)
|
||||||
|
m.AddMethod(md, desc)
|
||||||
|
|
||||||
|
desc = newDescriptor("deploy", smartcontract.ArrayType,
|
||||||
|
manifest.NewParameter("script", smartcontract.ByteArrayType),
|
||||||
|
manifest.NewParameter("manifest", smartcontract.ByteArrayType))
|
||||||
|
md = newMethodAndPrice(m.deploy, 0, smartcontract.WriteStates)
|
||||||
|
m.AddMethod(md, desc)
|
||||||
|
|
||||||
|
desc = newDescriptor("update", smartcontract.VoidType,
|
||||||
|
manifest.NewParameter("script", smartcontract.ByteArrayType),
|
||||||
|
manifest.NewParameter("manifest", smartcontract.ByteArrayType))
|
||||||
|
md = newMethodAndPrice(m.update, 0, smartcontract.WriteStates)
|
||||||
|
m.AddMethod(md, desc)
|
||||||
|
|
||||||
|
desc = newDescriptor("destroy", smartcontract.VoidType)
|
||||||
|
md = newMethodAndPrice(m.destroy, 10000000, smartcontract.WriteStates)
|
||||||
|
m.AddMethod(md, desc)
|
||||||
|
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// getContract is an implementation of public getContract method, it's run under
|
||||||
|
// VM protections, so it's OK for it to panic instead of returning errors.
|
||||||
|
func (m *Management) getContract(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
|
hashBytes, err := args[0].TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
hash, err := util.Uint160DecodeBytesBE(hashBytes)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
ctr, err := m.GetContract(ic.DAO, hash)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return contractToStack(ctr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetContract returns contract with given hash from given DAO.
|
||||||
|
func (m *Management) GetContract(d dao.DAO, hash util.Uint160) (*state.Contract, error) {
|
||||||
|
contract := new(state.Contract)
|
||||||
|
key := makeContractKey(hash)
|
||||||
|
err := getSerializableFromDAO(m.ContractID, d, key, contract)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return contract, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLimitedSlice(arg stackitem.Item, max int) ([]byte, error) {
|
||||||
|
_, isNull := arg.(stackitem.Null)
|
||||||
|
if isNull {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
b, err := arg.TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
l := len(b)
|
||||||
|
if l == 0 {
|
||||||
|
return nil, errors.New("empty")
|
||||||
|
} else if l > max {
|
||||||
|
return nil, fmt.Errorf("len is %d (max %d)", l, max)
|
||||||
|
}
|
||||||
|
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getNefAndManifestFromItems converts input arguments into NEF and manifest
|
||||||
|
// adding appropriate deployment GAS price and sanitizing inputs.
|
||||||
|
func getNefAndManifestFromItems(args []stackitem.Item, v *vm.VM) (*nef.File, *manifest.Manifest, error) {
|
||||||
|
nefBytes, err := getLimitedSlice(args[0], math.MaxInt32) // Upper limits are checked during NEF deserialization.
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("invalid NEF file: %w", err)
|
||||||
|
}
|
||||||
|
manifestBytes, err := getLimitedSlice(args[1], manifest.MaxManifestSize)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("invalid manifest: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !v.AddGas(int64(StoragePrice * (len(nefBytes) + len(manifestBytes)))) {
|
||||||
|
return nil, nil, errGasLimitExceeded
|
||||||
|
}
|
||||||
|
var resManifest *manifest.Manifest
|
||||||
|
var resNef *nef.File
|
||||||
|
if nefBytes != nil {
|
||||||
|
nf, err := nef.FileFromBytes(nefBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("invalid NEF file: %w", err)
|
||||||
|
}
|
||||||
|
resNef = &nf
|
||||||
|
}
|
||||||
|
if manifestBytes != nil {
|
||||||
|
resManifest = new(manifest.Manifest)
|
||||||
|
err := json.Unmarshal(manifestBytes, resManifest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("invalid manifest: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resNef, resManifest, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// deploy is an implementation of public deploy method, it's run under
|
||||||
|
// VM protections, so it's OK for it to panic instead of returning errors.
|
||||||
|
func (m *Management) deploy(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
|
neff, manif, err := getNefAndManifestFromItems(args, ic.VM)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if neff == nil {
|
||||||
|
panic(errors.New("no valid NEF provided"))
|
||||||
|
}
|
||||||
|
if manif == nil {
|
||||||
|
panic(errors.New("no valid manifest provided"))
|
||||||
|
}
|
||||||
|
if ic.Tx == nil {
|
||||||
|
panic(errors.New("no transaction provided"))
|
||||||
|
}
|
||||||
|
newcontract, err := m.Deploy(ic.DAO, ic.Tx.Sender(), neff, manif)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
callDeploy(ic, newcontract, false)
|
||||||
|
return contractToStack(newcontract)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deploy creates contract's hash/ID and saves new contract into the given DAO.
|
||||||
|
// It doesn't run _deploy method.
|
||||||
|
func (m *Management) Deploy(d dao.DAO, sender util.Uint160, neff *nef.File, manif *manifest.Manifest) (*state.Contract, error) {
|
||||||
|
h := state.CreateContractHash(sender, neff.Script)
|
||||||
|
key := makeContractKey(h)
|
||||||
|
si := d.GetStorageItem(m.ContractID, key)
|
||||||
|
if si != nil {
|
||||||
|
return nil, errors.New("contract already exists")
|
||||||
|
}
|
||||||
|
id, err := m.getNextContractID(d)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !manif.IsValid(h) {
|
||||||
|
return nil, errors.New("invalid manifest for this contract")
|
||||||
|
}
|
||||||
|
newcontract := &state.Contract{
|
||||||
|
ID: id,
|
||||||
|
Hash: h,
|
||||||
|
Script: neff.Script,
|
||||||
|
Manifest: *manif,
|
||||||
|
}
|
||||||
|
err = m.PutContractState(d, newcontract)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return newcontract, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// update is an implementation of public update method, it's run under
|
||||||
|
// VM protections, so it's OK for it to panic instead of returning errors.
|
||||||
|
func (m *Management) update(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
|
neff, manif, err := getNefAndManifestFromItems(args, ic.VM)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if neff == nil && manif == nil {
|
||||||
|
panic(errors.New("both NEF and manifest are nil"))
|
||||||
|
}
|
||||||
|
contract, err := m.Update(ic.DAO, ic.VM.GetCallingScriptHash(), neff, manif)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
callDeploy(ic, contract, true)
|
||||||
|
return stackitem.Null{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update updates contract's script and/or manifest in the given DAO.
|
||||||
|
// It doesn't run _deploy method.
|
||||||
|
func (m *Management) Update(d dao.DAO, hash util.Uint160, neff *nef.File, manif *manifest.Manifest) (*state.Contract, error) {
|
||||||
|
contract, err := m.GetContract(d, hash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("contract doesn't exist")
|
||||||
|
}
|
||||||
|
// if NEF was provided, update the contract script
|
||||||
|
if neff != nil {
|
||||||
|
contract.Script = neff.Script
|
||||||
|
}
|
||||||
|
// if manifest was provided, update the contract manifest
|
||||||
|
if manif != nil {
|
||||||
|
contract.Manifest = *manif
|
||||||
|
if !contract.Manifest.IsValid(contract.Hash) {
|
||||||
|
return nil, errors.New("invalid manifest for this contract")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contract.UpdateCounter++
|
||||||
|
err = m.PutContractState(d, contract)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return contract, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// destroy is an implementation of destroy update method, it's run under
|
||||||
|
// VM protections, so it's OK for it to panic instead of returning errors.
|
||||||
|
func (m *Management) destroy(ic *interop.Context, sis []stackitem.Item) stackitem.Item {
|
||||||
|
hash := ic.VM.GetCallingScriptHash()
|
||||||
|
err := m.Destroy(ic.DAO, hash)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return stackitem.Null{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroy drops given contract from DAO along with its storage.
|
||||||
|
func (m *Management) Destroy(d dao.DAO, hash util.Uint160) error {
|
||||||
|
contract, err := m.GetContract(d, hash)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
key := makeContractKey(hash)
|
||||||
|
err = d.DeleteStorageItem(m.ContractID, key)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = d.DeleteContractID(contract.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
siMap, err := d.GetStorageItems(contract.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for k := range siMap {
|
||||||
|
err := d.DeleteStorageItem(contract.ID, []byte(k))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func callDeploy(ic *interop.Context, cs *state.Contract, isUpdate bool) {
|
||||||
|
md := cs.Manifest.ABI.GetMethod(manifest.MethodDeploy)
|
||||||
|
if md != nil {
|
||||||
|
err := contract.CallExInternal(ic, cs, manifest.MethodDeploy,
|
||||||
|
[]stackitem.Item{stackitem.NewBool(isUpdate)}, smartcontract.All, vm.EnsureIsEmpty)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func contractToStack(cs *state.Contract) stackitem.Item {
|
||||||
|
si, err := cs.ToStackItem()
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("contract to stack item: %w", err))
|
||||||
|
}
|
||||||
|
return si
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metadata implements Contract interface.
|
||||||
|
func (m *Management) Metadata() *interop.ContractMD {
|
||||||
|
return &m.ContractMD
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnPersist implements Contract interface.
|
||||||
|
func (m *Management) OnPersist(ic *interop.Context) error {
|
||||||
|
if ic.Block.Index != 0 { // We're only deploying at 0 at the moment.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, native := range ic.Natives {
|
||||||
|
md := native.Metadata()
|
||||||
|
|
||||||
|
cs := &state.Contract{
|
||||||
|
ID: md.ContractID,
|
||||||
|
Hash: md.Hash,
|
||||||
|
Script: md.Script,
|
||||||
|
Manifest: md.Manifest,
|
||||||
|
}
|
||||||
|
err := m.PutContractState(ic.DAO, cs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := native.Initialize(ic); err != nil {
|
||||||
|
return fmt.Errorf("initializing %s native contract: %w", md.Name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PostPersist implements Contract interface.
|
||||||
|
func (m *Management) PostPersist(_ *interop.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize implements Contract interface.
|
||||||
|
func (m *Management) Initialize(_ *interop.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PutContractState saves given contract state into given DAO.
|
||||||
|
func (m *Management) PutContractState(d dao.DAO, cs *state.Contract) error {
|
||||||
|
key := makeContractKey(cs.Hash)
|
||||||
|
if err := putSerializableToDAO(m.ContractID, d, key, cs); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if cs.UpdateCounter != 0 { // Update.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return d.PutContractID(cs.ID, cs.Hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Management) getNextContractID(d dao.DAO) (int32, error) {
|
||||||
|
var id = big.NewInt(1)
|
||||||
|
si := d.GetStorageItem(m.ContractID, keyNextAvailableID)
|
||||||
|
if si != nil {
|
||||||
|
id = bigint.FromBytes(si.Value)
|
||||||
|
} else {
|
||||||
|
si = new(state.StorageItem)
|
||||||
|
si.Value = make([]byte, 0, 2)
|
||||||
|
}
|
||||||
|
ret := int32(id.Int64())
|
||||||
|
id.Add(id, big.NewInt(1))
|
||||||
|
si.Value = bigint.ToPreallocatedBytes(id, si.Value)
|
||||||
|
return ret, d.PutStorageItem(m.ContractID, keyNextAvailableID, si)
|
||||||
|
}
|
63
pkg/core/native/management_test.go
Normal file
63
pkg/core/native/management_test.go
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
package native
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDeployGetUpdateDestroyContract(t *testing.T) {
|
||||||
|
mgmt := newManagement()
|
||||||
|
d := dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet, false)
|
||||||
|
script := []byte{1}
|
||||||
|
sender := util.Uint160{1, 2, 3}
|
||||||
|
h := state.CreateContractHash(sender, script)
|
||||||
|
|
||||||
|
ne, err := nef.NewFile(script)
|
||||||
|
require.NoError(t, err)
|
||||||
|
manif := manifest.NewManifest("Test")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
contract, err := mgmt.Deploy(d, sender, ne, manif)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, int32(1), contract.ID)
|
||||||
|
require.Equal(t, uint16(0), contract.UpdateCounter)
|
||||||
|
require.Equal(t, h, contract.Hash)
|
||||||
|
require.Equal(t, script, contract.Script)
|
||||||
|
require.Equal(t, *manif, contract.Manifest)
|
||||||
|
|
||||||
|
// Double deploy.
|
||||||
|
_, err = mgmt.Deploy(d, sender, ne, manif)
|
||||||
|
require.Error(t, err)
|
||||||
|
|
||||||
|
// Different sender.
|
||||||
|
sender2 := util.Uint160{3, 2, 1}
|
||||||
|
contract2, err := mgmt.Deploy(d, sender2, ne, manif)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, int32(2), contract2.ID)
|
||||||
|
require.Equal(t, uint16(0), contract2.UpdateCounter)
|
||||||
|
require.Equal(t, state.CreateContractHash(sender2, script), contract2.Hash)
|
||||||
|
require.Equal(t, script, contract2.Script)
|
||||||
|
require.Equal(t, *manif, contract2.Manifest)
|
||||||
|
|
||||||
|
refContract, err := mgmt.GetContract(d, h)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, contract, refContract)
|
||||||
|
|
||||||
|
upContract, err := mgmt.Update(d, h, ne, manif)
|
||||||
|
refContract.UpdateCounter++
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, refContract, upContract)
|
||||||
|
|
||||||
|
err = mgmt.Destroy(d, h)
|
||||||
|
require.NoError(t, err)
|
||||||
|
_, err = mgmt.GetContract(d, h)
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
"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/hash"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
|
@ -17,7 +18,6 @@ type GAS struct {
|
||||||
NEO *NEO
|
NEO *NEO
|
||||||
}
|
}
|
||||||
|
|
||||||
const gasName = "GAS"
|
|
||||||
const gasContractID = -2
|
const gasContractID = -2
|
||||||
|
|
||||||
// GASFactor is a divisor for finding GAS integral value.
|
// GASFactor is a divisor for finding GAS integral value.
|
||||||
|
@ -27,20 +27,15 @@ const initialGAS = 30000000
|
||||||
// newGAS returns GAS native contract.
|
// newGAS returns GAS native contract.
|
||||||
func newGAS() *GAS {
|
func newGAS() *GAS {
|
||||||
g := &GAS{}
|
g := &GAS{}
|
||||||
nep17 := newNEP17Native(gasName)
|
nep17 := newNEP17Native(nativenames.Gas)
|
||||||
nep17.symbol = "gas"
|
nep17.symbol = "GAS"
|
||||||
nep17.decimals = 8
|
nep17.decimals = 8
|
||||||
nep17.factor = GASFactor
|
nep17.factor = GASFactor
|
||||||
nep17.onPersist = chainOnPersist(onPersistBase, g.OnPersist)
|
|
||||||
nep17.incBalance = g.increaseBalance
|
nep17.incBalance = g.increaseBalance
|
||||||
nep17.ContractID = gasContractID
|
nep17.ContractID = gasContractID
|
||||||
|
|
||||||
g.nep17TokenNative = *nep17
|
g.nep17TokenNative = *nep17
|
||||||
|
|
||||||
onp := g.Methods["onPersist"]
|
|
||||||
onp.Func = getOnPersistWrapper(g.onPersist)
|
|
||||||
g.Methods["onPersist"] = onp
|
|
||||||
|
|
||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,6 +93,11 @@ func (g *GAS) OnPersist(ic *interop.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PostPersist implements Contract interface.
|
||||||
|
func (g *GAS) PostPersist(ic *interop.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func getStandbyValidatorsHash(ic *interop.Context) (util.Uint160, error) {
|
func getStandbyValidatorsHash(ic *interop.Context) (util.Uint160, error) {
|
||||||
s, err := smartcontract.CreateDefaultMultiSigRedeemScript(ic.Chain.GetStandByValidators())
|
s, err := smartcontract.CreateDefaultMultiSigRedeemScript(ic.Chain.GetStandByValidators())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -105,16 +105,3 @@ func getStandbyValidatorsHash(ic *interop.Context) (util.Uint160, error) {
|
||||||
}
|
}
|
||||||
return hash.Hash160(s), nil
|
return hash.Hash160(s), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func chainOnPersist(fs ...func(*interop.Context) error) func(*interop.Context) error {
|
|
||||||
return func(ic *interop.Context) error {
|
|
||||||
for i := range fs {
|
|
||||||
if fs[i] != nil {
|
|
||||||
if err := fs[i](ic); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/runtime"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/runtime"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
"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/core/storage"
|
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
|
@ -49,7 +50,6 @@ type NEO struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
neoName = "NEO"
|
|
||||||
neoContractID = -1
|
neoContractID = -1
|
||||||
// NEOTotalSupply is the total amount of NEO in the system.
|
// NEOTotalSupply is the total amount of NEO in the system.
|
||||||
NEOTotalSupply = 100000000
|
NEOTotalSupply = 100000000
|
||||||
|
@ -93,12 +93,10 @@ func makeValidatorKey(key *keys.PublicKey) []byte {
|
||||||
// newNEO returns NEO native contract.
|
// newNEO returns NEO native contract.
|
||||||
func newNEO() *NEO {
|
func newNEO() *NEO {
|
||||||
n := &NEO{}
|
n := &NEO{}
|
||||||
nep17 := newNEP17Native(neoName)
|
nep17 := newNEP17Native(nativenames.Neo)
|
||||||
nep17.symbol = "neo"
|
nep17.symbol = "NEO"
|
||||||
nep17.decimals = 0
|
nep17.decimals = 0
|
||||||
nep17.factor = 1
|
nep17.factor = 1
|
||||||
nep17.onPersist = chainOnPersist(onPersistBase, n.OnPersist)
|
|
||||||
nep17.postPersist = chainOnPersist(nep17.postPersist, n.PostPersist)
|
|
||||||
nep17.incBalance = n.increaseBalance
|
nep17.incBalance = n.increaseBalance
|
||||||
nep17.ContractID = neoContractID
|
nep17.ContractID = neoContractID
|
||||||
|
|
||||||
|
@ -109,14 +107,6 @@ func newNEO() *NEO {
|
||||||
n.committee.Store(keysWithVotes(nil))
|
n.committee.Store(keysWithVotes(nil))
|
||||||
n.committeeHash.Store(util.Uint160{})
|
n.committeeHash.Store(util.Uint160{})
|
||||||
|
|
||||||
onp := n.Methods["onPersist"]
|
|
||||||
onp.Func = getOnPersistWrapper(n.onPersist)
|
|
||||||
n.Methods["onPersist"] = onp
|
|
||||||
|
|
||||||
pp := n.Methods["postPersist"]
|
|
||||||
pp.Func = getOnPersistWrapper(n.postPersist)
|
|
||||||
n.Methods["postPersist"] = pp
|
|
||||||
|
|
||||||
desc := newDescriptor("unclaimedGas", smartcontract.IntegerType,
|
desc := newDescriptor("unclaimedGas", smartcontract.IntegerType,
|
||||||
manifest.NewParameter("account", smartcontract.Hash160Type),
|
manifest.NewParameter("account", smartcontract.Hash160Type),
|
||||||
manifest.NewParameter("end", smartcontract.IntegerType))
|
manifest.NewParameter("end", smartcontract.IntegerType))
|
||||||
|
@ -330,7 +320,14 @@ func (n *NEO) PostPersist(ic *interop.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
n.OnPersistEnd(ic.DAO)
|
if n.gasPerBlockChanged.Load().(bool) {
|
||||||
|
gr, err := n.getSortedGASRecordFromDAO(ic.DAO)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
n.gasPerBlock.Store(gr)
|
||||||
|
n.gasPerBlockChanged.Store(false)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,18 +354,6 @@ func (n *NEO) getGASPerVote(d dao.DAO, key []byte, index ...uint32) []big.Int {
|
||||||
return reward
|
return reward
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnPersistEnd updates cached values if they've been changed.
|
|
||||||
func (n *NEO) OnPersistEnd(d dao.DAO) {
|
|
||||||
if n.gasPerBlockChanged.Load().(bool) {
|
|
||||||
gr, err := n.getSortedGASRecordFromDAO(d)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
n.gasPerBlock.Store(gr)
|
|
||||||
n.gasPerBlockChanged.Store(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NEO) increaseBalance(ic *interop.Context, h util.Uint160, si *state.StorageItem, amount *big.Int) error {
|
func (n *NEO) increaseBalance(ic *interop.Context, h util.Uint160, si *state.StorageItem, amount *big.Int) error {
|
||||||
acc, err := state.NEOBalanceStateFromBytes(si.Value)
|
acc, err := state.NEOBalanceStateFromBytes(si.Value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -2,7 +2,6 @@ package native
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
|
@ -24,21 +23,16 @@ const prefixAccount = 20
|
||||||
|
|
||||||
// makeAccountKey creates a key from account script hash.
|
// makeAccountKey creates a key from account script hash.
|
||||||
func makeAccountKey(h util.Uint160) []byte {
|
func makeAccountKey(h util.Uint160) []byte {
|
||||||
k := make([]byte, util.Uint160Size+1)
|
return makeUint160Key(prefixAccount, h)
|
||||||
k[0] = prefixAccount
|
|
||||||
copy(k[1:], h.BytesBE())
|
|
||||||
return k
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// nep17TokenNative represents NEP-17 token contract.
|
// nep17TokenNative represents NEP-17 token contract.
|
||||||
type nep17TokenNative struct {
|
type nep17TokenNative struct {
|
||||||
interop.ContractMD
|
interop.ContractMD
|
||||||
symbol string
|
symbol string
|
||||||
decimals int64
|
decimals int64
|
||||||
factor int64
|
factor int64
|
||||||
onPersist func(*interop.Context) error
|
incBalance func(*interop.Context, util.Uint160, *state.StorageItem, *big.Int) error
|
||||||
postPersist func(*interop.Context) error
|
|
||||||
incBalance func(*interop.Context, util.Uint160, *state.StorageItem, *big.Int) error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// totalSupplyKey is the key used to store totalSupply value.
|
// totalSupplyKey is the key used to store totalSupply value.
|
||||||
|
@ -48,8 +42,6 @@ func (c *nep17TokenNative) Metadata() *interop.ContractMD {
|
||||||
return &c.ContractMD
|
return &c.ContractMD
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ interop.Contract = (*nep17TokenNative)(nil)
|
|
||||||
|
|
||||||
func newNEP17Native(name string) *nep17TokenNative {
|
func newNEP17Native(name string) *nep17TokenNative {
|
||||||
n := &nep17TokenNative{ContractMD: *interop.NewContractMD(name)}
|
n := &nep17TokenNative{ContractMD: *interop.NewContractMD(name)}
|
||||||
n.Manifest.SupportedStandards = []string{manifest.NEP17StandardName}
|
n.Manifest.SupportedStandards = []string{manifest.NEP17StandardName}
|
||||||
|
@ -82,14 +74,6 @@ func newNEP17Native(name string) *nep17TokenNative {
|
||||||
md = newMethodAndPrice(n.Transfer, 8000000, smartcontract.WriteStates|smartcontract.AllowCall|smartcontract.AllowNotify)
|
md = newMethodAndPrice(n.Transfer, 8000000, smartcontract.WriteStates|smartcontract.AllowCall|smartcontract.AllowNotify)
|
||||||
n.AddMethod(md, desc)
|
n.AddMethod(md, desc)
|
||||||
|
|
||||||
desc = newDescriptor("onPersist", smartcontract.VoidType)
|
|
||||||
md = newMethodAndPrice(getOnPersistWrapper(onPersistBase), 0, smartcontract.WriteStates)
|
|
||||||
n.AddMethod(md, desc)
|
|
||||||
|
|
||||||
desc = newDescriptor("postPersist", smartcontract.VoidType)
|
|
||||||
md = newMethodAndPrice(getOnPersistWrapper(postPersistBase), 0, smartcontract.WriteStates)
|
|
||||||
n.AddMethod(md, desc)
|
|
||||||
|
|
||||||
n.AddEvent("Transfer", transferParams...)
|
n.AddEvent("Transfer", transferParams...)
|
||||||
|
|
||||||
return n
|
return n
|
||||||
|
@ -145,7 +129,7 @@ func (c *nep17TokenNative) postTransfer(ic *interop.Context, from, to *util.Uint
|
||||||
if to == nil || !callOnPayment {
|
if to == nil || !callOnPayment {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cs, err := ic.DAO.GetContractState(*to)
|
cs, err := ic.GetContract(*to)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -333,13 +317,3 @@ func toUint32(s stackitem.Item) uint32 {
|
||||||
}
|
}
|
||||||
return uint32(int64Value)
|
return uint32(int64Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getOnPersistWrapper(f func(ic *interop.Context) error) interop.Method {
|
|
||||||
return func(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
|
|
||||||
err := f(ic)
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Errorf("OnPersist for native contract: %w", err))
|
|
||||||
}
|
|
||||||
return stackitem.Null{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
12
pkg/core/native/nativenames/names.go
Normal file
12
pkg/core/native/nativenames/names.go
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package nativenames
|
||||||
|
|
||||||
|
// Names of all native contracts.
|
||||||
|
const (
|
||||||
|
Management = "ManagementContract"
|
||||||
|
Neo = "NeoToken"
|
||||||
|
Gas = "GasToken"
|
||||||
|
Policy = "PolicyContract"
|
||||||
|
Oracle = "OracleContract"
|
||||||
|
Designation = "DesignationContract"
|
||||||
|
Notary = "Notary"
|
||||||
|
)
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/contract"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/contract"
|
||||||
"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/native/nativenames"
|
||||||
"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/core/storage"
|
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
|
@ -37,7 +38,6 @@ type Notary struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
notaryName = "Notary"
|
|
||||||
notaryContractID = reservedContractID - 1
|
notaryContractID = reservedContractID - 1
|
||||||
// NotaryVerificationPrice is the price of `verify` Notary method.
|
// NotaryVerificationPrice is the price of `verify` Notary method.
|
||||||
NotaryVerificationPrice = 100_0000
|
NotaryVerificationPrice = 100_0000
|
||||||
|
@ -52,7 +52,7 @@ var maxNotValidBeforeDeltaKey = []byte{10}
|
||||||
|
|
||||||
// newNotary returns Notary native contract.
|
// newNotary returns Notary native contract.
|
||||||
func newNotary() *Notary {
|
func newNotary() *Notary {
|
||||||
n := &Notary{ContractMD: *interop.NewContractMD(notaryName)}
|
n := &Notary{ContractMD: *interop.NewContractMD(nativenames.Notary)}
|
||||||
n.ContractID = notaryContractID
|
n.ContractID = notaryContractID
|
||||||
|
|
||||||
desc := newDescriptor("onPayment", smartcontract.VoidType,
|
desc := newDescriptor("onPayment", smartcontract.VoidType,
|
||||||
|
@ -98,14 +98,6 @@ func newNotary() *Notary {
|
||||||
md = newMethodAndPrice(n.setMaxNotValidBeforeDelta, 300_0000, smartcontract.WriteStates)
|
md = newMethodAndPrice(n.setMaxNotValidBeforeDelta, 300_0000, smartcontract.WriteStates)
|
||||||
n.AddMethod(md, desc)
|
n.AddMethod(md, desc)
|
||||||
|
|
||||||
desc = newDescriptor("onPersist", smartcontract.VoidType)
|
|
||||||
md = newMethodAndPrice(getOnPersistWrapper(n.OnPersist), 0, smartcontract.WriteStates)
|
|
||||||
n.AddMethod(md, desc)
|
|
||||||
|
|
||||||
desc = newDescriptor("postPersist", smartcontract.VoidType)
|
|
||||||
md = newMethodAndPrice(getOnPersistWrapper(postPersistBase), 0, smartcontract.WriteStates)
|
|
||||||
n.AddMethod(md, desc)
|
|
||||||
|
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,15 +158,15 @@ func (n *Notary) OnPersist(ic *interop.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnPersistEnd updates cached Policy values if they've been changed
|
// PostPersist implements Contract interface.
|
||||||
func (n *Notary) OnPersistEnd(dao dao.DAO) error {
|
func (n *Notary) PostPersist(ic *interop.Context) error {
|
||||||
|
n.lock.Lock()
|
||||||
|
defer n.lock.Unlock()
|
||||||
if n.isValid {
|
if n.isValid {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
n.lock.Lock()
|
|
||||||
defer n.lock.Unlock()
|
|
||||||
|
|
||||||
n.maxNotValidBeforeDelta = getUint32WithKey(n.ContractID, dao, maxNotValidBeforeDeltaKey, defaultMaxNotValidBeforeDelta)
|
n.maxNotValidBeforeDelta = getUint32WithKey(n.ContractID, ic.DAO, maxNotValidBeforeDeltaKey, defaultMaxNotValidBeforeDelta)
|
||||||
n.isValid = true
|
n.isValid = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -279,7 +271,7 @@ func (n *Notary) withdraw(ic *interop.Context, args []stackitem.Item) stackitem.
|
||||||
if ic.Chain.BlockHeight() < deposit.Till {
|
if ic.Chain.BlockHeight() < deposit.Till {
|
||||||
return stackitem.NewBool(false)
|
return stackitem.NewBool(false)
|
||||||
}
|
}
|
||||||
cs, err := ic.DAO.GetContractState(n.GAS.Hash)
|
cs, err := ic.GetContract(n.GAS.Hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("failed to get GAS contract state: %w", err))
|
panic(fmt.Errorf("failed to get GAS contract state: %w", err))
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/contract"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/contract"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
"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/core/storage"
|
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
|
@ -36,11 +37,7 @@ type Oracle struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
oracleContractID = -4
|
oracleContractID = -4
|
||||||
oracleName = "Oracle"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
maxURLLength = 256
|
maxURLLength = 256
|
||||||
maxFilterLength = 128
|
maxFilterLength = 128
|
||||||
maxCallbackLength = 32
|
maxCallbackLength = 32
|
||||||
|
@ -58,8 +55,8 @@ var (
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
w := io.NewBufBinWriter()
|
w := io.NewBufBinWriter()
|
||||||
emit.String(w.BinWriter, oracleName)
|
emit.String(w.BinWriter, nativenames.Oracle)
|
||||||
emit.Syscall(w.BinWriter, interopnames.NeoNativeCall)
|
emit.Syscall(w.BinWriter, interopnames.SystemContractCallNative)
|
||||||
oracleInvokeScript = w.Bytes()
|
oracleInvokeScript = w.Bytes()
|
||||||
h := hash.Hash160(oracleInvokeScript)
|
h := hash.Hash160(oracleInvokeScript)
|
||||||
|
|
||||||
|
@ -102,7 +99,7 @@ func GetOracleResponseScript() []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newOracle() *Oracle {
|
func newOracle() *Oracle {
|
||||||
o := &Oracle{ContractMD: *interop.NewContractMD(oracleName)}
|
o := &Oracle{ContractMD: *interop.NewContractMD(nativenames.Oracle)}
|
||||||
o.ContractID = oracleContractID
|
o.ContractID = oracleContractID
|
||||||
|
|
||||||
desc := newDescriptor("request", smartcontract.VoidType,
|
desc := newDescriptor("request", smartcontract.VoidType,
|
||||||
|
@ -122,15 +119,6 @@ func newOracle() *Oracle {
|
||||||
md = newMethodAndPrice(o.verify, 100_0000, smartcontract.NoneFlag)
|
md = newMethodAndPrice(o.verify, 100_0000, smartcontract.NoneFlag)
|
||||||
o.AddMethod(md, desc)
|
o.AddMethod(md, desc)
|
||||||
|
|
||||||
pp := chainOnPersist(postPersistBase, o.PostPersist)
|
|
||||||
desc = newDescriptor("postPersist", smartcontract.VoidType)
|
|
||||||
md = newMethodAndPrice(getOnPersistWrapper(pp), 0, smartcontract.WriteStates)
|
|
||||||
o.AddMethod(md, desc)
|
|
||||||
|
|
||||||
desc = newDescriptor("onPersist", smartcontract.VoidType)
|
|
||||||
md = newMethodAndPrice(getOnPersistWrapper(onPersistBase), 0, smartcontract.WriteStates)
|
|
||||||
o.AddMethod(md, desc)
|
|
||||||
|
|
||||||
o.AddEvent("OracleRequest", manifest.NewParameter("Id", smartcontract.IntegerType),
|
o.AddEvent("OracleRequest", manifest.NewParameter("Id", smartcontract.IntegerType),
|
||||||
manifest.NewParameter("RequestContract", smartcontract.Hash160Type),
|
manifest.NewParameter("RequestContract", smartcontract.Hash160Type),
|
||||||
manifest.NewParameter("Url", smartcontract.StringType),
|
manifest.NewParameter("Url", smartcontract.StringType),
|
||||||
|
@ -141,6 +129,11 @@ func newOracle() *Oracle {
|
||||||
return o
|
return o
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OnPersist implements Contract interface.
|
||||||
|
func (o *Oracle) OnPersist(ic *interop.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// PostPersist represents `postPersist` method.
|
// PostPersist represents `postPersist` method.
|
||||||
func (o *Oracle) PostPersist(ic *interop.Context) error {
|
func (o *Oracle) PostPersist(ic *interop.Context) error {
|
||||||
var nodes keys.PublicKeys
|
var nodes keys.PublicKeys
|
||||||
|
@ -255,7 +248,7 @@ func (o *Oracle) FinishInternal(ic *interop.Context) error {
|
||||||
stackitem.Make(resp.Code),
|
stackitem.Make(resp.Code),
|
||||||
stackitem.Make(resp.Result),
|
stackitem.Make(resp.Result),
|
||||||
}
|
}
|
||||||
cs, err := ic.DAO.GetContractState(req.CallbackContract)
|
cs, err := ic.GetContract(req.CallbackContract)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -311,7 +304,7 @@ func (o *Oracle) RequestInternal(ic *interop.Context, url string, filter *string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should be executed from contract.
|
// Should be executed from contract.
|
||||||
_, err := ic.DAO.GetContractState(ic.VM.GetCallingScriptHash())
|
_, err := ic.GetContract(ic.VM.GetCallingScriptHash())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
"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/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/network/payload"
|
"github.com/nspcc-dev/neo-go/pkg/network/payload"
|
||||||
|
@ -19,7 +20,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
policyName = "Policy"
|
|
||||||
policyContractID = -3
|
policyContractID = -3
|
||||||
|
|
||||||
defaultMaxBlockSize = 1024 * 256
|
defaultMaxBlockSize = 1024 * 256
|
||||||
|
@ -69,7 +69,7 @@ var _ interop.Contract = (*Policy)(nil)
|
||||||
|
|
||||||
// newPolicy returns Policy native contract.
|
// newPolicy returns Policy native contract.
|
||||||
func newPolicy() *Policy {
|
func newPolicy() *Policy {
|
||||||
p := &Policy{ContractMD: *interop.NewContractMD(policyName)}
|
p := &Policy{ContractMD: *interop.NewContractMD(nativenames.Policy)}
|
||||||
|
|
||||||
p.ContractID = policyContractID
|
p.ContractID = policyContractID
|
||||||
|
|
||||||
|
@ -124,13 +124,6 @@ func newPolicy() *Policy {
|
||||||
md = newMethodAndPrice(p.unblockAccount, 3000000, smartcontract.WriteStates)
|
md = newMethodAndPrice(p.unblockAccount, 3000000, smartcontract.WriteStates)
|
||||||
p.AddMethod(md, desc)
|
p.AddMethod(md, desc)
|
||||||
|
|
||||||
desc = newDescriptor("onPersist", smartcontract.VoidType)
|
|
||||||
md = newMethodAndPrice(getOnPersistWrapper(p.OnPersist), 0, smartcontract.WriteStates)
|
|
||||||
p.AddMethod(md, desc)
|
|
||||||
|
|
||||||
desc = newDescriptor("postPersist", smartcontract.VoidType)
|
|
||||||
md = newMethodAndPrice(getOnPersistWrapper(postPersistBase), 0, smartcontract.WriteStates)
|
|
||||||
p.AddMethod(md, desc)
|
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,22 +150,22 @@ func (p *Policy) OnPersist(ic *interop.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnPersistEnd updates cached Policy values if they've been changed
|
// PostPersist implements Contract interface.
|
||||||
func (p *Policy) OnPersistEnd(dao dao.DAO) error {
|
func (p *Policy) PostPersist(ic *interop.Context) error {
|
||||||
|
p.lock.Lock()
|
||||||
|
defer p.lock.Unlock()
|
||||||
if p.isValid {
|
if p.isValid {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
p.lock.Lock()
|
|
||||||
defer p.lock.Unlock()
|
|
||||||
|
|
||||||
p.maxTransactionsPerBlock = getUint32WithKey(p.ContractID, dao, maxTransactionsPerBlockKey, defaultMaxTransactionsPerBlock)
|
p.maxTransactionsPerBlock = getUint32WithKey(p.ContractID, ic.DAO, maxTransactionsPerBlockKey, defaultMaxTransactionsPerBlock)
|
||||||
p.maxBlockSize = getUint32WithKey(p.ContractID, dao, maxBlockSizeKey, defaultMaxBlockSize)
|
p.maxBlockSize = getUint32WithKey(p.ContractID, ic.DAO, maxBlockSizeKey, defaultMaxBlockSize)
|
||||||
p.feePerByte = getInt64WithKey(p.ContractID, dao, feePerByteKey, defaultFeePerByte)
|
p.feePerByte = getInt64WithKey(p.ContractID, ic.DAO, feePerByteKey, defaultFeePerByte)
|
||||||
p.maxBlockSystemFee = getInt64WithKey(p.ContractID, dao, maxBlockSystemFeeKey, defaultMaxBlockSystemFee)
|
p.maxBlockSystemFee = getInt64WithKey(p.ContractID, ic.DAO, maxBlockSystemFeeKey, defaultMaxBlockSystemFee)
|
||||||
p.maxVerificationGas = defaultMaxVerificationGas
|
p.maxVerificationGas = defaultMaxVerificationGas
|
||||||
|
|
||||||
p.blockedAccounts = make([]util.Uint160, 0)
|
p.blockedAccounts = make([]util.Uint160, 0)
|
||||||
siMap, err := dao.GetStorageItemsWithPrefix(p.ContractID, []byte{blockedAccountPrefix})
|
siMap, err := ic.DAO.GetStorageItemsWithPrefix(p.ContractID, []byte{blockedAccountPrefix})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get blocked accounts from storage: %w", err)
|
return fmt.Errorf("failed to get blocked accounts from storage: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"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/core/storage"
|
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getSerializableFromDAO(id int32, d dao.DAO, key []byte, item io.Serializable) error {
|
func getSerializableFromDAO(id int32, d dao.DAO, key []byte, item io.Serializable) error {
|
||||||
|
@ -71,3 +72,11 @@ func checkValidators(ic *interop.Context) (bool, error) {
|
||||||
}
|
}
|
||||||
return runtime.CheckHashedWitness(ic, prevBlock.NextConsensus)
|
return runtime.CheckHashedWitness(ic, prevBlock.NextConsensus)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// makeUint160Key creates a key from account script hash.
|
||||||
|
func makeUint160Key(prefix byte, h util.Uint160) []byte {
|
||||||
|
k := make([]byte, util.Uint160Size+1)
|
||||||
|
k[0] = prefix
|
||||||
|
copy(k[1:], h.BytesBE())
|
||||||
|
return k
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -33,18 +34,19 @@ func (tn *testNative) Metadata() *interop.ContractMD {
|
||||||
return &tn.meta
|
return &tn.meta
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tn *testNative) OnPersist(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
|
func (tn *testNative) OnPersist(ic *interop.Context) error {
|
||||||
if ic.Trigger != trigger.OnPersist {
|
|
||||||
panic("invalid trigger")
|
|
||||||
}
|
|
||||||
select {
|
select {
|
||||||
case tn.blocks <- ic.Block.Index:
|
case tn.blocks <- ic.Block.Index:
|
||||||
return stackitem.NewBool(true)
|
return nil
|
||||||
default:
|
default:
|
||||||
return stackitem.NewBool(false)
|
return errors.New("can't send index")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tn *testNative) PostPersist(ic *interop.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var _ interop.Contract = (*testNative)(nil)
|
var _ interop.Contract = (*testNative)(nil)
|
||||||
|
|
||||||
// registerNative registers native contract in the blockchain.
|
// registerNative registers native contract in the blockchain.
|
||||||
|
@ -106,10 +108,6 @@ func newTestNative() *testNative {
|
||||||
RequiredFlags: smartcontract.NoneFlag}
|
RequiredFlags: smartcontract.NoneFlag}
|
||||||
tn.meta.AddMethod(md, desc)
|
tn.meta.AddMethod(md, desc)
|
||||||
|
|
||||||
desc = &manifest.Method{Name: "onPersist", ReturnType: smartcontract.BoolType}
|
|
||||||
md = &interop.MethodAndPrice{Func: tn.OnPersist, RequiredFlags: smartcontract.WriteStates}
|
|
||||||
tn.meta.AddMethod(md, desc)
|
|
||||||
|
|
||||||
return tn
|
return tn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,12 +136,11 @@ func toUint160(item stackitem.Item) util.Uint160 {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tn *testNative) call(ic *interop.Context, args []stackitem.Item, checkReturn vm.CheckReturnState) {
|
func (tn *testNative) call(ic *interop.Context, args []stackitem.Item, checkReturn vm.CheckReturnState) {
|
||||||
h := toUint160(args[0])
|
cs, err := ic.GetContract(toUint160(args[0]))
|
||||||
bs, err := args[1].TryBytes()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
cs, err := ic.DAO.GetContractState(h)
|
bs, err := args[1].TryBytes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -171,7 +168,8 @@ func TestNativeContract_Invoke(t *testing.T) {
|
||||||
tn := newTestNative()
|
tn := newTestNative()
|
||||||
chain.registerNative(tn)
|
chain.registerNative(tn)
|
||||||
|
|
||||||
err := chain.dao.PutContractState(&state.Contract{
|
err := chain.contracts.Management.PutContractState(chain.dao, &state.Contract{
|
||||||
|
ID: 1,
|
||||||
Script: tn.meta.Script,
|
Script: tn.meta.Script,
|
||||||
Hash: tn.meta.Hash,
|
Hash: tn.meta.Hash,
|
||||||
Manifest: tn.meta.Manifest,
|
Manifest: tn.meta.Manifest,
|
||||||
|
@ -205,7 +203,8 @@ func TestNativeContract_InvokeInternal(t *testing.T) {
|
||||||
tn := newTestNative()
|
tn := newTestNative()
|
||||||
chain.registerNative(tn)
|
chain.registerNative(tn)
|
||||||
|
|
||||||
err := chain.dao.PutContractState(&state.Contract{
|
err := chain.contracts.Management.PutContractState(chain.dao, &state.Contract{
|
||||||
|
ID: 1,
|
||||||
Script: tn.meta.Script,
|
Script: tn.meta.Script,
|
||||||
Manifest: tn.meta.Manifest,
|
Manifest: tn.meta.Manifest,
|
||||||
})
|
})
|
||||||
|
@ -245,25 +244,36 @@ func TestNativeContract_InvokeOtherContract(t *testing.T) {
|
||||||
tn := newTestNative()
|
tn := newTestNative()
|
||||||
chain.registerNative(tn)
|
chain.registerNative(tn)
|
||||||
|
|
||||||
err := chain.dao.PutContractState(&state.Contract{
|
err := chain.contracts.Management.PutContractState(chain.dao, &state.Contract{
|
||||||
|
ID: 1,
|
||||||
Hash: tn.meta.Hash,
|
Hash: tn.meta.Hash,
|
||||||
Script: tn.meta.Script,
|
Script: tn.meta.Script,
|
||||||
Manifest: tn.meta.Manifest,
|
Manifest: tn.meta.Manifest,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
cs, _ := getTestContractState()
|
var drainTN = func(t *testing.T) {
|
||||||
require.NoError(t, chain.dao.PutContractState(cs))
|
select {
|
||||||
|
case <-tn.blocks:
|
||||||
|
default:
|
||||||
|
require.Fail(t, "testNative didn't send us block")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cs, _ := getTestContractState(chain)
|
||||||
|
require.NoError(t, chain.contracts.Management.PutContractState(chain.dao, cs))
|
||||||
|
|
||||||
t.Run("non-native, no return", func(t *testing.T) {
|
t.Run("non-native, no return", func(t *testing.T) {
|
||||||
res, err := invokeContractMethod(chain, testSumPrice*4+10000, tn.Metadata().Hash, "callOtherContractNoReturn", cs.Hash, "justReturn", []interface{}{})
|
res, err := invokeContractMethod(chain, testSumPrice*4+10000, tn.Metadata().Hash, "callOtherContractNoReturn", cs.Hash, "justReturn", []interface{}{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
drainTN(t)
|
||||||
checkResult(t, res, stackitem.Null{}) // simple call is done with EnsureNotEmpty
|
checkResult(t, res, stackitem.Null{}) // simple call is done with EnsureNotEmpty
|
||||||
})
|
})
|
||||||
t.Run("non-native, with return", func(t *testing.T) {
|
t.Run("non-native, with return", func(t *testing.T) {
|
||||||
res, err := invokeContractMethod(chain, testSumPrice*4+10000, tn.Metadata().Hash,
|
res, err := invokeContractMethod(chain, testSumPrice*4+10000, tn.Metadata().Hash,
|
||||||
"callOtherContractWithReturn", cs.Hash, "ret7", []interface{}{})
|
"callOtherContractWithReturn", cs.Hash, "ret7", []interface{}{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
drainTN(t)
|
||||||
checkResult(t, res, stackitem.Make(8))
|
checkResult(t, res, stackitem.Make(8))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,7 +134,6 @@ func TestDesignate_DesignateAsRole(t *testing.T) {
|
||||||
setSigner(tx, testchain.CommitteeScriptHash())
|
setSigner(tx, testchain.CommitteeScriptHash())
|
||||||
err = des.DesignateAsRole(ic, native.RoleOracle, keys.PublicKeys{pub})
|
err = des.DesignateAsRole(ic, native.RoleOracle, keys.PublicKeys{pub})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, des.OnPersistEnd(ic.DAO))
|
|
||||||
|
|
||||||
pubs, index, err = des.GetDesignatedByRole(ic.DAO, native.RoleOracle, bl.Index+1)
|
pubs, index, err = des.GetDesignatedByRole(ic.DAO, native.RoleOracle, bl.Index+1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -152,7 +151,6 @@ func TestDesignate_DesignateAsRole(t *testing.T) {
|
||||||
pub1 := priv.PublicKey()
|
pub1 := priv.PublicKey()
|
||||||
err = des.DesignateAsRole(ic, native.RoleStateValidator, keys.PublicKeys{pub1})
|
err = des.DesignateAsRole(ic, native.RoleStateValidator, keys.PublicKeys{pub1})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, des.OnPersistEnd(ic.DAO))
|
|
||||||
|
|
||||||
pubs, index, err = des.GetDesignatedByRole(ic.DAO, native.RoleOracle, 255)
|
pubs, index, err = des.GetDesignatedByRole(ic.DAO, native.RoleOracle, 255)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -172,7 +170,6 @@ func TestDesignate_DesignateAsRole(t *testing.T) {
|
||||||
|
|
||||||
err = des.DesignateAsRole(ic, native.RoleP2PNotary, keys.PublicKeys{pub1})
|
err = des.DesignateAsRole(ic, native.RoleP2PNotary, keys.PublicKeys{pub1})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, des.OnPersistEnd(ic.DAO))
|
|
||||||
|
|
||||||
pubs, index, err = des.GetDesignatedByRole(ic.DAO, native.RoleP2PNotary, 255)
|
pubs, index, err = des.GetDesignatedByRole(ic.DAO, native.RoleP2PNotary, 255)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
362
pkg/core/native_management_test.go
Normal file
362
pkg/core/native_management_test.go
Normal file
|
@ -0,0 +1,362 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/internal/testchain"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
|
"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/smartcontract"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestContractDeploy(t *testing.T) {
|
||||||
|
bc := newTestChain(t)
|
||||||
|
defer bc.Close()
|
||||||
|
|
||||||
|
// nef.NewFile() cares about version a lot.
|
||||||
|
config.Version = "0.90.0-test"
|
||||||
|
mgmtHash := bc.ManagementContractHash()
|
||||||
|
cs1, _ := getTestContractState(bc)
|
||||||
|
cs1.ID = 1
|
||||||
|
cs1.Hash = state.CreateContractHash(testchain.MultisigScriptHash(), cs1.Script)
|
||||||
|
manif1, err := json.Marshal(cs1.Manifest)
|
||||||
|
require.NoError(t, err)
|
||||||
|
nef1, err := nef.NewFile(cs1.Script)
|
||||||
|
require.NoError(t, err)
|
||||||
|
nef1b, err := nef1.Bytes()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
t.Run("no NEF", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(bc, 10_00000000, mgmtHash, "deploy", nil, manif1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkFAULTState(t, res)
|
||||||
|
})
|
||||||
|
t.Run("no manifest", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(bc, 10_00000000, mgmtHash, "deploy", nef1b, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkFAULTState(t, res)
|
||||||
|
})
|
||||||
|
t.Run("int for NEF", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(bc, 10_00000000, mgmtHash, "deploy", int64(1), manif1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkFAULTState(t, res)
|
||||||
|
})
|
||||||
|
t.Run("zero-length NEF", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(bc, 10_00000000, mgmtHash, "deploy", []byte{}, manif1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkFAULTState(t, res)
|
||||||
|
})
|
||||||
|
t.Run("array for NEF", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(bc, 10_00000000, mgmtHash, "deploy", []interface{}{int64(1)}, manif1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkFAULTState(t, res)
|
||||||
|
})
|
||||||
|
t.Run("int for manifest", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(bc, 10_00000000, mgmtHash, "deploy", nef1b, int64(1))
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkFAULTState(t, res)
|
||||||
|
})
|
||||||
|
t.Run("zero-length manifest", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(bc, 10_00000000, mgmtHash, "deploy", nef1b, []byte{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkFAULTState(t, res)
|
||||||
|
})
|
||||||
|
t.Run("too long manifest", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(bc, 100_00000000, mgmtHash, "deploy", nef1b, append(manif1, make([]byte, manifest.MaxManifestSize)...))
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkFAULTState(t, res)
|
||||||
|
})
|
||||||
|
t.Run("array for manifest", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(bc, 10_00000000, mgmtHash, "deploy", nef1b, []interface{}{int64(1)})
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkFAULTState(t, res)
|
||||||
|
})
|
||||||
|
t.Run("invalid manifest", func(t *testing.T) {
|
||||||
|
pkey, err := keys.NewPrivateKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var badManifest = cs1.Manifest
|
||||||
|
badManifest.Groups = []manifest.Group{manifest.Group{PublicKey: pkey.PublicKey(), Signature: make([]byte, 64)}}
|
||||||
|
manifB, err := json.Marshal(badManifest)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
res, err := invokeContractMethod(bc, 10_00000000, mgmtHash, "deploy", nef1b, manifB)
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkFAULTState(t, res)
|
||||||
|
})
|
||||||
|
t.Run("not enough GAS", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(bc, 1_00000000, mgmtHash, "deploy", nef1b, manif1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkFAULTState(t, res)
|
||||||
|
})
|
||||||
|
t.Run("positive", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(bc, 10_00000000, mgmtHash, "deploy", nef1b, manif1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, vm.HaltState, res.VMState)
|
||||||
|
require.Equal(t, 1, len(res.Stack))
|
||||||
|
compareContractStates(t, cs1, res.Stack[0])
|
||||||
|
t.Run("_deploy called", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(bc, 1_00000000, cs1.Hash, "getValue")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 1, len(res.Stack))
|
||||||
|
require.Equal(t, []byte("create"), res.Stack[0].Value())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("contract already exists", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(bc, 10_00000000, mgmtHash, "deploy", nef1b, manif1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkFAULTState(t, res)
|
||||||
|
})
|
||||||
|
t.Run("failed _deploy", func(t *testing.T) {
|
||||||
|
deployScript := []byte{byte(opcode.ABORT)}
|
||||||
|
m := manifest.NewManifest("TestDeployAbort")
|
||||||
|
m.ABI.Methods = []manifest.Method{
|
||||||
|
{
|
||||||
|
Name: manifest.MethodDeploy,
|
||||||
|
Offset: 0,
|
||||||
|
Parameters: []manifest.Parameter{
|
||||||
|
manifest.NewParameter("isUpdate", smartcontract.BoolType),
|
||||||
|
},
|
||||||
|
ReturnType: smartcontract.VoidType,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
nefD, err := nef.NewFile(deployScript)
|
||||||
|
require.NoError(t, err)
|
||||||
|
nefDb, err := nefD.Bytes()
|
||||||
|
require.NoError(t, err)
|
||||||
|
manifD, err := json.Marshal(m)
|
||||||
|
require.NoError(t, err)
|
||||||
|
res, err := invokeContractMethod(bc, 10_00000000, mgmtHash, "deploy", nefDb, manifD)
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkFAULTState(t, res)
|
||||||
|
})
|
||||||
|
t.Run("bad _deploy", func(t *testing.T) { // invalid _deploy signature
|
||||||
|
deployScript := []byte{byte(opcode.RET)}
|
||||||
|
m := manifest.NewManifest("TestBadDeploy")
|
||||||
|
m.ABI.Methods = []manifest.Method{
|
||||||
|
{
|
||||||
|
Name: manifest.MethodDeploy,
|
||||||
|
Offset: 0,
|
||||||
|
Parameters: []manifest.Parameter{
|
||||||
|
manifest.NewParameter("isUpdate", smartcontract.BoolType),
|
||||||
|
manifest.NewParameter("param", smartcontract.IntegerType),
|
||||||
|
},
|
||||||
|
ReturnType: smartcontract.VoidType,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
nefD, err := nef.NewFile(deployScript)
|
||||||
|
require.NoError(t, err)
|
||||||
|
nefDb, err := nefD.Bytes()
|
||||||
|
require.NoError(t, err)
|
||||||
|
manifD, err := json.Marshal(m)
|
||||||
|
require.NoError(t, err)
|
||||||
|
res, err := invokeContractMethod(bc, 10_00000000, mgmtHash, "deploy", nefDb, manifD)
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkFAULTState(t, res)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContractUpdate(t *testing.T) {
|
||||||
|
bc := newTestChain(t)
|
||||||
|
defer bc.Close()
|
||||||
|
|
||||||
|
// nef.NewFile() cares about version a lot.
|
||||||
|
config.Version = "0.90.0-test"
|
||||||
|
mgmtHash := bc.ManagementContractHash()
|
||||||
|
cs1, _ := getTestContractState(bc)
|
||||||
|
// Allow calling management contract.
|
||||||
|
cs1.Manifest.Permissions = []manifest.Permission{*manifest.NewPermission(manifest.PermissionWildcard)}
|
||||||
|
err := bc.contracts.Management.PutContractState(bc.dao, cs1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
manif1, err := json.Marshal(cs1.Manifest)
|
||||||
|
require.NoError(t, err)
|
||||||
|
nef1, err := nef.NewFile(cs1.Script)
|
||||||
|
require.NoError(t, err)
|
||||||
|
nef1b, err := nef1.Bytes()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
t.Run("no contract", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(bc, 10_00000000, mgmtHash, "update", nef1b, manif1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkFAULTState(t, res)
|
||||||
|
})
|
||||||
|
t.Run("zero-length NEF", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(bc, 10_00000000, cs1.Hash, "update", []byte{}, manif1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkFAULTState(t, res)
|
||||||
|
})
|
||||||
|
t.Run("zero-length manifest", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(bc, 10_00000000, cs1.Hash, "update", nef1b, []byte{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkFAULTState(t, res)
|
||||||
|
})
|
||||||
|
t.Run("not enough GAS", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(bc, 1_00000000, cs1.Hash, "update", nef1b, manif1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkFAULTState(t, res)
|
||||||
|
})
|
||||||
|
t.Run("no real params", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(bc, 10_00000000, cs1.Hash, "update", nil, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkFAULTState(t, res)
|
||||||
|
})
|
||||||
|
t.Run("invalid manifest", func(t *testing.T) {
|
||||||
|
pkey, err := keys.NewPrivateKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var badManifest = cs1.Manifest
|
||||||
|
badManifest.Groups = []manifest.Group{manifest.Group{PublicKey: pkey.PublicKey(), Signature: make([]byte, 64)}}
|
||||||
|
manifB, err := json.Marshal(badManifest)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
res, err := invokeContractMethod(bc, 10_00000000, cs1.Hash, "update", nef1b, manifB)
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkFAULTState(t, res)
|
||||||
|
})
|
||||||
|
|
||||||
|
cs1.Script = append(cs1.Script, byte(opcode.RET))
|
||||||
|
nef1, err = nef.NewFile(cs1.Script)
|
||||||
|
require.NoError(t, err)
|
||||||
|
nef1b, err = nef1.Bytes()
|
||||||
|
require.NoError(t, err)
|
||||||
|
cs1.UpdateCounter++
|
||||||
|
|
||||||
|
t.Run("update script, positive", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(bc, 10_00000000, cs1.Hash, "update", nef1b, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, vm.HaltState, res.VMState)
|
||||||
|
t.Run("_deploy called", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(bc, 1_00000000, cs1.Hash, "getValue")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 1, len(res.Stack))
|
||||||
|
require.Equal(t, []byte("update"), res.Stack[0].Value())
|
||||||
|
})
|
||||||
|
t.Run("check contract", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(bc, 1_00000000, mgmtHash, "getContract", cs1.Hash.BytesBE())
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 1, len(res.Stack))
|
||||||
|
compareContractStates(t, cs1, res.Stack[0])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
cs1.Manifest.Extra = "update me"
|
||||||
|
manif1, err = json.Marshal(cs1.Manifest)
|
||||||
|
require.NoError(t, err)
|
||||||
|
cs1.UpdateCounter++
|
||||||
|
|
||||||
|
t.Run("update manifest, positive", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(bc, 10_00000000, cs1.Hash, "update", nil, manif1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, vm.HaltState, res.VMState)
|
||||||
|
t.Run("check contract", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(bc, 1_00000000, mgmtHash, "getContract", cs1.Hash.BytesBE())
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 1, len(res.Stack))
|
||||||
|
compareContractStates(t, cs1, res.Stack[0])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
cs1.Script = append(cs1.Script, byte(opcode.ABORT))
|
||||||
|
nef1, err = nef.NewFile(cs1.Script)
|
||||||
|
require.NoError(t, err)
|
||||||
|
nef1b, err = nef1.Bytes()
|
||||||
|
require.NoError(t, err)
|
||||||
|
cs1.Manifest.Extra = "update me once more"
|
||||||
|
manif1, err = json.Marshal(cs1.Manifest)
|
||||||
|
require.NoError(t, err)
|
||||||
|
cs1.UpdateCounter++
|
||||||
|
|
||||||
|
t.Run("update both script and manifest", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(bc, 10_00000000, cs1.Hash, "update", nef1b, manif1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, vm.HaltState, res.VMState)
|
||||||
|
t.Run("check contract", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(bc, 1_00000000, mgmtHash, "getContract", cs1.Hash.BytesBE())
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 1, len(res.Stack))
|
||||||
|
compareContractStates(t, cs1, res.Stack[0])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetContract(t *testing.T) {
|
||||||
|
bc := newTestChain(t)
|
||||||
|
defer bc.Close()
|
||||||
|
|
||||||
|
mgmtHash := bc.ManagementContractHash()
|
||||||
|
cs1, _ := getTestContractState(bc)
|
||||||
|
err := bc.contracts.Management.PutContractState(bc.dao, cs1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
t.Run("bad parameter type", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(bc, 1_00000000, mgmtHash, "getContract", []interface{}{int64(1)})
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkFAULTState(t, res)
|
||||||
|
})
|
||||||
|
t.Run("not a hash", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(bc, 1_00000000, mgmtHash, "getContract", []byte{1, 2, 3})
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkFAULTState(t, res)
|
||||||
|
})
|
||||||
|
t.Run("positive", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(bc, 1_00000000, mgmtHash, "getContract", cs1.Hash.BytesBE())
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, 1, len(res.Stack))
|
||||||
|
compareContractStates(t, cs1, res.Stack[0])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContractDestroy(t *testing.T) {
|
||||||
|
bc := newTestChain(t)
|
||||||
|
defer bc.Close()
|
||||||
|
|
||||||
|
mgmtHash := bc.ManagementContractHash()
|
||||||
|
cs1, _ := getTestContractState(bc)
|
||||||
|
// Allow calling management contract.
|
||||||
|
cs1.Manifest.Permissions = []manifest.Permission{*manifest.NewPermission(manifest.PermissionWildcard)}
|
||||||
|
err := bc.contracts.Management.PutContractState(bc.dao, cs1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = bc.dao.PutStorageItem(cs1.ID, []byte{1, 2, 3}, &state.StorageItem{Value: []byte{3, 2, 1}})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
t.Run("no contract", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(bc, 1_00000000, mgmtHash, "destroy")
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkFAULTState(t, res)
|
||||||
|
})
|
||||||
|
t.Run("positive", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(bc, 1_00000000, cs1.Hash, "destroy")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, vm.HaltState, res.VMState)
|
||||||
|
t.Run("check contract", func(t *testing.T) {
|
||||||
|
res, err := invokeContractMethod(bc, 1_00000000, mgmtHash, "getContract", cs1.Hash.BytesBE())
|
||||||
|
require.NoError(t, err)
|
||||||
|
checkFAULTState(t, res)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func compareContractStates(t *testing.T, expected *state.Contract, actual stackitem.Item) {
|
||||||
|
act, ok := actual.Value().([]stackitem.Item)
|
||||||
|
require.True(t, ok)
|
||||||
|
|
||||||
|
expectedManifest, err := json.Marshal(expected.Manifest)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Equal(t, 5, len(act))
|
||||||
|
require.Equal(t, expected.ID, int32(act[0].Value().(*big.Int).Int64()))
|
||||||
|
require.Equal(t, expected.UpdateCounter, uint16(act[1].Value().(*big.Int).Int64()))
|
||||||
|
require.Equal(t, expected.Hash.BytesBE(), act[2].Value().([]byte))
|
||||||
|
require.Equal(t, expected.Script, act[3].Value().([]byte))
|
||||||
|
require.Equal(t, expectedManifest, act[4].Value().([]byte))
|
||||||
|
}
|
|
@ -225,7 +225,6 @@ func TestNEO_SetGasPerBlock(t *testing.T) {
|
||||||
ok, err := neo.SetGASPerBlock(ic, 10, big.NewInt(native.GASFactor*2))
|
ok, err := neo.SetGASPerBlock(ic, 10, big.NewInt(native.GASFactor*2))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
neo.OnPersistEnd(ic.DAO)
|
|
||||||
_, err = ic.DAO.Persist()
|
_, err = ic.DAO.Persist()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -242,7 +241,6 @@ func TestNEO_SetGasPerBlock(t *testing.T) {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
neo.OnPersistEnd(ic.DAO)
|
|
||||||
g := neo.GetGASPerBlock(ic.DAO, 9)
|
g := neo.GetGASPerBlock(ic.DAO, 9)
|
||||||
require.EqualValues(t, 5*native.GASFactor, g.Int64())
|
require.EqualValues(t, 5*native.GASFactor, g.Int64())
|
||||||
|
|
||||||
|
@ -309,8 +307,8 @@ func TestNEO_TransferOnPayment(t *testing.T) {
|
||||||
bc := newTestChain(t)
|
bc := newTestChain(t)
|
||||||
defer bc.Close()
|
defer bc.Close()
|
||||||
|
|
||||||
cs, _ := getTestContractState()
|
cs, _ := getTestContractState(bc)
|
||||||
require.NoError(t, bc.dao.PutContractState(cs))
|
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
|
||||||
|
|
||||||
const amount = 2
|
const amount = 2
|
||||||
tx := transferTokenFromMultisigAccount(t, bc, cs.Hash, bc.contracts.NEO.Hash, amount)
|
tx := transferTokenFromMultisigAccount(t, bc, cs.Hash, bc.contracts.NEO.Hash, amount)
|
||||||
|
|
|
@ -109,7 +109,7 @@ func TestOracle_Request(t *testing.T) {
|
||||||
|
|
||||||
orc := bc.contracts.Oracle
|
orc := bc.contracts.Oracle
|
||||||
cs := getOracleContractState(orc.Hash)
|
cs := getOracleContractState(orc.Hash)
|
||||||
require.NoError(t, bc.dao.PutContractState(cs))
|
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
|
||||||
|
|
||||||
gasForResponse := int64(2000_1234)
|
gasForResponse := int64(2000_1234)
|
||||||
var filter = "flt"
|
var filter = "flt"
|
||||||
|
@ -144,7 +144,6 @@ func TestOracle_Request(t *testing.T) {
|
||||||
ic.VM.LoadScript([]byte{byte(opcode.RET)})
|
ic.VM.LoadScript([]byte{byte(opcode.RET)})
|
||||||
err = bc.contracts.Designate.DesignateAsRole(ic, native.RoleOracle, keys.PublicKeys{pub})
|
err = bc.contracts.Designate.DesignateAsRole(ic, native.RoleOracle, keys.PublicKeys{pub})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, bc.contracts.Designate.OnPersistEnd(ic.DAO))
|
|
||||||
|
|
||||||
tx = transaction.New(netmode.UnitTestNet, native.GetOracleResponseScript(), 0)
|
tx = transaction.New(netmode.UnitTestNet, native.GetOracleResponseScript(), 0)
|
||||||
ic.Tx = tx
|
ic.Tx = tx
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
package state
|
|
||||||
|
|
||||||
// Coin represents the state of a coin.
|
|
||||||
type Coin uint8
|
|
||||||
|
|
||||||
// Viable Coin constants.
|
|
||||||
const (
|
|
||||||
CoinConfirmed Coin = 0
|
|
||||||
CoinSpent Coin = 1 << 1
|
|
||||||
CoinClaimed Coin = 1 << 2
|
|
||||||
CoinFrozen Coin = 1 << 5
|
|
||||||
)
|
|
|
@ -1,12 +1,18 @@
|
||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"math"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"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"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Contract holds information about a smart contract in the NEO blockchain.
|
// Contract holds information about a smart contract in the NEO blockchain.
|
||||||
|
@ -19,21 +25,81 @@ type Contract struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeBinary implements Serializable interface.
|
// DecodeBinary implements Serializable interface.
|
||||||
func (cs *Contract) DecodeBinary(br *io.BinReader) {
|
func (c *Contract) DecodeBinary(r *io.BinReader) {
|
||||||
cs.ID = int32(br.ReadU32LE())
|
si := stackitem.DecodeBinaryStackItem(r)
|
||||||
cs.UpdateCounter = br.ReadU16LE()
|
if r.Err != nil {
|
||||||
cs.Hash.DecodeBinary(br)
|
return
|
||||||
cs.Script = br.ReadVarBytes()
|
}
|
||||||
cs.Manifest.DecodeBinary(br)
|
r.Err = c.FromStackItem(si)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncodeBinary implements Serializable interface.
|
// EncodeBinary implements Serializable interface.
|
||||||
func (cs *Contract) EncodeBinary(bw *io.BinWriter) {
|
func (c *Contract) EncodeBinary(w *io.BinWriter) {
|
||||||
bw.WriteU32LE(uint32(cs.ID))
|
si, err := c.ToStackItem()
|
||||||
bw.WriteU16LE(cs.UpdateCounter)
|
if err != nil {
|
||||||
cs.Hash.EncodeBinary(bw)
|
w.Err = err
|
||||||
bw.WriteVarBytes(cs.Script)
|
return
|
||||||
cs.Manifest.EncodeBinary(bw)
|
}
|
||||||
|
stackitem.EncodeBinaryStackItem(si, w)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToStackItem converts state.Contract to stackitem.Item
|
||||||
|
func (c *Contract) ToStackItem() (stackitem.Item, error) {
|
||||||
|
manifest, err := json.Marshal(c.Manifest)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return stackitem.NewArray([]stackitem.Item{
|
||||||
|
stackitem.Make(c.ID),
|
||||||
|
stackitem.Make(c.UpdateCounter),
|
||||||
|
stackitem.NewByteArray(c.Hash.BytesBE()),
|
||||||
|
stackitem.NewByteArray(c.Script),
|
||||||
|
stackitem.NewByteArray(manifest),
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromStackItem fills Contract's data from given stack itemized contract
|
||||||
|
// representation.
|
||||||
|
func (c *Contract) FromStackItem(item stackitem.Item) error {
|
||||||
|
arr, ok := item.Value().([]stackitem.Item)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("not an array")
|
||||||
|
}
|
||||||
|
bi, ok := arr[0].Value().(*big.Int)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("ID is not an integer")
|
||||||
|
}
|
||||||
|
if !bi.IsInt64() || bi.Int64() > math.MaxInt32 || bi.Int64() < math.MinInt32 {
|
||||||
|
return errors.New("ID not in int32 range")
|
||||||
|
}
|
||||||
|
c.ID = int32(bi.Int64())
|
||||||
|
bi, ok = arr[1].Value().(*big.Int)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("UpdateCounter is not an integer")
|
||||||
|
}
|
||||||
|
if !bi.IsInt64() || bi.Int64() > math.MaxUint16 || bi.Int64() < 0 {
|
||||||
|
return errors.New("UpdateCounter not in uint16 range")
|
||||||
|
}
|
||||||
|
c.UpdateCounter = uint16(bi.Int64())
|
||||||
|
bytes, err := arr[2].TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.Hash, err = util.Uint160DecodeBytesBE(bytes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bytes, err = arr[3].TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.Script = make([]byte, len(bytes))
|
||||||
|
copy(c.Script, bytes)
|
||||||
|
bytes, err = arr[4].TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return json.Unmarshal(bytes, &c.Manifest)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateContractHash creates deployed contract hash from transaction sender
|
// CreateContractHash creates deployed contract hash from transaction sender
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"math"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/internal/testserdes"
|
"github.com/nspcc-dev/neo-go/internal/testserdes"
|
||||||
|
@ -8,6 +10,7 @@ import (
|
||||||
"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"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -58,3 +61,41 @@ func TestCreateContractHash(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, "e56e4ee87f89a70e9138432c387ad49f2ee5b55f", CreateContractHash(sender, script).StringLE())
|
require.Equal(t, "e56e4ee87f89a70e9138432c387ad49f2ee5b55f", CreateContractHash(sender, script).StringLE())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestContractFromStackItem(t *testing.T) {
|
||||||
|
var (
|
||||||
|
id = stackitem.Make(42)
|
||||||
|
counter = stackitem.Make(11)
|
||||||
|
chash = stackitem.Make(util.Uint160{1, 2, 3}.BytesBE())
|
||||||
|
script = stackitem.Make([]byte{0, 9, 8})
|
||||||
|
manifest = manifest.DefaultManifest("stack item")
|
||||||
|
manifestB, _ = json.Marshal(manifest)
|
||||||
|
manifItem = stackitem.Make(manifestB)
|
||||||
|
|
||||||
|
badCases = []struct {
|
||||||
|
name string
|
||||||
|
item stackitem.Item
|
||||||
|
}{
|
||||||
|
{"not an array", stackitem.Make(1)},
|
||||||
|
{"id is not a number", stackitem.Make([]stackitem.Item{manifItem, counter, chash, script, manifItem})},
|
||||||
|
{"id is out of range", stackitem.Make([]stackitem.Item{stackitem.Make(math.MaxUint32), counter, chash, script, manifItem})},
|
||||||
|
{"counter is not a number", stackitem.Make([]stackitem.Item{id, manifItem, chash, script, manifItem})},
|
||||||
|
{"counter is out of range", stackitem.Make([]stackitem.Item{id, stackitem.Make(100500), chash, script, manifItem})},
|
||||||
|
{"hash is not a byte string", stackitem.Make([]stackitem.Item{id, counter, stackitem.NewArray(nil), script, manifItem})},
|
||||||
|
{"hash is not a hash", stackitem.Make([]stackitem.Item{id, counter, stackitem.Make([]byte{1, 2, 3}), script, manifItem})},
|
||||||
|
{"script is not a byte string", stackitem.Make([]stackitem.Item{id, counter, chash, stackitem.NewArray(nil), manifItem})},
|
||||||
|
{"manifest is not a byte string", stackitem.Make([]stackitem.Item{id, counter, chash, script, stackitem.NewArray(nil)})},
|
||||||
|
{"manifest is not correct", stackitem.Make([]stackitem.Item{id, counter, chash, script, stackitem.Make(100500)})},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
for _, cs := range badCases {
|
||||||
|
t.Run(cs.name, func(t *testing.T) {
|
||||||
|
var c = new(Contract)
|
||||||
|
err := c.FromStackItem(cs.item)
|
||||||
|
require.Error(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
var c = new(Contract)
|
||||||
|
err := c.FromStackItem(stackitem.Make([]stackitem.Item{id, counter, chash, script, manifItem}))
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@ const (
|
||||||
DataMPT KeyPrefix = 0x03
|
DataMPT KeyPrefix = 0x03
|
||||||
STAccount KeyPrefix = 0x40
|
STAccount KeyPrefix = 0x40
|
||||||
STNotification KeyPrefix = 0x4d
|
STNotification KeyPrefix = 0x4d
|
||||||
STContract KeyPrefix = 0x50
|
|
||||||
STContractID KeyPrefix = 0x51
|
STContractID KeyPrefix = 0x51
|
||||||
STStorage KeyPrefix = 0x70
|
STStorage KeyPrefix = 0x70
|
||||||
STNEP17Transfers KeyPrefix = 0x72
|
STNEP17Transfers KeyPrefix = 0x72
|
||||||
|
@ -21,7 +20,6 @@ const (
|
||||||
IXHeaderHashList KeyPrefix = 0x80
|
IXHeaderHashList KeyPrefix = 0x80
|
||||||
SYSCurrentBlock KeyPrefix = 0xc0
|
SYSCurrentBlock KeyPrefix = 0xc0
|
||||||
SYSCurrentHeader KeyPrefix = 0xc1
|
SYSCurrentHeader KeyPrefix = 0xc1
|
||||||
SYSContractID KeyPrefix = 0xc2
|
|
||||||
SYSVersion KeyPrefix = 0xf0
|
SYSVersion KeyPrefix = 0xf0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@ var (
|
||||||
DataBlock,
|
DataBlock,
|
||||||
DataTransaction,
|
DataTransaction,
|
||||||
STAccount,
|
STAccount,
|
||||||
STContract,
|
|
||||||
STStorage,
|
STStorage,
|
||||||
IXHeaderHashList,
|
IXHeaderHashList,
|
||||||
SYSCurrentBlock,
|
SYSCurrentBlock,
|
||||||
|
@ -23,7 +22,6 @@ var (
|
||||||
0x01,
|
0x01,
|
||||||
0x02,
|
0x02,
|
||||||
0x40,
|
0x40,
|
||||||
0x50,
|
|
||||||
0x70,
|
0x70,
|
||||||
0x80,
|
0x80,
|
||||||
0xc0,
|
0xc0,
|
||||||
|
|
|
@ -5,16 +5,12 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -56,10 +52,8 @@ func createGenesisBlock(cfg config.ProtocolConfiguration) (*block.Block, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
b := &block.Block{
|
b := &block.Block{
|
||||||
Base: base,
|
Base: base,
|
||||||
Transactions: []*transaction.Transaction{
|
Transactions: []*transaction.Transaction{},
|
||||||
deployNativeContracts(cfg.Magic),
|
|
||||||
},
|
|
||||||
ConsensusData: block.ConsensusData{
|
ConsensusData: block.ConsensusData{
|
||||||
PrimaryIndex: 0,
|
PrimaryIndex: 0,
|
||||||
Nonce: 2083236893,
|
Nonce: 2083236893,
|
||||||
|
@ -70,27 +64,6 @@ func createGenesisBlock(cfg config.ProtocolConfiguration) (*block.Block, error)
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func deployNativeContracts(magic netmode.Magic) *transaction.Transaction {
|
|
||||||
buf := io.NewBufBinWriter()
|
|
||||||
emit.Syscall(buf.BinWriter, interopnames.NeoNativeDeploy)
|
|
||||||
script := buf.Bytes()
|
|
||||||
tx := transaction.New(magic, script, 0)
|
|
||||||
tx.Nonce = 0
|
|
||||||
tx.Signers = []transaction.Signer{
|
|
||||||
{
|
|
||||||
Account: hash.Hash160([]byte{byte(opcode.PUSH1)}),
|
|
||||||
Scopes: transaction.None,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
tx.Scripts = []transaction.Witness{
|
|
||||||
{
|
|
||||||
InvocationScript: []byte{},
|
|
||||||
VerificationScript: []byte{byte(opcode.PUSH1)},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return tx
|
|
||||||
}
|
|
||||||
|
|
||||||
func validatorsFromConfig(cfg config.ProtocolConfiguration) ([]*keys.PublicKey, error) {
|
func validatorsFromConfig(cfg config.ProtocolConfiguration) ([]*keys.PublicKey, error) {
|
||||||
vs, err := committeeFromConfig(cfg)
|
vs, err := committeeFromConfig(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -17,7 +17,7 @@ func TestGenesisBlockMainNet(t *testing.T) {
|
||||||
block, err := createGenesisBlock(cfg.ProtocolConfiguration)
|
block, err := createGenesisBlock(cfg.ProtocolConfiguration)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
expect := "ecaee33262f1bc7c7c28f2b25b54a5d61d50670871f45c0c6fe755a40cbde4a8"
|
expect := "00c6803707b564153d444bfcdf3a13325fc96dda55cc8a740bbd543a1d752fda"
|
||||||
assert.Equal(t, expect, block.Hash().StringLE())
|
assert.Equal(t, expect, block.Hash().StringLE())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@ package blockchain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop"
|
"github.com/nspcc-dev/neo-go/pkg/interop"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Transaction represents a NEO transaction. It's similar to Transaction class
|
// Transaction represents a NEO transaction. It's similar to Transaction class
|
||||||
|
@ -95,11 +94,3 @@ func GetTransactionFromBlock(heightOrHash interface{}, index int) interop.Hash25
|
||||||
func GetTransactionHeight(hash interop.Hash256) int {
|
func GetTransactionHeight(hash interop.Hash256) int {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetContract returns contract found by the given script hash (160 bit in BE
|
|
||||||
// format represented as a slice of 20 bytes). Refer to the `contract` package
|
|
||||||
// for details on how to use the returned structure. This function uses
|
|
||||||
// `System.Blockchain.GetContract` syscall.
|
|
||||||
func GetContract(scriptHash interop.Hash160) *contract.Contract {
|
|
||||||
return &contract.Contract{}
|
|
||||||
}
|
|
||||||
|
|
|
@ -5,15 +5,6 @@ package contract
|
||||||
|
|
||||||
import "github.com/nspcc-dev/neo-go/pkg/interop"
|
import "github.com/nspcc-dev/neo-go/pkg/interop"
|
||||||
|
|
||||||
// Contract represents a Neo contract and is used in interop functions. It's
|
|
||||||
// a data structure that you can manipulate with using functions from
|
|
||||||
// this package. It's similar in function to the Contract class in the Neo .net
|
|
||||||
// framework.
|
|
||||||
type Contract struct {
|
|
||||||
Script []byte
|
|
||||||
Manifest []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// CallFlag specifies valid call flags.
|
// CallFlag specifies valid call flags.
|
||||||
type CallFlag byte
|
type CallFlag byte
|
||||||
|
|
||||||
|
@ -30,30 +21,6 @@ const (
|
||||||
NoneFlag CallFlag = 0
|
NoneFlag CallFlag = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
// Create creates a new contract using a set of input parameters:
|
|
||||||
// script contract's bytecode (limited in length by 1M)
|
|
||||||
// manifest contract's manifest (limited in length by 2 KiB)
|
|
||||||
// It returns this new created Contract when successful (and fails transaction
|
|
||||||
// if not). It uses `System.Contract.Create` syscall.
|
|
||||||
func Create(script []byte, manifest []byte) *Contract {
|
|
||||||
return &Contract{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update updates script and manifest of the calling contract (that is the one that calls Update)
|
|
||||||
// to the new ones. Its parameters have exactly the same semantics as for
|
|
||||||
// Create. The old contract will be deleted by this call, if it has any storage
|
|
||||||
// associated it will be migrated to the new contract. New contract is returned.
|
|
||||||
// This function uses `System.Contract.Update` syscall.
|
|
||||||
func Update(script []byte, manifest []byte) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Destroy deletes calling contract (the one that calls Destroy) from the
|
|
||||||
// blockchain, so it's only possible to do that from the contract itself and
|
|
||||||
// not by any outside code. When contract is deleted all associated storage
|
|
||||||
// items are deleted too. This function uses `System.Contract.Destroy` syscall.
|
|
||||||
func Destroy() {}
|
|
||||||
|
|
||||||
// IsStandard checks if contract with provided hash is a standard signature/multisig contract.
|
// IsStandard checks if contract with provided hash is a standard signature/multisig contract.
|
||||||
// This function uses `System.Contract.IsStandard` syscall.
|
// This function uses `System.Contract.IsStandard` syscall.
|
||||||
func IsStandard(h interop.Hash160) bool {
|
func IsStandard(h interop.Hash160) bool {
|
||||||
|
|
|
@ -264,6 +264,9 @@ func (chain *testChain) GetUtilityTokenBalance(uint160 util.Uint160) *big.Int {
|
||||||
}
|
}
|
||||||
panic("TODO")
|
panic("TODO")
|
||||||
}
|
}
|
||||||
|
func (chain testChain) ManagementContractHash() util.Uint160 {
|
||||||
|
panic("TODO")
|
||||||
|
}
|
||||||
|
|
||||||
func (chain *testChain) PoolTx(tx *transaction.Transaction, _ ...*mempool.Pool) error {
|
func (chain *testChain) PoolTx(tx *transaction.Transaction, _ ...*mempool.Pool) error {
|
||||||
return chain.poolTx(tx)
|
return chain.poolTx(tx)
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpc/request"
|
"github.com/nspcc-dev/neo-go/pkg/rpc/request"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpc/response"
|
"github.com/nspcc-dev/neo-go/pkg/rpc/response"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
@ -117,16 +118,21 @@ func (c *Client) Init() error {
|
||||||
}
|
}
|
||||||
c.network = version.Magic
|
c.network = version.Magic
|
||||||
c.stateRootInHeader = version.StateRootInHeader
|
c.stateRootInHeader = version.StateRootInHeader
|
||||||
neoContractHash, err := c.GetContractStateByAddressOrName("neo")
|
neoContractHash, err := c.GetContractStateByAddressOrName(nativenames.Neo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get NEO contract scripthash: %w", err)
|
return fmt.Errorf("failed to get NEO contract scripthash: %w", err)
|
||||||
}
|
}
|
||||||
c.cache.nativeHashes["neo"] = neoContractHash.Hash
|
c.cache.nativeHashes[nativenames.Neo] = neoContractHash.Hash
|
||||||
gasContractHash, err := c.GetContractStateByAddressOrName("gas")
|
gasContractHash, err := c.GetContractStateByAddressOrName(nativenames.Gas)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get GAS contract scripthash: %w", err)
|
return fmt.Errorf("failed to get GAS contract scripthash: %w", err)
|
||||||
}
|
}
|
||||||
c.cache.nativeHashes["gas"] = gasContractHash.Hash
|
c.cache.nativeHashes[nativenames.Gas] = gasContractHash.Hash
|
||||||
|
policyContractHash, err := c.GetContractStateByAddressOrName(nativenames.Policy)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get Policy contract scripthash: %w", err)
|
||||||
|
}
|
||||||
|
c.cache.nativeHashes[nativenames.Policy] = policyContractHash.Hash
|
||||||
c.initDone = true
|
c.initDone = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,14 +3,12 @@ package client
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PolicyContractHash represents a hash of native Policy contract.
|
|
||||||
var PolicyContractHash, _ = util.Uint160DecodeStringBE("e9ff4ca7cc252e1dfddb26315869cd79505906ce")
|
|
||||||
|
|
||||||
// GetMaxTransactionsPerBlock invokes `getMaxTransactionsPerBlock` method on a
|
// GetMaxTransactionsPerBlock invokes `getMaxTransactionsPerBlock` method on a
|
||||||
// native Policy contract.
|
// native Policy contract.
|
||||||
func (c *Client) GetMaxTransactionsPerBlock() (int64, error) {
|
func (c *Client) GetMaxTransactionsPerBlock() (int64, error) {
|
||||||
|
@ -28,7 +26,10 @@ func (c *Client) GetFeePerByte() (int64, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) invokeNativePolicyMethod(operation string) (int64, error) {
|
func (c *Client) invokeNativePolicyMethod(operation string) (int64, error) {
|
||||||
result, err := c.InvokeFunction(PolicyContractHash, operation, []smartcontract.Parameter{}, nil)
|
if !c.initDone {
|
||||||
|
return 0, errNetworkNotInitialized
|
||||||
|
}
|
||||||
|
result, err := c.InvokeFunction(c.cache.nativeHashes[nativenames.Policy], operation, []smartcontract.Parameter{}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
@ -42,7 +43,10 @@ func (c *Client) invokeNativePolicyMethod(operation string) (int64, error) {
|
||||||
|
|
||||||
// IsBlocked invokes `isBlocked` method on native Policy contract.
|
// IsBlocked invokes `isBlocked` method on native Policy contract.
|
||||||
func (c *Client) IsBlocked(hash util.Uint160) (bool, error) {
|
func (c *Client) IsBlocked(hash util.Uint160) (bool, error) {
|
||||||
result, err := c.InvokeFunction(PolicyContractHash, "isBlocked", []smartcontract.Parameter{{
|
if !c.initDone {
|
||||||
|
return false, errNetworkNotInitialized
|
||||||
|
}
|
||||||
|
result, err := c.InvokeFunction(c.cache.nativeHashes[nativenames.Policy], "isBlocked", []smartcontract.Parameter{{
|
||||||
Type: smartcontract.Hash160Type,
|
Type: smartcontract.Hash160Type,
|
||||||
Value: hash,
|
Value: hash,
|
||||||
}}, nil)
|
}}, nil)
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core"
|
"github.com/nspcc-dev/neo-go/pkg/core"
|
||||||
|
@ -612,10 +611,9 @@ func (c *Client) StateRootInHeader() bool {
|
||||||
return c.stateRootInHeader
|
return c.stateRootInHeader
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNativeContractHash returns native contract hash by its name. It is not case-sensitive.
|
// GetNativeContractHash returns native contract hash by its name.
|
||||||
func (c *Client) GetNativeContractHash(name string) (util.Uint160, error) {
|
func (c *Client) GetNativeContractHash(name string) (util.Uint160, error) {
|
||||||
lowercasedName := strings.ToLower(name)
|
hash, ok := c.cache.nativeHashes[name]
|
||||||
hash, ok := c.cache.nativeHashes[lowercasedName]
|
|
||||||
if ok {
|
if ok {
|
||||||
return hash, nil
|
return hash, nil
|
||||||
}
|
}
|
||||||
|
@ -623,6 +621,6 @@ func (c *Client) GetNativeContractHash(name string) (util.Uint160, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.Uint160{}, err
|
return util.Uint160{}, err
|
||||||
}
|
}
|
||||||
c.cache.nativeHashes[lowercasedName] = cs.Hash
|
c.cache.nativeHashes[name] = cs.Hash
|
||||||
return cs.Hash, nil
|
return cs.Hash, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1463,10 +1463,12 @@ func wrapInitResponse(r *request.In, resp string) string {
|
||||||
response = resp
|
response = resp
|
||||||
}
|
}
|
||||||
switch name {
|
switch name {
|
||||||
case "neo":
|
case "NeoToken":
|
||||||
response = `{"id":1,"jsonrpc":"2.0","result":{"id":-1,"script":"DANORU9Ba2d4Cw==","manifest":{"name":"NEO","abi":{"hash":"0xde5f57d430d3dece511cf975a8d37848cb9e0525","methods":[{"name":"name","offset":0,"parameters":null,"returntype":"String"},{"name":"symbol","offset":0,"parameters":null,"returntype":"String"},{"name":"decimals","offset":0,"parameters":null,"returntype":"Integer"},{"name":"totalSupply","offset":0,"parameters":null,"returntype":"Integer"},{"name":"balanceOf","offset":0,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Integer"},{"name":"transfer","offset":0,"parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"}],"returntype":"Boolean"},{"name":"onPersist","offset":0,"parameters":null,"returntype":"Void"},{"name":"postPersist","offset":0,"parameters":null,"returntype":"Void"},{"name":"unclaimedGas","offset":0,"parameters":[{"name":"account","type":"Hash160"},{"name":"end","type":"Integer"}],"returntype":"Integer"},{"name":"registerCandidate","offset":0,"parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean"},{"name":"unregisterCandidate","offset":0,"parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean"},{"name":"vote","offset":0,"parameters":[{"name":"account","type":"Hash160"},{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean"},{"name":"getCandidates","offset":0,"parameters":null,"returntype":"Array"},{"name":"getСommittee","offset":0,"parameters":null,"returntype":"Array"},{"name":"getNextBlockValidators","offset":0,"parameters":null,"returntype":"Array"},{"name":"getGasPerBlock","offset":0,"parameters":null,"returntype":"Integer"},{"name":"setGasPerBlock","offset":0,"parameters":[{"name":"gasPerBlock","type":"Integer"}],"returntype":"Boolean"}],"events":[{"name":"Transfer","parameters":null}]},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":["NEP-5"],"trusts":[],"safemethods":["name","symbol","decimals","totalSupply","balanceOf","unclaimedGas","getCandidates","getСommittee","getNextBlockValidators"],"extra":null},"hash":"0xde5f57d430d3dece511cf975a8d37848cb9e0525"}}`
|
response = `{"id":1,"jsonrpc":"2.0","result":{"id":-1,"script":"DANORU9Ba2d4Cw==","manifest":{"name":"NEO","abi":{"hash":"0xde5f57d430d3dece511cf975a8d37848cb9e0525","methods":[{"name":"name","offset":0,"parameters":null,"returntype":"String"},{"name":"symbol","offset":0,"parameters":null,"returntype":"String"},{"name":"decimals","offset":0,"parameters":null,"returntype":"Integer"},{"name":"totalSupply","offset":0,"parameters":null,"returntype":"Integer"},{"name":"balanceOf","offset":0,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Integer"},{"name":"transfer","offset":0,"parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"}],"returntype":"Boolean"},{"name":"onPersist","offset":0,"parameters":null,"returntype":"Void"},{"name":"postPersist","offset":0,"parameters":null,"returntype":"Void"},{"name":"unclaimedGas","offset":0,"parameters":[{"name":"account","type":"Hash160"},{"name":"end","type":"Integer"}],"returntype":"Integer"},{"name":"registerCandidate","offset":0,"parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean"},{"name":"unregisterCandidate","offset":0,"parameters":[{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean"},{"name":"vote","offset":0,"parameters":[{"name":"account","type":"Hash160"},{"name":"pubkey","type":"PublicKey"}],"returntype":"Boolean"},{"name":"getCandidates","offset":0,"parameters":null,"returntype":"Array"},{"name":"getСommittee","offset":0,"parameters":null,"returntype":"Array"},{"name":"getNextBlockValidators","offset":0,"parameters":null,"returntype":"Array"},{"name":"getGasPerBlock","offset":0,"parameters":null,"returntype":"Integer"},{"name":"setGasPerBlock","offset":0,"parameters":[{"name":"gasPerBlock","type":"Integer"}],"returntype":"Boolean"}],"events":[{"name":"Transfer","parameters":null}]},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":["NEP-5"],"trusts":[],"safemethods":["name","symbol","decimals","totalSupply","balanceOf","unclaimedGas","getCandidates","getСommittee","getNextBlockValidators"],"extra":null},"hash":"0xde5f57d430d3dece511cf975a8d37848cb9e0525"}}`
|
||||||
case "gas":
|
case "GasToken":
|
||||||
response = `{"id":1,"jsonrpc":"2.0","result":{"id":-2,"script":"DANHQVNBa2d4Cw==","manifest":{"name":"GAS","abi":{"hash":"0x668e0c1f9d7b70a99dd9e06eadd4c784d641afbc","methods":[{"name":"name","offset":0,"parameters":null,"returntype":"String"},{"name":"symbol","offset":0,"parameters":null,"returntype":"String"},{"name":"decimals","offset":0,"parameters":null,"returntype":"Integer"},{"name":"totalSupply","offset":0,"parameters":null,"returntype":"Integer"},{"name":"balanceOf","offset":0,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Integer"},{"name":"transfer","offset":0,"parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"}],"returntype":"Boolean"},{"name":"onPersist","offset":0,"parameters":null,"returntype":"Void"},{"name":"postPersist","offset":0,"parameters":null,"returntype":"Void"}],"events":[{"name":"Transfer","parameters":null}]},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":["NEP-5"],"trusts":[],"safemethods":["name","symbol","decimals","totalSupply","balanceOf"],"extra":null},"hash":"0x668e0c1f9d7b70a99dd9e06eadd4c784d641afbc"}}`
|
response = `{"id":1,"jsonrpc":"2.0","result":{"id":-2,"script":"DANHQVNBa2d4Cw==","manifest":{"name":"GAS","abi":{"hash":"0x668e0c1f9d7b70a99dd9e06eadd4c784d641afbc","methods":[{"name":"name","offset":0,"parameters":null,"returntype":"String"},{"name":"symbol","offset":0,"parameters":null,"returntype":"String"},{"name":"decimals","offset":0,"parameters":null,"returntype":"Integer"},{"name":"totalSupply","offset":0,"parameters":null,"returntype":"Integer"},{"name":"balanceOf","offset":0,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Integer"},{"name":"transfer","offset":0,"parameters":[{"name":"from","type":"Hash160"},{"name":"to","type":"Hash160"},{"name":"amount","type":"Integer"}],"returntype":"Boolean"},{"name":"onPersist","offset":0,"parameters":null,"returntype":"Void"},{"name":"postPersist","offset":0,"parameters":null,"returntype":"Void"}],"events":[{"name":"Transfer","parameters":null}]},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":["NEP-5"],"trusts":[],"safemethods":["name","symbol","decimals","totalSupply","balanceOf"],"extra":null},"hash":"0x668e0c1f9d7b70a99dd9e06eadd4c784d641afbc"}}`
|
||||||
|
case "PolicyContract":
|
||||||
|
response = `{"id":1,"jsonrpc":"2.0","result":{"id":-3,"updatecounter":0,"hash":"0xac593e6183643940a9193f87c64ccf55ef19c529","script":"DAZQb2xpY3lBGvd7Zw==","manifest":{"name":"Policy","abi":{"methods":[{"name":"getMaxTransactionsPerBlock","offset":0,"parameters":null,"returntype":"Integer"},{"name":"getMaxBlockSize","offset":0,"parameters":null,"returntype":"Integer"},{"name":"getFeePerByte","offset":0,"parameters":null,"returntype":"Integer"},{"name":"isBlocked","offset":0,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Boolean"},{"name":"getMaxBlockSystemFee","offset":0,"parameters":null,"returntype":"Integer"},{"name":"setMaxBlockSize","offset":0,"parameters":[{"name":"value","type":"Integer"}],"returntype":"Boolean"},{"name":"setMaxTransactionsPerBlock","offset":0,"parameters":[{"name":"value","type":"Integer"}],"returntype":"Boolean"},{"name":"setFeePerByte","offset":0,"parameters":[{"name":"value","type":"Integer"}],"returntype":"Boolean"},{"name":"setMaxBlockSystemFee","offset":0,"parameters":[{"name":"value","type":"Integer"}],"returntype":"Boolean"},{"name":"blockAccount","offset":0,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Boolean"},{"name":"unblockAccount","offset":0,"parameters":[{"name":"account","type":"Hash160"}],"returntype":"Boolean"}],"events":[]},"groups":[],"permissions":[{"contract":"*","methods":"*"}],"supportedstandards":[],"trusts":[],"safemethods":["getMaxTransactionsPerBlock","getMaxBlockSize","getFeePerByte","isBlocked","getMaxBlockSystemFee"],"extra":null}}}`
|
||||||
default:
|
default:
|
||||||
response = resp
|
response = resp
|
||||||
}
|
}
|
||||||
|
@ -1555,3 +1557,34 @@ func TestGetNetwork(t *testing.T) {
|
||||||
require.Equal(t, netmode.UnitTestNet, c.GetNetwork())
|
require.Equal(t, netmode.UnitTestNet, c.GetNetwork())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUninitedClient(t *testing.T) {
|
||||||
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
r := request.NewRequest()
|
||||||
|
err := r.DecodeData(req.Body)
|
||||||
|
require.NoErrorf(t, err, "Cannot decode request body: %s", req.Body)
|
||||||
|
// request handler already have `getversion` response wrapper
|
||||||
|
requestHandler(t, r.In, w, "")
|
||||||
|
}))
|
||||||
|
defer srv.Close()
|
||||||
|
endpoint := srv.URL
|
||||||
|
opts := Options{}
|
||||||
|
|
||||||
|
c, err := New(context.TODO(), endpoint, opts)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = c.GetBlockByIndex(0)
|
||||||
|
require.Error(t, err)
|
||||||
|
_, err = c.GetBlockByIndexVerbose(0)
|
||||||
|
require.Error(t, err)
|
||||||
|
_, err = c.GetBlockHeader(util.Uint256{})
|
||||||
|
require.Error(t, err)
|
||||||
|
_, err = c.GetRawTransaction(util.Uint256{})
|
||||||
|
require.Error(t, err)
|
||||||
|
_, err = c.GetRawTransactionVerbose(util.Uint256{})
|
||||||
|
require.Error(t, err)
|
||||||
|
_, err = c.IsBlocked(util.Uint160{})
|
||||||
|
require.Error(t, err)
|
||||||
|
_, err = c.GetFeePerByte()
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
|
|
|
@ -1,40 +1,18 @@
|
||||||
package request
|
package request
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
|
|
||||||
"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/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"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/nef"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CreateDeploymentScript returns a script that deploys given smart contract
|
|
||||||
// with its metadata.
|
|
||||||
func CreateDeploymentScript(ne *nef.File, manif *manifest.Manifest) ([]byte, error) {
|
|
||||||
script := io.NewBufBinWriter()
|
|
||||||
rawManifest, err := json.Marshal(manif)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
neb, err := ne.Bytes()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
emit.Bytes(script.BinWriter, rawManifest)
|
|
||||||
emit.Bytes(script.BinWriter, neb)
|
|
||||||
emit.Syscall(script.BinWriter, interopnames.SystemContractCreate)
|
|
||||||
return script.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// expandArrayIntoScript pushes all FuncParam parameters from the given array
|
// expandArrayIntoScript pushes all FuncParam parameters from the given array
|
||||||
// into the given buffer in reverse order.
|
// into the given buffer in reverse order.
|
||||||
func expandArrayIntoScript(script *io.BinWriter, slice []Param) error {
|
func expandArrayIntoScript(script *io.BinWriter, slice []Param) error {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/internal/testchain"
|
"github.com/nspcc-dev/neo-go/internal/testchain"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/fee"
|
"github.com/nspcc-dev/neo-go/pkg/core/fee"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
@ -266,7 +267,7 @@ func TestCreateNEP17TransferTx(t *testing.T) {
|
||||||
priv := testchain.PrivateKeyByID(0)
|
priv := testchain.PrivateKeyByID(0)
|
||||||
acc := wallet.NewAccountFromPrivateKey(priv)
|
acc := wallet.NewAccountFromPrivateKey(priv)
|
||||||
|
|
||||||
gasContractHash, err := c.GetNativeContractHash("gas")
|
gasContractHash, err := c.GetNativeContractHash(nativenames.Gas)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
tx, err := c.CreateNEP17TransferTx(acc, util.Uint160{}, gasContractHash, 1000, 0)
|
tx, err := c.CreateNEP17TransferTx(acc, util.Uint160{}, gasContractHash, 1000, 0)
|
||||||
|
|
|
@ -57,8 +57,8 @@ type rpcTestCase struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
const testContractHash = "743ed26f78e29ecd595535b74a943b1f9ccbc444"
|
const testContractHash = "743ed26f78e29ecd595535b74a943b1f9ccbc444"
|
||||||
const deploymentTxHash = "9ecf1273fe0d8868cc024c8270b569a12edd7ea9d675c88554b937134efb03f8"
|
const deploymentTxHash = "37644146394ad76ddb9431d10b724a3cad5f8b249abdaed0b086fcd761756951"
|
||||||
const genesisBlockHash = "a496577895eb8c227bb866dc44f99f21c0cf06417ca8f2a877cc5d761a50dac0"
|
const genesisBlockHash = "0542f4350c6e236d0509bcd98188b0034bfbecc1a0c7fcdb8e4295310d468b70"
|
||||||
|
|
||||||
const verifyContractHash = "a2eb22340979804cb10cc1add0b8822c201f4d8a"
|
const verifyContractHash = "a2eb22340979804cb10cc1add0b8822c201f4d8a"
|
||||||
const verifyContractAVM = "570300412d51083021700c14aa8acf859d4fe402b34e673f2156821796a488ebdb30716813cedb2869db289740"
|
const verifyContractAVM = "570300412d51083021700c14aa8acf859d4fe402b34e673f2156821796a488ebdb30716813cedb2869db289740"
|
||||||
|
@ -89,8 +89,9 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
res, ok := acc.(*result.ApplicationLog)
|
res, ok := acc.(*result.ApplicationLog)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
assert.Equal(t, genesisBlockHash, res.Container.StringLE())
|
assert.Equal(t, genesisBlockHash, res.Container.StringLE())
|
||||||
assert.Equal(t, 1, len(res.Executions))
|
assert.Equal(t, 2, len(res.Executions))
|
||||||
assert.Equal(t, trigger.PostPersist, res.Executions[0].Trigger) // no onPersist for genesis block
|
assert.Equal(t, trigger.OnPersist, res.Executions[0].Trigger)
|
||||||
|
assert.Equal(t, trigger.PostPersist, res.Executions[1].Trigger)
|
||||||
assert.Equal(t, vm.HaltState, res.Executions[0].VMState)
|
assert.Equal(t, vm.HaltState, res.Executions[0].VMState)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -103,7 +104,7 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
assert.Equal(t, genesisBlockHash, res.Container.StringLE())
|
assert.Equal(t, genesisBlockHash, res.Container.StringLE())
|
||||||
assert.Equal(t, 1, len(res.Executions))
|
assert.Equal(t, 1, len(res.Executions))
|
||||||
assert.Equal(t, trigger.PostPersist, res.Executions[0].Trigger) // no onPersist for genesis block
|
assert.Equal(t, trigger.PostPersist, res.Executions[0].Trigger)
|
||||||
assert.Equal(t, vm.HaltState, res.Executions[0].VMState)
|
assert.Equal(t, vm.HaltState, res.Executions[0].VMState)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -115,7 +116,9 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
res, ok := acc.(*result.ApplicationLog)
|
res, ok := acc.(*result.ApplicationLog)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
assert.Equal(t, genesisBlockHash, res.Container.StringLE())
|
assert.Equal(t, genesisBlockHash, res.Container.StringLE())
|
||||||
assert.Equal(t, 0, len(res.Executions)) // no onPersist for genesis block
|
assert.Equal(t, 1, len(res.Executions))
|
||||||
|
assert.Equal(t, trigger.OnPersist, res.Executions[0].Trigger)
|
||||||
|
assert.Equal(t, vm.HaltState, res.Executions[0].VMState)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -167,7 +170,7 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "positive, native by name",
|
name: "positive, native by name",
|
||||||
params: `["Policy"]`,
|
params: `["PolicyContract"]`,
|
||||||
result: func(e *executor) interface{} { return &state.Contract{} },
|
result: func(e *executor) interface{} { return &state.Contract{} },
|
||||||
check: func(t *testing.T, e *executor, cs interface{}) {
|
check: func(t *testing.T, e *executor, cs interface{}) {
|
||||||
res, ok := cs.(*state.Contract)
|
res, ok := cs.(*state.Contract)
|
||||||
|
@ -1075,7 +1078,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("getrawtransaction", func(t *testing.T) {
|
t.Run("getrawtransaction", func(t *testing.T) {
|
||||||
block, _ := chain.GetBlock(chain.GetHeaderHash(0))
|
block, _ := chain.GetBlock(chain.GetHeaderHash(1))
|
||||||
tx := block.Transactions[0]
|
tx := block.Transactions[0]
|
||||||
rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["%s"]}"`, tx.Hash().StringLE())
|
rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["%s"]}"`, tx.Hash().StringLE())
|
||||||
body := doRPCCall(rpc, httpSrv.URL, t)
|
body := doRPCCall(rpc, httpSrv.URL, t)
|
||||||
|
@ -1090,7 +1093,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("getrawtransaction 2 arguments", func(t *testing.T) {
|
t.Run("getrawtransaction 2 arguments", func(t *testing.T) {
|
||||||
block, _ := chain.GetBlock(chain.GetHeaderHash(0))
|
block, _ := chain.GetBlock(chain.GetHeaderHash(1))
|
||||||
tx := block.Transactions[0]
|
tx := block.Transactions[0]
|
||||||
rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["%s", 0]}"`, tx.Hash().StringLE())
|
rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["%s", 0]}"`, tx.Hash().StringLE())
|
||||||
body := doRPCCall(rpc, httpSrv.URL, t)
|
body := doRPCCall(rpc, httpSrv.URL, t)
|
||||||
|
@ -1105,7 +1108,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("getrawtransaction 2 arguments, verbose", func(t *testing.T) {
|
t.Run("getrawtransaction 2 arguments, verbose", func(t *testing.T) {
|
||||||
block, _ := chain.GetBlock(chain.GetHeaderHash(0))
|
block, _ := chain.GetBlock(chain.GetHeaderHash(1))
|
||||||
TXHash := block.Transactions[0].Hash()
|
TXHash := block.Transactions[0].Hash()
|
||||||
_ = block.Transactions[0].Size()
|
_ = block.Transactions[0].Size()
|
||||||
rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["%s", 1]}"`, TXHash.StringLE())
|
rpc := fmt.Sprintf(`{"jsonrpc": "2.0", "id": 1, "method": "getrawtransaction", "params": ["%s", 1]}"`, TXHash.StringLE())
|
||||||
|
@ -1116,7 +1119,7 @@ func testRPCProtocol(t *testing.T, doRPCCall func(string, string, *testing.T) []
|
||||||
require.NoErrorf(t, err, "could not parse response: %s", txOut)
|
require.NoErrorf(t, err, "could not parse response: %s", txOut)
|
||||||
|
|
||||||
assert.Equal(t, *block.Transactions[0], actual.Transaction)
|
assert.Equal(t, *block.Transactions[0], actual.Transaction)
|
||||||
assert.Equal(t, 9, actual.Confirmations)
|
assert.Equal(t, 8, actual.Confirmations)
|
||||||
assert.Equal(t, TXHash, actual.Transaction.Hash())
|
assert.Equal(t, TXHash, actual.Transaction.Hash())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1344,7 +1347,7 @@ func checkNep17Balances(t *testing.T, e *executor, acc interface{}) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Asset: e.chain.UtilityTokenHash(),
|
Asset: e.chain.UtilityTokenHash(),
|
||||||
Amount: "80009698770",
|
Amount: "80009634770",
|
||||||
LastUpdated: 7,
|
LastUpdated: 7,
|
||||||
}},
|
}},
|
||||||
Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(),
|
Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(),
|
||||||
|
|
BIN
pkg/rpc/server/testdata/testblocks.acc
vendored
BIN
pkg/rpc/server/testdata/testblocks.acc
vendored
Binary file not shown.
|
@ -29,7 +29,7 @@ const (
|
||||||
// Magic is a magic File header constant.
|
// Magic is a magic File header constant.
|
||||||
Magic uint32 = 0x3346454E
|
Magic uint32 = 0x3346454E
|
||||||
// MaxScriptLength is the maximum allowed contract script length.
|
// MaxScriptLength is the maximum allowed contract script length.
|
||||||
MaxScriptLength = 1024 * 1024
|
MaxScriptLength = 512 * 1024
|
||||||
// compilerFieldSize is the length of `Compiler` and `Version` File header fields in bytes.
|
// compilerFieldSize is the length of `Compiler` and `Version` File header fields in bytes.
|
||||||
compilerFieldSize = 32
|
compilerFieldSize = 32
|
||||||
)
|
)
|
||||||
|
|
|
@ -74,7 +74,7 @@ func main() {
|
||||||
handleError("can't tranfser GAS", err)
|
handleError("can't tranfser GAS", err)
|
||||||
lastBlock = addBlock(bc, lastBlock, valScript, txMoveNeo, txMoveGas)
|
lastBlock = addBlock(bc, lastBlock, valScript, txMoveNeo, txMoveGas)
|
||||||
|
|
||||||
tx, contractHash, err := testchain.NewDeployTx("DumpContract", h, strings.NewReader(contract))
|
tx, contractHash, err := testchain.NewDeployTx(bc, "DumpContract", h, strings.NewReader(contract))
|
||||||
handleError("can't create deploy tx", err)
|
handleError("can't create deploy tx", err)
|
||||||
tx.NetworkFee = 10_000_000
|
tx.NetworkFee = 10_000_000
|
||||||
tx.ValidUntilBlock = bc.BlockHeight() + 1
|
tx.ValidUntilBlock = bc.BlockHeight() + 1
|
||||||
|
|
Loading…
Reference in a new issue