Merge pull request #1555 from nspcc-dev/constant-contract-hashes
Constant contract hashes
This commit is contained in:
commit
9211cb636c
46 changed files with 468 additions and 676 deletions
|
@ -10,9 +10,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
|
||||||
"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/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/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -58,7 +56,7 @@ func TestComlileAndInvokeFunction(t *testing.T) {
|
||||||
|
|
||||||
res := new(result.Invoke)
|
res := new(result.Invoke)
|
||||||
require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
|
require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
|
||||||
require.Equal(t, vm.HaltState.String(), res.State)
|
require.Equal(t, vm.HaltState.String(), res.State, res.FaultException)
|
||||||
require.Len(t, res.Stack, 1)
|
require.Len(t, res.Stack, 1)
|
||||||
require.Equal(t, []byte("on create|sub create"), res.Stack[0].Value())
|
require.Equal(t, []byte("on create|sub create"), res.Stack[0].Value())
|
||||||
|
|
||||||
|
@ -77,8 +75,6 @@ func TestComlileAndInvokeFunction(t *testing.T) {
|
||||||
|
|
||||||
rawNef, err := ioutil.ReadFile(nefName)
|
rawNef, err := ioutil.ReadFile(nefName)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
realNef, err := nef.FileFromBytes(rawNef)
|
|
||||||
require.NoError(t, err)
|
|
||||||
rawManifest, err := ioutil.ReadFile(manifestName)
|
rawManifest, err := ioutil.ReadFile(manifestName)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -87,7 +83,7 @@ func TestComlileAndInvokeFunction(t *testing.T) {
|
||||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||||
"--wallet", validatorWallet, "--address", validatorAddr,
|
"--wallet", validatorWallet, "--address", validatorAddr,
|
||||||
h.StringLE(), "update",
|
h.StringLE(), "update",
|
||||||
"bytes:"+hex.EncodeToString(realNef.Script),
|
"bytes:"+hex.EncodeToString(rawNef),
|
||||||
"bytes:"+hex.EncodeToString(rawManifest),
|
"bytes:"+hex.EncodeToString(rawManifest),
|
||||||
)
|
)
|
||||||
e.checkTxPersisted(t, "Sent invocation transaction ")
|
e.checkTxPersisted(t, "Sent invocation transaction ")
|
||||||
|
@ -95,7 +91,7 @@ func TestComlileAndInvokeFunction(t *testing.T) {
|
||||||
e.In.WriteString("one\r")
|
e.In.WriteString("one\r")
|
||||||
e.Run(t, "neo-go", "contract", "testinvokefunction",
|
e.Run(t, "neo-go", "contract", "testinvokefunction",
|
||||||
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
"--rpc-endpoint", "http://"+e.RPC.Addr,
|
||||||
hash.Hash160(realNef.Script).StringLE(), "getValue")
|
h.StringLE(), "getValue")
|
||||||
|
|
||||||
res := new(result.Invoke)
|
res := new(result.Invoke)
|
||||||
require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
|
require.NoError(t, json.Unmarshal(e.Out.Bytes(), res))
|
||||||
|
|
|
@ -15,6 +15,7 @@ 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/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/rpc/request"
|
"github.com/nspcc-dev/neo-go/pkg/rpc/request"
|
||||||
|
@ -734,13 +735,18 @@ func contractDeploy(ctx *cli.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
sender, err := address.StringToUint160(acc.Address)
|
||||||
|
if err != nil {
|
||||||
|
return cli.NewExitError(err, 1)
|
||||||
|
}
|
||||||
f, err := ioutil.ReadFile(in)
|
f, err := ioutil.ReadFile(in)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.NewExitError(err, 1)
|
return cli.NewExitError(err, 1)
|
||||||
}
|
}
|
||||||
|
// Check the file.
|
||||||
nefFile, err := nef.FileFromBytes(f)
|
nefFile, err := nef.FileFromBytes(f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.NewExitError(fmt.Errorf("failed to restore .nef file: %w", err), 1)
|
return cli.NewExitError(fmt.Errorf("failed to read .nef file: %w", err), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
manifestBytes, err := ioutil.ReadFile(manifestFile)
|
manifestBytes, err := ioutil.ReadFile(manifestFile)
|
||||||
|
@ -761,7 +767,7 @@ func contractDeploy(ctx *cli.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
txScript, err := request.CreateDeploymentScript(nefFile.Script, m)
|
txScript, err := request.CreateDeploymentScript(&nefFile, m)
|
||||||
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 create deployment script: %w", err), 1)
|
||||||
}
|
}
|
||||||
|
@ -775,7 +781,8 @@ func contractDeploy(ctx *cli.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.NewExitError(fmt.Errorf("failed to push invocation tx: %w", err), 1)
|
return cli.NewExitError(fmt.Errorf("failed to push invocation tx: %w", err), 1)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(ctx.App.Writer, "Contract: %s\n", nefFile.Header.ScriptHash.StringLE())
|
hash := state.CreateContractHash(sender, nefFile.Script)
|
||||||
|
fmt.Fprintf(ctx.App.Writer, "Contract: %s\n", hash.StringLE())
|
||||||
fmt.Fprintln(ctx.App.Writer, txHash.StringLE())
|
fmt.Fprintln(ctx.App.Writer, txHash.StringLE())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
BIN
cli/testdata/verify.nef
vendored
BIN
cli/testdata/verify.nef
vendored
Binary file not shown.
|
@ -437,7 +437,7 @@ func importDeployed(ctx *cli.Context) error {
|
||||||
if md == nil {
|
if md == nil {
|
||||||
return cli.NewExitError("contract has no `verify` method", 1)
|
return cli.NewExitError("contract has no `verify` method", 1)
|
||||||
}
|
}
|
||||||
acc.Address = address.Uint160ToString(cs.ScriptHash())
|
acc.Address = address.Uint160ToString(cs.Hash)
|
||||||
acc.Contract.Script = cs.Script
|
acc.Contract.Script = cs.Script
|
||||||
acc.Contract.Parameters = acc.Contract.Parameters[:0]
|
acc.Contract.Parameters = acc.Contract.Parameters[:0]
|
||||||
for _, p := range md.Parameters {
|
for _, p := range md.Parameters {
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
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"
|
||||||
|
"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/blockchainer"
|
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer"
|
||||||
"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/interop/interopnames"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
"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/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/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"
|
||||||
|
@ -46,28 +48,35 @@ 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, r gio.Reader) (*transaction.Transaction, []byte, error) {
|
func NewDeployTx(name string, sender util.Uint160, r gio.Reader) (*transaction.Transaction, util.Uint160, error) {
|
||||||
|
// nef.NewFile() cares about version a lot.
|
||||||
|
config.Version = "0.90.0-test"
|
||||||
|
|
||||||
avm, di, err := compiler.CompileWithDebugInfo(name, r)
|
avm, di, err := compiler.CompileWithDebugInfo(name, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, util.Uint160{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ne, err := nef.NewFile(avm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, util.Uint160{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
w := io.NewBufBinWriter()
|
|
||||||
m, err := di.ConvertToManifest(name, nil)
|
m, err := di.ConvertToManifest(name, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, util.Uint160{}, err
|
||||||
}
|
}
|
||||||
bs, err := json.Marshal(m)
|
|
||||||
|
txScript, err := request.CreateDeploymentScript(ne, m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, util.Uint160{}, err
|
||||||
}
|
}
|
||||||
emit.Bytes(w.BinWriter, bs)
|
|
||||||
emit.Bytes(w.BinWriter, avm)
|
tx := transaction.New(Network(), txScript, 100*native.GASFactor)
|
||||||
emit.Syscall(w.BinWriter, interopnames.SystemContractCreate)
|
tx.Signers = []transaction.Signer{{Account: sender}}
|
||||||
if w.Err != nil {
|
h := state.CreateContractHash(tx.Sender(), avm)
|
||||||
return nil, nil, err
|
|
||||||
}
|
return tx, h, nil
|
||||||
return transaction.New(Network(), w.Bytes(), 100*native.GASFactor), avm, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignTx signs provided transactions with validator keys.
|
// SignTx signs provided transactions with validator keys.
|
||||||
|
|
|
@ -11,16 +11,13 @@ import (
|
||||||
"unicode"
|
"unicode"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
"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"
|
||||||
"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"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// DebugInfo represents smart-contract debug information.
|
// DebugInfo represents smart-contract debug information.
|
||||||
type DebugInfo struct {
|
type DebugInfo struct {
|
||||||
MainPkg string `json:"-"`
|
MainPkg string `json:"-"`
|
||||||
Hash util.Uint160 `json:"hash"`
|
|
||||||
Documents []string `json:"documents"`
|
Documents []string `json:"documents"`
|
||||||
Methods []MethodDebugInfo `json:"methods"`
|
Methods []MethodDebugInfo `json:"methods"`
|
||||||
Events []EventDebugInfo `json:"events"`
|
Events []EventDebugInfo `json:"events"`
|
||||||
|
@ -115,7 +112,6 @@ func (c *codegen) saveSequencePoint(n ast.Node) {
|
||||||
func (c *codegen) emitDebugInfo(contract []byte) *DebugInfo {
|
func (c *codegen) emitDebugInfo(contract []byte) *DebugInfo {
|
||||||
d := &DebugInfo{
|
d := &DebugInfo{
|
||||||
MainPkg: c.mainPkg.Pkg.Name(),
|
MainPkg: c.mainPkg.Pkg.Name(),
|
||||||
Hash: hash.Hash160(contract),
|
|
||||||
Events: []EventDebugInfo{},
|
Events: []EventDebugInfo{},
|
||||||
Documents: c.documents,
|
Documents: c.documents,
|
||||||
}
|
}
|
||||||
|
@ -427,7 +423,7 @@ func (di *DebugInfo) ConvertToManifest(name string, events []manifest.Event, sup
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result := manifest.NewManifest(di.Hash, name)
|
result := manifest.NewManifest(name)
|
||||||
if supportedStandards != nil {
|
if supportedStandards != nil {
|
||||||
result.SupportedStandards = supportedStandards
|
result.SupportedStandards = supportedStandards
|
||||||
}
|
}
|
||||||
|
@ -435,7 +431,6 @@ func (di *DebugInfo) ConvertToManifest(name string, events []manifest.Event, sup
|
||||||
events = make([]manifest.Event, 0)
|
events = make([]manifest.Event, 0)
|
||||||
}
|
}
|
||||||
result.ABI = manifest.ABI{
|
result.ABI = manifest.ABI{
|
||||||
Hash: di.Hash,
|
|
||||||
Methods: methods,
|
Methods: methods,
|
||||||
Events: events,
|
Events: events,
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/internal/testserdes"
|
"github.com/nspcc-dev/neo-go/internal/testserdes"
|
||||||
"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"
|
||||||
"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"
|
||||||
|
@ -69,10 +68,6 @@ func _deploy(isUpdate bool) {}
|
||||||
d := c.emitDebugInfo(buf)
|
d := c.emitDebugInfo(buf)
|
||||||
require.NotNil(t, d)
|
require.NotNil(t, d)
|
||||||
|
|
||||||
t.Run("hash", func(t *testing.T) {
|
|
||||||
require.True(t, hash.Hash160(buf).Equals(d.Hash))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("return types", func(t *testing.T) {
|
t.Run("return types", func(t *testing.T) {
|
||||||
returnTypes := map[string]string{
|
returnTypes := map[string]string{
|
||||||
"MethodInt": "Integer",
|
"MethodInt": "Integer",
|
||||||
|
@ -155,7 +150,6 @@ func _deploy(isUpdate bool) {}
|
||||||
expected := &manifest.Manifest{
|
expected := &manifest.Manifest{
|
||||||
Name: "MyCTR",
|
Name: "MyCTR",
|
||||||
ABI: manifest.ABI{
|
ABI: manifest.ABI{
|
||||||
Hash: hash.Hash160(buf),
|
|
||||||
Methods: []manifest.Method{
|
Methods: []manifest.Method{
|
||||||
{
|
{
|
||||||
Name: "_deploy",
|
Name: "_deploy",
|
||||||
|
@ -262,7 +256,6 @@ func _deploy(isUpdate bool) {}
|
||||||
},
|
},
|
||||||
Extra: nil,
|
Extra: nil,
|
||||||
}
|
}
|
||||||
require.True(t, expected.ABI.Hash.Equals(actual.ABI.Hash))
|
|
||||||
require.ElementsMatch(t, expected.ABI.Methods, actual.ABI.Methods)
|
require.ElementsMatch(t, expected.ABI.Methods, actual.ABI.Methods)
|
||||||
require.Equal(t, expected.ABI.Events, actual.ABI.Events)
|
require.Equal(t, expected.ABI.Events, actual.ABI.Events)
|
||||||
require.Equal(t, expected.Groups, actual.Groups)
|
require.Equal(t, expected.Groups, actual.Groups)
|
||||||
|
@ -304,7 +297,6 @@ func TestSequencePoints(t *testing.T) {
|
||||||
|
|
||||||
func TestDebugInfo_MarshalJSON(t *testing.T) {
|
func TestDebugInfo_MarshalJSON(t *testing.T) {
|
||||||
d := &DebugInfo{
|
d := &DebugInfo{
|
||||||
Hash: util.Uint160{10, 11, 12, 13},
|
|
||||||
Documents: []string{"/path/to/file"},
|
Documents: []string{"/path/to/file"},
|
||||||
Methods: []MethodDebugInfo{
|
Methods: []MethodDebugInfo{
|
||||||
{
|
{
|
||||||
|
|
|
@ -107,6 +107,7 @@ func TestAppCall(t *testing.T) {
|
||||||
ih := hash.Hash160(inner)
|
ih := hash.Hash160(inner)
|
||||||
ic := interop.NewContext(trigger.Application, nil, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet, false), nil, nil, nil, zaptest.NewLogger(t))
|
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{
|
require.NoError(t, ic.DAO.PutContractState(&state.Contract{
|
||||||
|
Hash: ih,
|
||||||
Script: inner,
|
Script: inner,
|
||||||
Manifest: *m,
|
Manifest: *m,
|
||||||
}))
|
}))
|
||||||
|
|
|
@ -1601,18 +1601,15 @@ var (
|
||||||
|
|
||||||
// initVerificationVM initializes VM for witness check.
|
// initVerificationVM initializes VM for witness check.
|
||||||
func (bc *Blockchain) initVerificationVM(ic *interop.Context, hash util.Uint160, witness *transaction.Witness) error {
|
func (bc *Blockchain) initVerificationVM(ic *interop.Context, hash util.Uint160, witness *transaction.Witness) error {
|
||||||
var offset int
|
v := ic.VM
|
||||||
var isNative bool
|
if len(witness.VerificationScript) != 0 {
|
||||||
var initMD *manifest.Method
|
|
||||||
verification := witness.VerificationScript
|
|
||||||
flags := smartcontract.NoneFlag
|
|
||||||
if len(verification) != 0 {
|
|
||||||
if witness.ScriptHash() != hash {
|
if witness.ScriptHash() != hash {
|
||||||
return ErrWitnessHashMismatch
|
return ErrWitnessHashMismatch
|
||||||
}
|
}
|
||||||
if bc.contracts.ByHash(hash) != nil {
|
if bc.contracts.ByHash(hash) != nil {
|
||||||
return ErrNativeContractWitness
|
return ErrNativeContractWitness
|
||||||
}
|
}
|
||||||
|
v.LoadScriptWithFlags(witness.VerificationScript, smartcontract.NoneFlag)
|
||||||
} else {
|
} else {
|
||||||
cs, err := ic.DAO.GetContractState(hash)
|
cs, err := ic.DAO.GetContractState(hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1622,26 +1619,21 @@ func (bc *Blockchain) initVerificationVM(ic *interop.Context, hash util.Uint160,
|
||||||
if md == nil {
|
if md == nil {
|
||||||
return ErrInvalidVerificationContract
|
return ErrInvalidVerificationContract
|
||||||
}
|
}
|
||||||
verification = cs.Script
|
initMD := cs.Manifest.ABI.GetMethod(manifest.MethodInit)
|
||||||
offset = md.Offset
|
v.LoadScriptWithHash(cs.Script, hash, smartcontract.AllowStates)
|
||||||
initMD = cs.Manifest.ABI.GetMethod(manifest.MethodInit)
|
v.Jump(v.Context(), md.Offset)
|
||||||
isNative = cs.ID < 0
|
|
||||||
flags = smartcontract.AllowStates
|
|
||||||
}
|
|
||||||
|
|
||||||
v := ic.VM
|
if cs.ID < 0 {
|
||||||
v.LoadScriptWithFlags(verification, flags)
|
w := io.NewBufBinWriter()
|
||||||
v.Jump(v.Context(), offset)
|
emit.Opcodes(w.BinWriter, opcode.DEPTH, opcode.PACK)
|
||||||
if isNative {
|
emit.String(w.BinWriter, manifest.MethodVerify)
|
||||||
w := io.NewBufBinWriter()
|
if w.Err != nil {
|
||||||
emit.Opcodes(w.BinWriter, opcode.DEPTH, opcode.PACK)
|
return w.Err
|
||||||
emit.String(w.BinWriter, manifest.MethodVerify)
|
}
|
||||||
if w.Err != nil {
|
v.LoadScript(w.Bytes())
|
||||||
return w.Err
|
} else if initMD != nil {
|
||||||
|
v.Call(v.Context(), initMD.Offset)
|
||||||
}
|
}
|
||||||
v.LoadScript(w.Bytes())
|
|
||||||
} else if initMD != nil {
|
|
||||||
v.Call(v.Context(), initMD.Offset)
|
|
||||||
}
|
}
|
||||||
v.LoadScript(witness.InvocationScript)
|
v.LoadScript(witness.InvocationScript)
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -940,7 +940,7 @@ func TestVerifyHashAgainstScript(t *testing.T) {
|
||||||
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) {
|
||||||
t.Run("Missing", func(t *testing.T) {
|
t.Run("Missing", func(t *testing.T) {
|
||||||
newH := cs.ScriptHash()
|
newH := cs.Hash
|
||||||
newH[0] = ^newH[0]
|
newH[0] = ^newH[0]
|
||||||
w := &transaction.Witness{InvocationScript: []byte{byte(opcode.PUSH4)}}
|
w := &transaction.Witness{InvocationScript: []byte{byte(opcode.PUSH4)}}
|
||||||
_, err := bc.verifyHashAgainstScript(newH, w, ic, gas)
|
_, err := bc.verifyHashAgainstScript(newH, w, ic, gas)
|
||||||
|
@ -948,17 +948,17 @@ func TestVerifyHashAgainstScript(t *testing.T) {
|
||||||
})
|
})
|
||||||
t.Run("Invalid", func(t *testing.T) {
|
t.Run("Invalid", func(t *testing.T) {
|
||||||
w := &transaction.Witness{InvocationScript: []byte{byte(opcode.PUSH4)}}
|
w := &transaction.Witness{InvocationScript: []byte{byte(opcode.PUSH4)}}
|
||||||
_, err := bc.verifyHashAgainstScript(csInvalid.ScriptHash(), w, ic, gas)
|
_, err := bc.verifyHashAgainstScript(csInvalid.Hash, w, ic, gas)
|
||||||
require.True(t, errors.Is(err, ErrInvalidVerificationContract))
|
require.True(t, errors.Is(err, ErrInvalidVerificationContract))
|
||||||
})
|
})
|
||||||
t.Run("ValidSignature", func(t *testing.T) {
|
t.Run("ValidSignature", func(t *testing.T) {
|
||||||
w := &transaction.Witness{InvocationScript: []byte{byte(opcode.PUSH4)}}
|
w := &transaction.Witness{InvocationScript: []byte{byte(opcode.PUSH4)}}
|
||||||
_, err := bc.verifyHashAgainstScript(cs.ScriptHash(), w, ic, gas)
|
_, err := bc.verifyHashAgainstScript(cs.Hash, w, ic, gas)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
})
|
})
|
||||||
t.Run("InvalidSignature", func(t *testing.T) {
|
t.Run("InvalidSignature", func(t *testing.T) {
|
||||||
w := &transaction.Witness{InvocationScript: []byte{byte(opcode.PUSH3)}}
|
w := &transaction.Witness{InvocationScript: []byte{byte(opcode.PUSH3)}}
|
||||||
_, err := bc.verifyHashAgainstScript(cs.ScriptHash(), w, ic, gas)
|
_, err := bc.verifyHashAgainstScript(cs.Hash, w, ic, gas)
|
||||||
require.True(t, errors.Is(err, ErrVerificationFailed))
|
require.True(t, errors.Is(err, ErrVerificationFailed))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -1071,7 +1071,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, avm, err := testchain.NewDeployTx("TestVerify", strings.NewReader(src))
|
txDeploy, h, err := testchain.NewDeployTx("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)
|
||||||
|
@ -1080,7 +1080,7 @@ func TestIsTxStillRelevant(t *testing.T) {
|
||||||
|
|
||||||
tx := newTx(t)
|
tx := newTx(t)
|
||||||
tx.Signers = append(tx.Signers, transaction.Signer{
|
tx.Signers = append(tx.Signers, transaction.Signer{
|
||||||
Account: hash.Hash160(avm),
|
Account: h,
|
||||||
Scopes: transaction.None,
|
Scopes: transaction.None,
|
||||||
})
|
})
|
||||||
tx.NetworkFee += 1_000_000
|
tx.NetworkFee += 1_000_000
|
||||||
|
|
|
@ -42,7 +42,7 @@ func (cd *Cached) GetContractState(hash util.Uint160) (*state.Contract, error) {
|
||||||
|
|
||||||
// PutContractState puts given contract state into the given store.
|
// PutContractState puts given contract state into the given store.
|
||||||
func (cd *Cached) PutContractState(cs *state.Contract) error {
|
func (cd *Cached) PutContractState(cs *state.Contract) error {
|
||||||
cd.contracts[cs.ScriptHash()] = cs
|
cd.contracts[cs.Hash] = cs
|
||||||
return cd.DAO.PutContractState(cs)
|
return cd.DAO.PutContractState(cs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,10 +23,11 @@ func TestCachedDaoContracts(t *testing.T) {
|
||||||
_, err := dao.GetContractState(sh)
|
_, err := dao.GetContractState(sh)
|
||||||
require.NotNil(t, err)
|
require.NotNil(t, err)
|
||||||
|
|
||||||
m := manifest.NewManifest(hash.Hash160(script), "Test")
|
m := manifest.NewManifest("Test")
|
||||||
|
|
||||||
cs := &state.Contract{
|
cs := &state.Contract{
|
||||||
ID: 123,
|
ID: 123,
|
||||||
|
Hash: sh,
|
||||||
Script: script,
|
Script: script,
|
||||||
Manifest: *m,
|
Manifest: *m,
|
||||||
}
|
}
|
||||||
|
|
|
@ -136,20 +136,23 @@ func (dao *Simple) GetContractState(hash util.Uint160) (*state.Contract, error)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if contract.ScriptHash() != hash {
|
|
||||||
return nil, fmt.Errorf("found script hash is not equal to expected")
|
|
||||||
}
|
|
||||||
|
|
||||||
return contract, nil
|
return contract, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PutContractState puts given contract state into the given store.
|
// PutContractState puts given contract state into the given store.
|
||||||
func (dao *Simple) PutContractState(cs *state.Contract) error {
|
func (dao *Simple) PutContractState(cs *state.Contract) error {
|
||||||
key := storage.AppendPrefix(storage.STContract, cs.ScriptHash().BytesBE())
|
key := storage.AppendPrefix(storage.STContract, cs.Hash.BytesBE())
|
||||||
if err := dao.Put(cs, key); err != nil {
|
if err := dao.Put(cs, key); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return dao.putContractScriptHash(cs)
|
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.
|
// DeleteContractState deletes given contract state in the given store.
|
||||||
|
@ -173,16 +176,6 @@ func (dao *Simple) GetAndUpdateNextContractID() (int32, error) {
|
||||||
return id, dao.Store.Put(key, data)
|
return id, dao.Store.Put(key, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// putContractScriptHash puts given contract script hash into the given store.
|
|
||||||
// It's a private method because it should be used after PutContractState to keep
|
|
||||||
// ID-Hash pair always up-to-date.
|
|
||||||
func (dao *Simple) putContractScriptHash(cs *state.Contract) error {
|
|
||||||
key := make([]byte, 5)
|
|
||||||
key[0] = byte(storage.STContractID)
|
|
||||||
binary.LittleEndian.PutUint32(key[1:], uint32(cs.ID))
|
|
||||||
return dao.Store.Put(key, cs.ScriptHash().BytesBE())
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetContractScriptHash returns script hash of the contract with the specified ID.
|
// GetContractScriptHash returns script hash of the contract with the specified ID.
|
||||||
// Contract with the script hash may be destroyed.
|
// Contract with the script hash may be destroyed.
|
||||||
func (dao *Simple) GetContractScriptHash(id int32) (util.Uint160, error) {
|
func (dao *Simple) GetContractScriptHash(id int32) (util.Uint160, error) {
|
||||||
|
|
|
@ -10,6 +10,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/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"
|
||||||
|
@ -44,24 +45,26 @@ func (t *TestSerializable) DecodeBinary(reader *io.BinReader) {
|
||||||
|
|
||||||
func TestPutAndGetContractState(t *testing.T) {
|
func TestPutAndGetContractState(t *testing.T) {
|
||||||
dao := NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet, false)
|
dao := NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet, false)
|
||||||
contractState := &state.Contract{Script: []byte{}}
|
script := []byte{}
|
||||||
hash := contractState.ScriptHash()
|
h := hash.Hash160(script)
|
||||||
|
contractState := &state.Contract{Hash: h, Script: script}
|
||||||
err := dao.PutContractState(contractState)
|
err := dao.PutContractState(contractState)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
gotContractState, err := dao.GetContractState(hash)
|
gotContractState, err := dao.GetContractState(contractState.Hash)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, contractState, gotContractState)
|
require.Equal(t, contractState, gotContractState)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDeleteContractState(t *testing.T) {
|
func TestDeleteContractState(t *testing.T) {
|
||||||
dao := NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet, false)
|
dao := NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet, false)
|
||||||
contractState := &state.Contract{Script: []byte{}}
|
script := []byte{}
|
||||||
hash := contractState.ScriptHash()
|
h := hash.Hash160(script)
|
||||||
|
contractState := &state.Contract{Hash: h, Script: script}
|
||||||
err := dao.PutContractState(contractState)
|
err := dao.PutContractState(contractState)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
err = dao.DeleteContractState(hash)
|
err = dao.DeleteContractState(h)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
gotContractState, err := dao.GetContractState(hash)
|
gotContractState, err := dao.GetContractState(h)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.Nil(t, gotContractState)
|
require.Nil(t, gotContractState)
|
||||||
}
|
}
|
||||||
|
|
|
@ -272,10 +272,9 @@ func initBasicChain(t *testing.T, bc *Blockchain) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Push some contract into the chain.
|
// Push some contract into the chain.
|
||||||
txDeploy, avm := newDeployTx(t, prefix+"test_contract.go", "Rubl")
|
txDeploy, cHash := newDeployTx(t, priv0ScriptHash, prefix+"test_contract.go", "Rubl")
|
||||||
txDeploy.Nonce = getNextNonce()
|
txDeploy.Nonce = getNextNonce()
|
||||||
txDeploy.ValidUntilBlock = validUntilBlock
|
txDeploy.ValidUntilBlock = validUntilBlock
|
||||||
txDeploy.Signers = []transaction.Signer{{Account: priv0ScriptHash}}
|
|
||||||
require.NoError(t, addNetworkFee(bc, txDeploy, acc0))
|
require.NoError(t, addNetworkFee(bc, txDeploy, acc0))
|
||||||
require.NoError(t, acc0.SignTx(txDeploy))
|
require.NoError(t, acc0.SignTx(txDeploy))
|
||||||
b = bc.newBlock(txDeploy)
|
b = bc.newBlock(txDeploy)
|
||||||
|
@ -285,7 +284,7 @@ func initBasicChain(t *testing.T, bc *Blockchain) {
|
||||||
|
|
||||||
// Now invoke this contract.
|
// Now invoke this contract.
|
||||||
script := io.NewBufBinWriter()
|
script := io.NewBufBinWriter()
|
||||||
emit.AppCallWithOperationAndArgs(script.BinWriter, hash.Hash160(avm), "putValue", "testkey", "testvalue")
|
emit.AppCallWithOperationAndArgs(script.BinWriter, cHash, "putValue", "testkey", "testvalue")
|
||||||
|
|
||||||
txInv := transaction.New(testchain.Network(), script.Bytes(), 1*native.GASFactor)
|
txInv := transaction.New(testchain.Network(), script.Bytes(), 1*native.GASFactor)
|
||||||
txInv.Nonce = getNextNonce()
|
txInv.Nonce = getNextNonce()
|
||||||
|
@ -314,16 +313,15 @@ func initBasicChain(t *testing.T, bc *Blockchain) {
|
||||||
b = bc.newBlock(txNeo0to1)
|
b = bc.newBlock(txNeo0to1)
|
||||||
require.NoError(t, bc.AddBlock(b))
|
require.NoError(t, bc.AddBlock(b))
|
||||||
|
|
||||||
sh := hash.Hash160(avm)
|
|
||||||
w := io.NewBufBinWriter()
|
w := io.NewBufBinWriter()
|
||||||
emit.AppCallWithOperationAndArgs(w.BinWriter, sh, "init")
|
emit.AppCallWithOperationAndArgs(w.BinWriter, cHash, "init")
|
||||||
initTx := transaction.New(testchain.Network(), w.Bytes(), 1*native.GASFactor)
|
initTx := transaction.New(testchain.Network(), w.Bytes(), 1*native.GASFactor)
|
||||||
initTx.Nonce = getNextNonce()
|
initTx.Nonce = getNextNonce()
|
||||||
initTx.ValidUntilBlock = validUntilBlock
|
initTx.ValidUntilBlock = validUntilBlock
|
||||||
initTx.Signers = []transaction.Signer{{Account: priv0ScriptHash}}
|
initTx.Signers = []transaction.Signer{{Account: priv0ScriptHash}}
|
||||||
require.NoError(t, addNetworkFee(bc, initTx, acc0))
|
require.NoError(t, addNetworkFee(bc, initTx, acc0))
|
||||||
require.NoError(t, acc0.SignTx(initTx))
|
require.NoError(t, acc0.SignTx(initTx))
|
||||||
transferTx := newNEP17Transfer(sh, sh, priv0.GetScriptHash(), 1000)
|
transferTx := newNEP17Transfer(cHash, cHash, priv0.GetScriptHash(), 1000)
|
||||||
transferTx.Nonce = getNextNonce()
|
transferTx.Nonce = getNextNonce()
|
||||||
transferTx.ValidUntilBlock = validUntilBlock
|
transferTx.ValidUntilBlock = validUntilBlock
|
||||||
transferTx.Signers = []transaction.Signer{
|
transferTx.Signers = []transaction.Signer{
|
||||||
|
@ -341,7 +339,7 @@ func initBasicChain(t *testing.T, bc *Blockchain) {
|
||||||
require.NoError(t, bc.AddBlock(b))
|
require.NoError(t, bc.AddBlock(b))
|
||||||
t.Logf("recieveRublesTx: %v", transferTx.Hash().StringLE())
|
t.Logf("recieveRublesTx: %v", transferTx.Hash().StringLE())
|
||||||
|
|
||||||
transferTx = newNEP17Transfer(sh, priv0.GetScriptHash(), priv1.GetScriptHash(), 123)
|
transferTx = newNEP17Transfer(cHash, priv0.GetScriptHash(), priv1.GetScriptHash(), 123)
|
||||||
transferTx.Nonce = getNextNonce()
|
transferTx.Nonce = getNextNonce()
|
||||||
transferTx.ValidUntilBlock = validUntilBlock
|
transferTx.ValidUntilBlock = validUntilBlock
|
||||||
transferTx.Signers = []transaction.Signer{
|
transferTx.Signers = []transaction.Signer{
|
||||||
|
@ -360,10 +358,9 @@ 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, prefix+"verification_contract.go", "Verify")
|
txDeploy2, _ := newDeployTx(t, priv0ScriptHash, prefix+"verification_contract.go", "Verify")
|
||||||
txDeploy2.Nonce = getNextNonce()
|
txDeploy2.Nonce = getNextNonce()
|
||||||
txDeploy2.ValidUntilBlock = validUntilBlock
|
txDeploy2.ValidUntilBlock = validUntilBlock
|
||||||
txDeploy2.Signers = []transaction.Signer{{Account: priv0ScriptHash}}
|
|
||||||
require.NoError(t, addNetworkFee(bc, txDeploy2, acc0))
|
require.NoError(t, addNetworkFee(bc, txDeploy2, acc0))
|
||||||
require.NoError(t, acc0.SignTx(txDeploy2))
|
require.NoError(t, acc0.SignTx(txDeploy2))
|
||||||
b = bc.newBlock(txDeploy2)
|
b = bc.newBlock(txDeploy2)
|
||||||
|
@ -379,15 +376,13 @@ 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, name, ctrName string) (*transaction.Transaction, []byte) {
|
func newDeployTx(t *testing.T, 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, avm, err := testchain.NewDeployTx(ctrName, bytes.NewReader(c))
|
tx, h, err := testchain.NewDeployTx(ctrName, sender, bytes.NewReader(c))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
t.Logf("contractHash (%s): %s", name, hash.Hash160(avm).StringLE())
|
t.Logf("contractHash (%s): %s", name, h.StringLE())
|
||||||
t.Logf("contractScript: %x", avm)
|
return tx, h
|
||||||
|
|
||||||
return tx, avm
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func addSigners(txs ...*transaction.Transaction) {
|
func addSigners(txs ...*transaction.Transaction) {
|
||||||
|
|
|
@ -29,7 +29,7 @@ func (s *MethodCallback) ArgCount() int {
|
||||||
func (s *MethodCallback) LoadContext(v *vm.VM, args []stackitem.Item) {
|
func (s *MethodCallback) LoadContext(v *vm.VM, args []stackitem.Item) {
|
||||||
v.Estack().PushVal(args)
|
v.Estack().PushVal(args)
|
||||||
v.Estack().PushVal(s.method.Name)
|
v.Estack().PushVal(s.method.Name)
|
||||||
v.Estack().PushVal(s.contract.ScriptHash().BytesBE())
|
v.Estack().PushVal(s.contract.Hash.BytesBE())
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateFromMethod creates callback for a contract method.
|
// CreateFromMethod creates callback for a contract method.
|
||||||
|
@ -48,7 +48,7 @@ func CreateFromMethod(ic *interop.Context) error {
|
||||||
return errors.New("invalid method name")
|
return errors.New("invalid method name")
|
||||||
}
|
}
|
||||||
currCs, err := ic.DAO.GetContractState(ic.VM.GetCurrentScriptHash())
|
currCs, err := ic.DAO.GetContractState(ic.VM.GetCurrentScriptHash())
|
||||||
if err == nil && !currCs.Manifest.CanCall(&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")
|
||||||
}
|
}
|
||||||
md := cs.Manifest.ABI.GetMethod(method)
|
md := cs.Manifest.ABI.GetMethod(method)
|
||||||
|
|
|
@ -114,7 +114,7 @@ func NewContractMD(name string) *ContractMD {
|
||||||
|
|
||||||
c.Script = w.Bytes()
|
c.Script = w.Bytes()
|
||||||
c.Hash = hash.Hash160(c.Script)
|
c.Hash = hash.Hash160(c.Script)
|
||||||
c.Manifest = *manifest.DefaultManifest(c.Hash, name)
|
c.Manifest = *manifest.DefaultManifest(name)
|
||||||
|
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,10 +46,13 @@ func callExInternal(ic *interop.Context, h []byte, name string, args []stackitem
|
||||||
if strings.HasPrefix(name, "_") {
|
if strings.HasPrefix(name, "_") {
|
||||||
return errors.New("invalid method name (starts with '_')")
|
return errors.New("invalid method name (starts with '_')")
|
||||||
}
|
}
|
||||||
curr, err := ic.DAO.GetContractState(ic.VM.GetCurrentScriptHash())
|
ctx := ic.VM.Context()
|
||||||
if err == nil {
|
if ctx != nil && ctx.IsDeployed() {
|
||||||
if !curr.Manifest.CanCall(&cs.Manifest, name) {
|
curr, err := ic.DAO.GetContractState(ic.VM.GetCurrentScriptHash())
|
||||||
return errors.New("disallowed method call")
|
if err == nil {
|
||||||
|
if !curr.Manifest.CanCall(u, &cs.Manifest, name) {
|
||||||
|
return errors.New("disallowed method call")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return CallExInternal(ic, cs, name, args, f, vm.EnsureNotEmpty, nil)
|
return CallExInternal(ic, cs, name, args, f, vm.EnsureNotEmpty, nil)
|
||||||
|
@ -67,12 +70,11 @@ func CallExInternal(ic *interop.Context, cs *state.Contract,
|
||||||
return fmt.Errorf("invalid argument count: %d (expected %d)", len(args), len(md.Parameters))
|
return fmt.Errorf("invalid argument count: %d (expected %d)", len(args), len(md.Parameters))
|
||||||
}
|
}
|
||||||
|
|
||||||
u := cs.ScriptHash()
|
ic.VM.Invocations[cs.Hash]++
|
||||||
ic.VM.Invocations[u]++
|
ic.VM.LoadScriptWithHash(cs.Script, cs.Hash, ic.VM.Context().GetCallFlags()&f)
|
||||||
ic.VM.LoadScriptWithHash(cs.Script, u, ic.VM.Context().GetCallFlags()&f)
|
|
||||||
var isNative bool
|
var isNative bool
|
||||||
for i := range ic.Natives {
|
for i := range ic.Natives {
|
||||||
if ic.Natives[i].Metadata().Hash.Equals(u) {
|
if ic.Natives[i].Metadata().Hash.Equals(cs.Hash) {
|
||||||
isNative = true
|
isNative = true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,15 +6,16 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/mr-tron/base58"
|
"github.com/mr-tron/base58"
|
||||||
"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/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/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/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"
|
||||||
)
|
)
|
||||||
|
@ -60,39 +61,59 @@ func storageFind(ic *interop.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// createContractStateFromVM pops all contract state elements from the VM
|
// getNefAndManifestFromVM pops NEF and manifest from the VM's evaluation stack,
|
||||||
// evaluation stack, does a lot of checks and returns Contract if it
|
// does a lot of checks and returns deserialized structures if succeeds.
|
||||||
// succeeds.
|
func getNefAndManifestFromVM(v *vm.VM) (*nef.File, *manifest.Manifest, error) {
|
||||||
func createContractStateFromVM(ic *interop.Context) (*state.Contract, error) {
|
// Always pop both elements.
|
||||||
script := ic.VM.Estack().Pop().Bytes()
|
nefBytes := v.Estack().Pop().BytesOrNil()
|
||||||
if len(script) > MaxContractScriptSize {
|
manifestBytes := v.Estack().Pop().BytesOrNil()
|
||||||
return nil, errors.New("the script is too big")
|
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
manifestBytes := ic.VM.Estack().Pop().Bytes()
|
if err := checkNonEmpty(manifestBytes, manifest.MaxManifestSize); err != nil {
|
||||||
if len(manifestBytes) > manifest.MaxManifestSize {
|
return nil, nil, fmt.Errorf("invalid manifest: %w", err)
|
||||||
return nil, errors.New("manifest is too big")
|
|
||||||
}
|
}
|
||||||
if !ic.VM.AddGas(int64(StoragePrice * (len(script) + len(manifestBytes)))) {
|
|
||||||
return nil, errGasLimitExceeded
|
if !v.AddGas(int64(StoragePrice * (len(nefBytes) + len(manifestBytes)))) {
|
||||||
|
return nil, nil, errGasLimitExceeded
|
||||||
}
|
}
|
||||||
var m manifest.Manifest
|
var resManifest *manifest.Manifest
|
||||||
err := json.Unmarshal(manifestBytes, &m)
|
var resNef *nef.File
|
||||||
if err != nil {
|
if nefBytes != nil {
|
||||||
return nil, fmt.Errorf("unable to retrieve manifest from stack: %w", err)
|
nf, err := nef.FileFromBytes(nefBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("invalid NEF file: %w", err)
|
||||||
|
}
|
||||||
|
resNef = &nf
|
||||||
}
|
}
|
||||||
return &state.Contract{
|
if manifestBytes != nil {
|
||||||
Script: script,
|
resManifest = new(manifest.Manifest)
|
||||||
Manifest: m,
|
err := json.Unmarshal(manifestBytes, resManifest)
|
||||||
}, nil
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("invalid manifest: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resNef, resManifest, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// contractCreate creates a contract.
|
// contractCreate creates a contract.
|
||||||
func contractCreate(ic *interop.Context) error {
|
func contractCreate(ic *interop.Context) error {
|
||||||
newcontract, err := createContractStateFromVM(ic)
|
neff, manif, err := getNefAndManifestFromVM(ic.VM)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
contract, err := ic.DAO.GetContractState(newcontract.ScriptHash())
|
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 {
|
if contract != nil && err == nil {
|
||||||
return errors.New("contract already exists")
|
return errors.New("contract already exists")
|
||||||
}
|
}
|
||||||
|
@ -100,10 +121,15 @@ func contractCreate(ic *interop.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
newcontract.ID = id
|
if !manif.IsValid(h) {
|
||||||
if !newcontract.Manifest.IsValid(newcontract.ScriptHash()) {
|
|
||||||
return errors.New("failed to check contract script hash against manifest")
|
return errors.New("failed to check contract script hash against manifest")
|
||||||
}
|
}
|
||||||
|
newcontract := &state.Contract{
|
||||||
|
ID: id,
|
||||||
|
Hash: h,
|
||||||
|
Script: neff.Script,
|
||||||
|
Manifest: *manif,
|
||||||
|
}
|
||||||
if err := ic.DAO.PutContractState(newcontract); err != nil {
|
if err := ic.DAO.PutContractState(newcontract); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -129,65 +155,33 @@ func checkNonEmpty(b []byte, max int) error {
|
||||||
// contractUpdate migrates a contract. This method assumes that Manifest and Script
|
// contractUpdate migrates a contract. This method assumes that Manifest and Script
|
||||||
// of the contract can be updated independently.
|
// of the contract can be updated independently.
|
||||||
func contractUpdate(ic *interop.Context) error {
|
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())
|
contract, _ := ic.DAO.GetContractState(ic.VM.GetCurrentScriptHash())
|
||||||
if contract == nil {
|
if contract == nil {
|
||||||
return errors.New("contract doesn't exist")
|
return errors.New("contract doesn't exist")
|
||||||
}
|
}
|
||||||
script := ic.VM.Estack().Pop().BytesOrNil()
|
// if NEF was provided, update the contract script
|
||||||
manifestBytes := ic.VM.Estack().Pop().BytesOrNil()
|
if neff != nil {
|
||||||
if script == nil && manifestBytes == nil {
|
contract.Script = neff.Script
|
||||||
return errors.New("both script and manifest are nil")
|
|
||||||
}
|
}
|
||||||
if err := checkNonEmpty(script, MaxContractScriptSize); err != nil {
|
// if manifest was provided, update the contract manifest
|
||||||
return fmt.Errorf("invalid script size: %w", err)
|
if manif != nil {
|
||||||
}
|
contract.Manifest = *manif
|
||||||
if err := checkNonEmpty(manifestBytes, manifest.MaxManifestSize); err != nil {
|
if !contract.Manifest.IsValid(contract.Hash) {
|
||||||
return fmt.Errorf("invalid manifest size: %w", err)
|
|
||||||
}
|
|
||||||
if !ic.VM.AddGas(int64(StoragePrice * (len(script) + len(manifestBytes)))) {
|
|
||||||
return errGasLimitExceeded
|
|
||||||
}
|
|
||||||
// if script was provided, update the old contract script and Manifest.ABI hash
|
|
||||||
if script != nil {
|
|
||||||
newHash := hash.Hash160(script)
|
|
||||||
if newHash.Equals(contract.ScriptHash()) {
|
|
||||||
return errors.New("the script is the same")
|
|
||||||
} else if _, err := ic.DAO.GetContractState(newHash); err == nil {
|
|
||||||
return errors.New("contract already exists")
|
|
||||||
}
|
|
||||||
oldHash := contract.ScriptHash()
|
|
||||||
// re-write existing contract variable, as we need it to be up-to-date during manifest update
|
|
||||||
contract = &state.Contract{
|
|
||||||
ID: contract.ID,
|
|
||||||
Script: script,
|
|
||||||
Manifest: contract.Manifest,
|
|
||||||
}
|
|
||||||
contract.Manifest.ABI.Hash = newHash
|
|
||||||
if err := ic.DAO.PutContractState(contract); err != nil {
|
|
||||||
return fmt.Errorf("failed to update script: %w", err)
|
|
||||||
}
|
|
||||||
if err := ic.DAO.DeleteContractState(oldHash); err != nil {
|
|
||||||
return fmt.Errorf("failed to update script: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// if manifest was provided, update the old contract manifest and check associated
|
|
||||||
// storage items if needed
|
|
||||||
if manifestBytes != nil {
|
|
||||||
var newManifest manifest.Manifest
|
|
||||||
err := json.Unmarshal(manifestBytes, &newManifest)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to retrieve manifest from stack: %w", err)
|
|
||||||
}
|
|
||||||
// we don't have to perform `GetContractState` one more time as it's already up-to-date
|
|
||||||
contract.Manifest = newManifest
|
|
||||||
if !contract.Manifest.IsValid(contract.ScriptHash()) {
|
|
||||||
return errors.New("failed to check contract script hash against new manifest")
|
return errors.New("failed to check contract script hash against new manifest")
|
||||||
}
|
}
|
||||||
if err := ic.DAO.PutContractState(contract); err != nil {
|
|
||||||
return fmt.Errorf("failed to update manifest: %w", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
contract.UpdateCounter++
|
||||||
|
|
||||||
|
if err := ic.DAO.PutContractState(contract); err != nil {
|
||||||
|
return fmt.Errorf("failed to update contract: %w", err)
|
||||||
|
}
|
||||||
return callDeploy(ic, contract, true)
|
return callDeploy(ic, contract, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -294,9 +294,10 @@ func createVMAndPushTX(t *testing.T) (*vm.VM, *transaction.Transaction, *interop
|
||||||
|
|
||||||
func createVMAndContractState(t *testing.T) (*vm.VM, *state.Contract, *interop.Context, *Blockchain) {
|
func createVMAndContractState(t *testing.T) (*vm.VM, *state.Contract, *interop.Context, *Blockchain) {
|
||||||
script := []byte("testscript")
|
script := []byte("testscript")
|
||||||
m := manifest.NewManifest(hash.Hash160(script), "Test")
|
m := manifest.NewManifest("Test")
|
||||||
contractState := &state.Contract{
|
contractState := &state.Contract{
|
||||||
Script: script,
|
Script: script,
|
||||||
|
Hash: hash.Hash160(script),
|
||||||
Manifest: *m,
|
Manifest: *m,
|
||||||
ID: 123,
|
ID: 123,
|
||||||
}
|
}
|
||||||
|
|
|
@ -105,6 +105,9 @@ func contractToStackItem(cs *state.Contract) (stackitem.Item, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return stackitem.NewArray([]stackitem.Item{
|
return stackitem.NewArray([]stackitem.Item{
|
||||||
|
stackitem.Make(cs.ID),
|
||||||
|
stackitem.Make(cs.UpdateCounter),
|
||||||
|
stackitem.NewByteArray(cs.Hash.BytesBE()),
|
||||||
stackitem.NewByteArray(cs.Script),
|
stackitem.NewByteArray(cs.Script),
|
||||||
stackitem.NewByteArray(manifest),
|
stackitem.NewByteArray(manifest),
|
||||||
}), nil
|
}), nil
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
|
|
||||||
"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"
|
||||||
|
@ -21,6 +22,7 @@ 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"
|
||||||
|
@ -204,7 +206,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, Script: pub.GetVerificationScript()})
|
err = ic.DAO.PutContractState(&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())
|
||||||
|
@ -213,7 +215,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, Script: script}))
|
require.NoError(t, ic.DAO.PutContractState(&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))
|
||||||
|
@ -319,7 +321,7 @@ func TestBlockchainGetContractState(t *testing.T) {
|
||||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
require.NoError(t, ic.DAO.PutContractState(cs))
|
||||||
|
|
||||||
t.Run("positive", func(t *testing.T) {
|
t.Run("positive", func(t *testing.T) {
|
||||||
v.Estack().PushVal(cs.ScriptHash().BytesBE())
|
v.Estack().PushVal(cs.Hash.BytesBE())
|
||||||
require.NoError(t, bcGetContract(ic))
|
require.NoError(t, bcGetContract(ic))
|
||||||
|
|
||||||
actual := v.Estack().Pop().Item()
|
actual := v.Estack().Pop().Item()
|
||||||
|
@ -418,7 +420,7 @@ func getTestContractState() (*state.Contract, *state.Contract) {
|
||||||
|
|
||||||
script := w.Bytes()
|
script := w.Bytes()
|
||||||
h := hash.Hash160(script)
|
h := hash.Hash160(script)
|
||||||
m := manifest.NewManifest(h, "TestMain")
|
m := manifest.NewManifest("TestMain")
|
||||||
m.ABI.Methods = []manifest.Method{
|
m.ABI.Methods = []manifest.Method{
|
||||||
{
|
{
|
||||||
Name: "add",
|
Name: "add",
|
||||||
|
@ -502,12 +504,13 @@ func getTestContractState() (*state.Contract, *state.Contract) {
|
||||||
}
|
}
|
||||||
cs := &state.Contract{
|
cs := &state.Contract{
|
||||||
Script: script,
|
Script: script,
|
||||||
|
Hash: h,
|
||||||
Manifest: *m,
|
Manifest: *m,
|
||||||
ID: 42,
|
ID: 42,
|
||||||
}
|
}
|
||||||
|
|
||||||
currScript := []byte{byte(opcode.RET)}
|
currScript := []byte{byte(opcode.RET)}
|
||||||
m = manifest.NewManifest(hash.Hash160(currScript), "TestAux")
|
m = manifest.NewManifest("TestAux")
|
||||||
perm := manifest.NewPermission(manifest.PermissionHash, h)
|
perm := manifest.NewPermission(manifest.PermissionHash, h)
|
||||||
perm.Methods.Add("add")
|
perm.Methods.Add("add")
|
||||||
perm.Methods.Add("drop")
|
perm.Methods.Add("drop")
|
||||||
|
@ -519,6 +522,7 @@ func getTestContractState() (*state.Contract, *state.Contract) {
|
||||||
|
|
||||||
return cs, &state.Contract{
|
return cs, &state.Contract{
|
||||||
Script: currScript,
|
Script: currScript,
|
||||||
|
Hash: hash.Hash160(currScript),
|
||||||
Manifest: *m,
|
Manifest: *m,
|
||||||
ID: 123,
|
ID: 123,
|
||||||
}
|
}
|
||||||
|
@ -551,7 +555,7 @@ func TestContractCall(t *testing.T) {
|
||||||
require.NoError(t, ic.DAO.PutContractState(currCs))
|
require.NoError(t, ic.DAO.PutContractState(currCs))
|
||||||
|
|
||||||
currScript := currCs.Script
|
currScript := currCs.Script
|
||||||
h := cs.Manifest.ABI.Hash
|
h := hash.Hash160(cs.Script)
|
||||||
|
|
||||||
addArgs := stackitem.NewArray([]stackitem.Item{stackitem.Make(1), stackitem.Make(2)})
|
addArgs := stackitem.NewArray([]stackitem.Item{stackitem.Make(1), stackitem.Make(2)})
|
||||||
t.Run("Good", func(t *testing.T) {
|
t.Run("Good", func(t *testing.T) {
|
||||||
|
@ -577,7 +581,7 @@ func TestContractCall(t *testing.T) {
|
||||||
|
|
||||||
runInvalid := func(args ...interface{}) func(t *testing.T) {
|
runInvalid := func(args ...interface{}) func(t *testing.T) {
|
||||||
return func(t *testing.T) {
|
return func(t *testing.T) {
|
||||||
loadScript(ic, currScript, 42)
|
loadScriptWithHashAndFlags(ic, currScript, h, smartcontract.All, 42)
|
||||||
for i := range args {
|
for i := range args {
|
||||||
ic.VM.Estack().PushVal(args[i])
|
ic.VM.Estack().PushVal(args[i])
|
||||||
}
|
}
|
||||||
|
@ -654,13 +658,31 @@ func TestContractCreate(t *testing.T) {
|
||||||
v.GasLimit = -1
|
v.GasLimit = -1
|
||||||
defer bc.Close()
|
defer bc.Close()
|
||||||
|
|
||||||
|
// nef.NewFile() cares about version a lot.
|
||||||
|
config.Version = "0.90.0-test"
|
||||||
putArgsOnStack := func() {
|
putArgsOnStack := func() {
|
||||||
manifest, err := json.Marshal(cs.Manifest)
|
manifest, err := json.Marshal(cs.Manifest)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
ne, err := nef.NewFile(cs.Script)
|
||||||
|
require.NoError(t, err)
|
||||||
|
neb, err := ne.Bytes()
|
||||||
|
require.NoError(t, err)
|
||||||
v.Estack().PushVal(manifest)
|
v.Estack().PushVal(manifest)
|
||||||
v.Estack().PushVal(cs.Script)
|
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)
|
||||||
|
var sender = util.Uint160{1, 2, 3}
|
||||||
|
ic.Tx.Signers = append(ic.Tx.Signers, transaction.Signer{Account: sender})
|
||||||
|
cs.ID = 0
|
||||||
|
cs.Hash = state.CreateContractHash(sender, cs.Script)
|
||||||
|
|
||||||
t.Run("positive", func(t *testing.T) {
|
t.Run("positive", func(t *testing.T) {
|
||||||
putArgsOnStack()
|
putArgsOnStack()
|
||||||
|
|
||||||
|
@ -669,16 +691,7 @@ func TestContractCreate(t *testing.T) {
|
||||||
compareContractStates(t, cs, actual)
|
compareContractStates(t, cs, actual)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("invalid scripthash", func(t *testing.T) {
|
|
||||||
cs.Script = append(cs.Script, 0x01)
|
|
||||||
putArgsOnStack()
|
|
||||||
|
|
||||||
require.Error(t, contractCreate(ic))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("contract already exists", func(t *testing.T) {
|
t.Run("contract already exists", func(t *testing.T) {
|
||||||
cs.Script = cs.Script[:len(cs.Script)-1]
|
|
||||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
|
||||||
putArgsOnStack()
|
putArgsOnStack()
|
||||||
|
|
||||||
require.Error(t, contractCreate(ic))
|
require.Error(t, contractCreate(ic))
|
||||||
|
@ -692,9 +705,12 @@ func compareContractStates(t *testing.T, expected *state.Contract, actual stacki
|
||||||
expectedManifest, err := json.Marshal(expected.Manifest)
|
expectedManifest, err := json.Marshal(expected.Manifest)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Equal(t, 2, len(act))
|
require.Equal(t, 5, len(act))
|
||||||
require.Equal(t, expected.Script, act[0].Value().([]byte))
|
require.Equal(t, expected.ID, int32(act[0].Value().(*big.Int).Int64()))
|
||||||
require.Equal(t, expectedManifest, act[1].Value().([]byte))
|
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) {
|
func TestContractUpdate(t *testing.T) {
|
||||||
|
@ -704,12 +720,19 @@ func TestContractUpdate(t *testing.T) {
|
||||||
|
|
||||||
putArgsOnStack := func(script, manifest interface{}) {
|
putArgsOnStack := func(script, manifest interface{}) {
|
||||||
v.Estack().PushVal(manifest)
|
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)
|
v.Estack().PushVal(script)
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("no args", func(t *testing.T) {
|
t.Run("no args", func(t *testing.T) {
|
||||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
require.NoError(t, ic.DAO.PutContractState(cs))
|
||||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
|
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.Hash, smartcontract.All)
|
||||||
putArgsOnStack(stackitem.Null{}, stackitem.Null{})
|
putArgsOnStack(stackitem.Null{}, stackitem.Null{})
|
||||||
require.Error(t, contractUpdate(ic))
|
require.Error(t, contractUpdate(ic))
|
||||||
})
|
})
|
||||||
|
@ -717,19 +740,20 @@ func TestContractUpdate(t *testing.T) {
|
||||||
t.Run("no contract", func(t *testing.T) {
|
t.Run("no contract", func(t *testing.T) {
|
||||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
require.NoError(t, ic.DAO.PutContractState(cs))
|
||||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, util.Uint160{8, 9, 7}, smartcontract.All)
|
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, util.Uint160{8, 9, 7}, smartcontract.All)
|
||||||
|
putArgsOnStack([]byte{1}, stackitem.Null{})
|
||||||
require.Error(t, contractUpdate(ic))
|
require.Error(t, contractUpdate(ic))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("too large script", func(t *testing.T) {
|
t.Run("too large script", func(t *testing.T) {
|
||||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
require.NoError(t, ic.DAO.PutContractState(cs))
|
||||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
|
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.Hash, smartcontract.All)
|
||||||
putArgsOnStack(make([]byte, MaxContractScriptSize+1), stackitem.Null{})
|
putArgsOnStack(make([]byte, MaxContractScriptSize+1), stackitem.Null{})
|
||||||
require.Error(t, contractUpdate(ic))
|
require.Error(t, contractUpdate(ic))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("too large manifest", func(t *testing.T) {
|
t.Run("too large manifest", func(t *testing.T) {
|
||||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
require.NoError(t, ic.DAO.PutContractState(cs))
|
||||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
|
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.Hash, smartcontract.All)
|
||||||
putArgsOnStack(stackitem.Null{}, make([]byte, manifest.MaxManifestSize+1))
|
putArgsOnStack(stackitem.Null{}, make([]byte, manifest.MaxManifestSize+1))
|
||||||
require.Error(t, contractUpdate(ic))
|
require.Error(t, contractUpdate(ic))
|
||||||
})
|
})
|
||||||
|
@ -737,133 +761,85 @@ func TestContractUpdate(t *testing.T) {
|
||||||
t.Run("gas limit exceeded", func(t *testing.T) {
|
t.Run("gas limit exceeded", func(t *testing.T) {
|
||||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
require.NoError(t, ic.DAO.PutContractState(cs))
|
||||||
v.GasLimit = 0
|
v.GasLimit = 0
|
||||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
|
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.Hash, smartcontract.All)
|
||||||
putArgsOnStack([]byte{1}, []byte{2})
|
putArgsOnStack([]byte{1}, []byte{2})
|
||||||
require.Error(t, contractUpdate(ic))
|
require.Error(t, contractUpdate(ic))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("update script, the same script", func(t *testing.T) {
|
v.GasLimit = -1
|
||||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
|
||||||
v.GasLimit = -1
|
|
||||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
|
|
||||||
putArgsOnStack(cs.Script, stackitem.Null{})
|
|
||||||
|
|
||||||
require.Error(t, contractUpdate(ic))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("update script, already exists", func(t *testing.T) {
|
|
||||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
|
||||||
duplicateScript := []byte{byte(opcode.PUSHDATA4)}
|
|
||||||
require.NoError(t, ic.DAO.PutContractState(&state.Contract{
|
|
||||||
ID: 95,
|
|
||||||
Script: duplicateScript,
|
|
||||||
Manifest: manifest.Manifest{
|
|
||||||
ABI: manifest.ABI{
|
|
||||||
Hash: hash.Hash160(duplicateScript),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
|
|
||||||
putArgsOnStack(duplicateScript, stackitem.Null{})
|
|
||||||
|
|
||||||
require.Error(t, contractUpdate(ic))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("update script, positive", func(t *testing.T) {
|
t.Run("update script, positive", func(t *testing.T) {
|
||||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
require.NoError(t, ic.DAO.PutContractState(cs))
|
||||||
t.Run("empty manifest", func(t *testing.T) {
|
t.Run("empty manifest", func(t *testing.T) {
|
||||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
|
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.Hash, smartcontract.All)
|
||||||
newScript := []byte{9, 8, 7, 6, 5}
|
newScript := []byte{9, 8, 7, 6, 5}
|
||||||
putArgsOnStack(newScript, []byte{})
|
putArgsOnStack(newScript, []byte{})
|
||||||
require.Error(t, contractUpdate(ic))
|
require.Error(t, contractUpdate(ic))
|
||||||
})
|
})
|
||||||
|
|
||||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
|
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.Hash, smartcontract.All)
|
||||||
newScript := []byte{9, 8, 7, 6, 5}
|
newScript := []byte{9, 8, 7, 6, 5}
|
||||||
putArgsOnStack(newScript, stackitem.Null{})
|
putArgsOnStack(newScript, stackitem.Null{})
|
||||||
|
|
||||||
require.NoError(t, contractUpdate(ic))
|
require.NoError(t, contractUpdate(ic))
|
||||||
|
|
||||||
// updated contract should have new scripthash
|
// updated contract should have the same scripthash
|
||||||
actual, err := ic.DAO.GetContractState(hash.Hash160(newScript))
|
actual, err := ic.DAO.GetContractState(cs.Hash)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
expected := &state.Contract{
|
expected := &state.Contract{
|
||||||
ID: cs.ID,
|
ID: cs.ID,
|
||||||
Script: newScript,
|
UpdateCounter: 1,
|
||||||
Manifest: cs.Manifest,
|
Hash: cs.Hash,
|
||||||
|
Script: newScript,
|
||||||
|
Manifest: cs.Manifest,
|
||||||
}
|
}
|
||||||
expected.Manifest.ABI.Hash = hash.Hash160(newScript)
|
|
||||||
_ = expected.ScriptHash()
|
|
||||||
require.Equal(t, expected, actual)
|
require.Equal(t, expected, actual)
|
||||||
|
|
||||||
// old contract should be deleted
|
|
||||||
_, err = ic.DAO.GetContractState(cs.ScriptHash())
|
|
||||||
require.Error(t, err)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("update manifest, bad manifest", func(t *testing.T) {
|
t.Run("update manifest, bad manifest", func(t *testing.T) {
|
||||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
require.NoError(t, ic.DAO.PutContractState(cs))
|
||||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
|
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.Hash, smartcontract.All)
|
||||||
putArgsOnStack(stackitem.Null{}, []byte{1, 2, 3})
|
putArgsOnStack(stackitem.Null{}, []byte{1, 2, 3})
|
||||||
|
|
||||||
require.Error(t, contractUpdate(ic))
|
require.Error(t, contractUpdate(ic))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("update manifest, bad contract hash", func(t *testing.T) {
|
|
||||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
|
||||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
|
|
||||||
manifest := &manifest.Manifest{
|
|
||||||
ABI: manifest.ABI{
|
|
||||||
Hash: util.Uint160{4, 5, 6},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
manifestBytes, err := json.Marshal(manifest)
|
|
||||||
require.NoError(t, err)
|
|
||||||
putArgsOnStack(stackitem.Null{}, manifestBytes)
|
|
||||||
|
|
||||||
require.Error(t, contractUpdate(ic))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("update manifest, positive", func(t *testing.T) {
|
t.Run("update manifest, positive", func(t *testing.T) {
|
||||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
require.NoError(t, ic.DAO.PutContractState(cs))
|
||||||
manifest := &manifest.Manifest{
|
manifest := &manifest.Manifest{
|
||||||
ABI: manifest.ABI{
|
ABI: manifest.ABI{},
|
||||||
Hash: cs.ScriptHash(),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
manifestBytes, err := json.Marshal(manifest)
|
manifestBytes, err := json.Marshal(manifest)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
t.Run("empty script", func(t *testing.T) {
|
t.Run("empty script", func(t *testing.T) {
|
||||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
|
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.Hash, smartcontract.All)
|
||||||
putArgsOnStack([]byte{}, manifestBytes)
|
putArgsOnStack([]byte{}, manifestBytes)
|
||||||
require.Error(t, contractUpdate(ic))
|
require.Error(t, contractUpdate(ic))
|
||||||
})
|
})
|
||||||
|
|
||||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
|
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.Hash, smartcontract.All)
|
||||||
putArgsOnStack(stackitem.Null{}, manifestBytes)
|
putArgsOnStack(stackitem.Null{}, manifestBytes)
|
||||||
require.NoError(t, contractUpdate(ic))
|
require.NoError(t, contractUpdate(ic))
|
||||||
|
|
||||||
// updated contract should have new scripthash
|
// updated contract should have old scripthash
|
||||||
actual, err := ic.DAO.GetContractState(cs.ScriptHash())
|
actual, err := ic.DAO.GetContractState(cs.Hash)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
expected := &state.Contract{
|
expected := &state.Contract{
|
||||||
ID: cs.ID,
|
ID: cs.ID,
|
||||||
Script: cs.Script,
|
UpdateCounter: 2,
|
||||||
Manifest: *manifest,
|
Hash: cs.Hash,
|
||||||
|
Script: cs.Script,
|
||||||
|
Manifest: *manifest,
|
||||||
}
|
}
|
||||||
_ = expected.ScriptHash()
|
|
||||||
require.Equal(t, expected, actual)
|
require.Equal(t, expected, actual)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("update both script and manifest", func(t *testing.T) {
|
t.Run("update both script and manifest", func(t *testing.T) {
|
||||||
require.NoError(t, ic.DAO.PutContractState(cs))
|
require.NoError(t, ic.DAO.PutContractState(cs))
|
||||||
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.ScriptHash(), smartcontract.All)
|
v.LoadScriptWithHash([]byte{byte(opcode.RET)}, cs.Hash, smartcontract.All)
|
||||||
newScript := []byte{12, 13, 14}
|
newScript := []byte{12, 13, 14}
|
||||||
newManifest := manifest.Manifest{
|
newManifest := manifest.Manifest{
|
||||||
ABI: manifest.ABI{
|
ABI: manifest.ABI{},
|
||||||
Hash: hash.Hash160(newScript),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
newManifestBytes, err := json.Marshal(newManifest)
|
newManifestBytes, err := json.Marshal(newManifest)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -873,20 +849,16 @@ func TestContractUpdate(t *testing.T) {
|
||||||
require.NoError(t, contractUpdate(ic))
|
require.NoError(t, contractUpdate(ic))
|
||||||
|
|
||||||
// updated contract should have new script and manifest
|
// updated contract should have new script and manifest
|
||||||
actual, err := ic.DAO.GetContractState(hash.Hash160(newScript))
|
actual, err := ic.DAO.GetContractState(cs.Hash)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
expected := &state.Contract{
|
expected := &state.Contract{
|
||||||
ID: cs.ID,
|
ID: cs.ID,
|
||||||
Script: newScript,
|
UpdateCounter: 3,
|
||||||
Manifest: newManifest,
|
Hash: cs.Hash,
|
||||||
|
Script: newScript,
|
||||||
|
Manifest: newManifest,
|
||||||
}
|
}
|
||||||
expected.Manifest.ABI.Hash = hash.Hash160(newScript)
|
|
||||||
_ = expected.ScriptHash()
|
|
||||||
require.Equal(t, expected, actual)
|
require.Equal(t, expected, actual)
|
||||||
|
|
||||||
// old contract should be deleted
|
|
||||||
_, err = ic.DAO.GetContractState(cs.ScriptHash())
|
|
||||||
require.Error(t, err)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -901,37 +873,45 @@ func TestContractCreateDeploy(t *testing.T) {
|
||||||
rawManifest, err := json.Marshal(cs.Manifest)
|
rawManifest, err := json.Marshal(cs.Manifest)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
v.Estack().PushVal(rawManifest)
|
v.Estack().PushVal(rawManifest)
|
||||||
v.Estack().PushVal(cs.Script)
|
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()
|
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)
|
v.LoadScriptWithFlags([]byte{byte(opcode.RET)}, smartcontract.All)
|
||||||
putArgs(cs)
|
putArgs(cs)
|
||||||
require.NoError(t, contractCreate(ic))
|
require.NoError(t, contractCreate(ic))
|
||||||
require.NoError(t, ic.VM.Run())
|
require.NoError(t, ic.VM.Run())
|
||||||
|
|
||||||
v.LoadScriptWithFlags(currCs.Script, smartcontract.All)
|
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, nil)
|
err := contract.CallExInternal(ic, cs, "getValue", nil, smartcontract.All, vm.EnsureNotEmpty, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, v.Run())
|
require.NoError(t, v.Run())
|
||||||
require.Equal(t, "create", v.Estack().Pop().String())
|
require.Equal(t, "create", v.Estack().Pop().String())
|
||||||
|
|
||||||
v.LoadScriptWithFlags(cs.Script, smartcontract.All)
|
v.LoadScriptWithHash(cs.Script, cs.Hash, smartcontract.All)
|
||||||
md := cs.Manifest.ABI.GetMethod("justReturn")
|
md := cs.Manifest.ABI.GetMethod("justReturn")
|
||||||
v.Jump(v.Context(), md.Offset)
|
v.Jump(v.Context(), md.Offset)
|
||||||
|
|
||||||
t.Run("Update", func(t *testing.T) {
|
t.Run("Update", func(t *testing.T) {
|
||||||
newCs := &state.Contract{
|
newCs := &state.Contract{
|
||||||
ID: cs.ID,
|
ID: cs.ID,
|
||||||
|
Hash: cs.Hash,
|
||||||
Script: append(cs.Script, byte(opcode.RET)),
|
Script: append(cs.Script, byte(opcode.RET)),
|
||||||
Manifest: cs.Manifest,
|
Manifest: cs.Manifest,
|
||||||
}
|
}
|
||||||
newCs.Manifest.ABI.Hash = hash.Hash160(newCs.Script)
|
|
||||||
putArgs(newCs)
|
putArgs(newCs)
|
||||||
require.NoError(t, contractUpdate(ic))
|
require.NoError(t, contractUpdate(ic))
|
||||||
require.NoError(t, v.Run())
|
require.NoError(t, v.Run())
|
||||||
|
|
||||||
v.LoadScriptWithFlags(currCs.Script, smartcontract.All)
|
v.LoadScriptWithHash(currCs.Script, cs.Hash, smartcontract.All)
|
||||||
err = contract.CallExInternal(ic, newCs, "getValue", nil, smartcontract.All, vm.EnsureNotEmpty, nil)
|
err = contract.CallExInternal(ic, newCs, "getValue", nil, smartcontract.All, vm.EnsureNotEmpty, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, v.Run())
|
require.NoError(t, v.Run())
|
||||||
|
@ -992,7 +972,7 @@ func TestMethodCallback(t *testing.T) {
|
||||||
require.NoError(t, ic.DAO.PutContractState(currCs))
|
require.NoError(t, ic.DAO.PutContractState(currCs))
|
||||||
|
|
||||||
ic.Functions = append(ic.Functions, systemInterops)
|
ic.Functions = append(ic.Functions, systemInterops)
|
||||||
rawHash := cs.Manifest.ABI.Hash.BytesBE()
|
rawHash := hash.Hash160(cs.Script).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) {
|
||||||
|
@ -1250,6 +1230,7 @@ func TestRuntimeCheckWitness(t *testing.T) {
|
||||||
contractScriptHash := hash.Hash160(contractScript)
|
contractScriptHash := hash.Hash160(contractScript)
|
||||||
contractState := &state.Contract{
|
contractState := &state.Contract{
|
||||||
ID: 15,
|
ID: 15,
|
||||||
|
Hash: contractScriptHash,
|
||||||
Script: contractScript,
|
Script: contractScript,
|
||||||
Manifest: manifest.Manifest{
|
Manifest: manifest.Manifest{
|
||||||
Groups: []manifest.Group{{PublicKey: pk.PublicKey()}},
|
Groups: []manifest.Group{{PublicKey: pk.PublicKey()}},
|
||||||
|
|
|
@ -20,6 +20,7 @@ func Deploy(ic *interop.Context) error {
|
||||||
|
|
||||||
cs := &state.Contract{
|
cs := &state.Contract{
|
||||||
ID: md.ContractID,
|
ID: md.ContractID,
|
||||||
|
Hash: md.Hash,
|
||||||
Script: md.Script,
|
Script: md.Script,
|
||||||
Manifest: md.Manifest,
|
Manifest: md.Manifest,
|
||||||
}
|
}
|
||||||
|
|
|
@ -149,6 +149,7 @@ func TestNativeContract_Invoke(t *testing.T) {
|
||||||
|
|
||||||
err := chain.dao.PutContractState(&state.Contract{
|
err := chain.dao.PutContractState(&state.Contract{
|
||||||
Script: tn.meta.Script,
|
Script: tn.meta.Script,
|
||||||
|
Hash: tn.meta.Hash,
|
||||||
Manifest: tn.meta.Manifest,
|
Manifest: tn.meta.Manifest,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -221,6 +222,7 @@ func TestNativeContract_InvokeOtherContract(t *testing.T) {
|
||||||
chain.registerNative(tn)
|
chain.registerNative(tn)
|
||||||
|
|
||||||
err := chain.dao.PutContractState(&state.Contract{
|
err := chain.dao.PutContractState(&state.Contract{
|
||||||
|
Hash: tn.meta.Hash,
|
||||||
Script: tn.meta.Script,
|
Script: tn.meta.Script,
|
||||||
Manifest: tn.meta.Manifest,
|
Manifest: tn.meta.Manifest,
|
||||||
})
|
})
|
||||||
|
@ -230,7 +232,7 @@ func TestNativeContract_InvokeOtherContract(t *testing.T) {
|
||||||
require.NoError(t, chain.dao.PutContractState(cs))
|
require.NoError(t, chain.dao.PutContractState(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.ScriptHash(), "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)
|
||||||
checkResult(t, res, stackitem.Null{}) // simple call is done with EnsureNotEmpty
|
checkResult(t, res, stackitem.Null{}) // simple call is done with EnsureNotEmpty
|
||||||
})
|
})
|
||||||
|
|
|
@ -313,7 +313,7 @@ func TestNEO_TransferOnPayment(t *testing.T) {
|
||||||
require.NoError(t, bc.dao.PutContractState(cs))
|
require.NoError(t, bc.dao.PutContractState(cs))
|
||||||
|
|
||||||
const amount = 2
|
const amount = 2
|
||||||
tx := transferTokenFromMultisigAccount(t, bc, cs.ScriptHash(), bc.contracts.NEO.Hash, amount)
|
tx := transferTokenFromMultisigAccount(t, bc, cs.Hash, bc.contracts.NEO.Hash, amount)
|
||||||
aer, err := bc.GetAppExecResults(tx.Hash(), trigger.Application)
|
aer, err := bc.GetAppExecResults(tx.Hash(), trigger.Application)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, vm.HaltState, aer[0].VMState)
|
require.Equal(t, vm.HaltState, aer[0].VMState)
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
"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/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"
|
||||||
|
@ -50,7 +51,7 @@ func getOracleContractState(h util.Uint160) *state.Contract {
|
||||||
emit.Syscall(w.BinWriter, interopnames.SystemStoragePut)
|
emit.Syscall(w.BinWriter, interopnames.SystemStoragePut)
|
||||||
emit.Opcodes(w.BinWriter, opcode.RET)
|
emit.Opcodes(w.BinWriter, opcode.RET)
|
||||||
|
|
||||||
m := manifest.NewManifest(h, "TestOracle")
|
m := manifest.NewManifest("TestOracle")
|
||||||
m.ABI.Methods = []manifest.Method{
|
m.ABI.Methods = []manifest.Method{
|
||||||
{
|
{
|
||||||
Name: "requestURL",
|
Name: "requestURL",
|
||||||
|
@ -81,8 +82,10 @@ func getOracleContractState(h util.Uint160) *state.Contract {
|
||||||
perm.Methods.Add("request")
|
perm.Methods.Add("request")
|
||||||
m.Permissions = append(m.Permissions, *perm)
|
m.Permissions = append(m.Permissions, *perm)
|
||||||
|
|
||||||
|
script := w.Bytes()
|
||||||
return &state.Contract{
|
return &state.Contract{
|
||||||
Script: w.Bytes(),
|
Script: script,
|
||||||
|
Hash: hash.Hash160(script),
|
||||||
Manifest: *m,
|
Manifest: *m,
|
||||||
ID: 42,
|
ID: 42,
|
||||||
}
|
}
|
||||||
|
@ -111,7 +114,7 @@ func TestOracle_Request(t *testing.T) {
|
||||||
gasForResponse := int64(2000_1234)
|
gasForResponse := int64(2000_1234)
|
||||||
var filter = "flt"
|
var filter = "flt"
|
||||||
userData := []byte("custom info")
|
userData := []byte("custom info")
|
||||||
txHash := putOracleRequest(t, cs.ScriptHash(), bc, "url", &filter, userData, gasForResponse)
|
txHash := putOracleRequest(t, cs.Hash, bc, "url", &filter, userData, gasForResponse)
|
||||||
|
|
||||||
req, err := orc.GetRequestInternal(bc.dao, 1)
|
req, err := orc.GetRequestInternal(bc.dao, 1)
|
||||||
require.NotNil(t, req)
|
require.NotNil(t, req)
|
||||||
|
@ -119,7 +122,7 @@ func TestOracle_Request(t *testing.T) {
|
||||||
require.Equal(t, txHash, req.OriginalTxID)
|
require.Equal(t, txHash, req.OriginalTxID)
|
||||||
require.Equal(t, "url", req.URL)
|
require.Equal(t, "url", req.URL)
|
||||||
require.Equal(t, filter, *req.Filter)
|
require.Equal(t, filter, *req.Filter)
|
||||||
require.Equal(t, cs.ScriptHash(), req.CallbackContract)
|
require.Equal(t, cs.Hash, req.CallbackContract)
|
||||||
require.Equal(t, "handle", req.CallbackMethod)
|
require.Equal(t, "handle", req.CallbackMethod)
|
||||||
require.Equal(t, uint64(gasForResponse), req.GasForResponse)
|
require.Equal(t, uint64(gasForResponse), req.GasForResponse)
|
||||||
|
|
||||||
|
@ -189,7 +192,7 @@ func TestOracle_Request(t *testing.T) {
|
||||||
t.Run("ErrorOnFinish", func(t *testing.T) {
|
t.Run("ErrorOnFinish", func(t *testing.T) {
|
||||||
const reqID = 2
|
const reqID = 2
|
||||||
|
|
||||||
putOracleRequest(t, cs.ScriptHash(), bc, "url", nil, []byte{1, 2}, gasForResponse)
|
putOracleRequest(t, cs.Hash, bc, "url", nil, []byte{1, 2}, gasForResponse)
|
||||||
_, err := orc.GetRequestInternal(bc.dao, reqID) // ensure ID is 2
|
_, err := orc.GetRequestInternal(bc.dao, reqID) // ensure ID is 2
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -221,14 +224,14 @@ func TestOracle_Request(t *testing.T) {
|
||||||
require.Equal(t, vm.FaultState, aer[0].VMState)
|
require.Equal(t, vm.FaultState, aer[0].VMState)
|
||||||
}
|
}
|
||||||
t.Run("non-UTF8 url", func(t *testing.T) {
|
t.Run("non-UTF8 url", func(t *testing.T) {
|
||||||
doBadRequest(t, cs.ScriptHash(), "\xff", nil, []byte{1, 2}, gasForResponse)
|
doBadRequest(t, cs.Hash, "\xff", nil, []byte{1, 2}, gasForResponse)
|
||||||
})
|
})
|
||||||
t.Run("non-UTF8 filter", func(t *testing.T) {
|
t.Run("non-UTF8 filter", func(t *testing.T) {
|
||||||
var f = "\xff"
|
var f = "\xff"
|
||||||
doBadRequest(t, cs.ScriptHash(), "url", &f, []byte{1, 2}, gasForResponse)
|
doBadRequest(t, cs.Hash, "url", &f, []byte{1, 2}, gasForResponse)
|
||||||
})
|
})
|
||||||
t.Run("not enough gas", func(t *testing.T) {
|
t.Run("not enough gas", func(t *testing.T) {
|
||||||
doBadRequest(t, cs.ScriptHash(), "url", nil, nil, 1000)
|
doBadRequest(t, cs.Hash, "url", nil, nil, 1000)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,80 +1,50 @@
|
||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"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/opcode"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Contract holds information about a smart contract in the NEO blockchain.
|
// Contract holds information about a smart contract in the NEO blockchain.
|
||||||
type Contract struct {
|
type Contract struct {
|
||||||
ID int32
|
ID int32 `json:"id"`
|
||||||
Script []byte
|
UpdateCounter uint16 `json:"updatecounter"`
|
||||||
Manifest manifest.Manifest
|
Hash util.Uint160 `json:"hash"`
|
||||||
|
Script []byte `json:"script"`
|
||||||
scriptHash util.Uint160
|
Manifest manifest.Manifest `json:"manifest"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeBinary implements Serializable interface.
|
// DecodeBinary implements Serializable interface.
|
||||||
func (cs *Contract) DecodeBinary(br *io.BinReader) {
|
func (cs *Contract) DecodeBinary(br *io.BinReader) {
|
||||||
cs.ID = int32(br.ReadU32LE())
|
cs.ID = int32(br.ReadU32LE())
|
||||||
|
cs.UpdateCounter = br.ReadU16LE()
|
||||||
|
cs.Hash.DecodeBinary(br)
|
||||||
cs.Script = br.ReadVarBytes()
|
cs.Script = br.ReadVarBytes()
|
||||||
cs.Manifest.DecodeBinary(br)
|
cs.Manifest.DecodeBinary(br)
|
||||||
cs.createHash()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncodeBinary implements Serializable interface.
|
// EncodeBinary implements Serializable interface.
|
||||||
func (cs *Contract) EncodeBinary(bw *io.BinWriter) {
|
func (cs *Contract) EncodeBinary(bw *io.BinWriter) {
|
||||||
bw.WriteU32LE(uint32(cs.ID))
|
bw.WriteU32LE(uint32(cs.ID))
|
||||||
|
bw.WriteU16LE(cs.UpdateCounter)
|
||||||
|
cs.Hash.EncodeBinary(bw)
|
||||||
bw.WriteVarBytes(cs.Script)
|
bw.WriteVarBytes(cs.Script)
|
||||||
cs.Manifest.EncodeBinary(bw)
|
cs.Manifest.EncodeBinary(bw)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ScriptHash returns a contract script hash.
|
// CreateContractHash creates deployed contract hash from transaction sender
|
||||||
func (cs *Contract) ScriptHash() util.Uint160 {
|
// and contract script.
|
||||||
if cs.scriptHash.Equals(util.Uint160{}) {
|
func CreateContractHash(sender util.Uint160, script []byte) util.Uint160 {
|
||||||
cs.createHash()
|
w := io.NewBufBinWriter()
|
||||||
|
emit.Opcodes(w.BinWriter, opcode.ABORT)
|
||||||
|
emit.Bytes(w.BinWriter, sender.BytesBE())
|
||||||
|
emit.Bytes(w.BinWriter, script)
|
||||||
|
if w.Err != nil {
|
||||||
|
panic(w.Err)
|
||||||
}
|
}
|
||||||
return cs.scriptHash
|
return hash.Hash160(w.Bytes())
|
||||||
}
|
|
||||||
|
|
||||||
// createHash creates contract script hash.
|
|
||||||
func (cs *Contract) createHash() {
|
|
||||||
cs.scriptHash = hash.Hash160(cs.Script)
|
|
||||||
}
|
|
||||||
|
|
||||||
type contractJSON struct {
|
|
||||||
ID int32 `json:"id"`
|
|
||||||
Script []byte `json:"script"`
|
|
||||||
Manifest *manifest.Manifest `json:"manifest"`
|
|
||||||
ScriptHash util.Uint160 `json:"hash"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalJSON implements json.Marshaler.
|
|
||||||
func (cs *Contract) MarshalJSON() ([]byte, error) {
|
|
||||||
return json.Marshal(&contractJSON{
|
|
||||||
ID: cs.ID,
|
|
||||||
Script: cs.Script,
|
|
||||||
Manifest: &cs.Manifest,
|
|
||||||
ScriptHash: cs.ScriptHash(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON implements json.Unmarshaler.
|
|
||||||
func (cs *Contract) UnmarshalJSON(data []byte) error {
|
|
||||||
var cj contractJSON
|
|
||||||
if err := json.Unmarshal(data, &cj); err != nil {
|
|
||||||
return err
|
|
||||||
} else if cj.Manifest == nil {
|
|
||||||
return errors.New("empty manifest")
|
|
||||||
}
|
|
||||||
cs.ID = cj.ID
|
|
||||||
cs.Script = cj.Script
|
|
||||||
cs.Manifest = *cj.Manifest
|
|
||||||
cs.createHash()
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,14 +7,15 @@ import (
|
||||||
"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"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestEncodeDecodeContractState(t *testing.T) {
|
func TestEncodeDecodeContractState(t *testing.T) {
|
||||||
script := []byte("testscript")
|
script := []byte("testscript")
|
||||||
|
|
||||||
h := hash.Hash160(script)
|
h := hash.Hash160(script)
|
||||||
m := manifest.NewManifest(h, "Test")
|
m := manifest.NewManifest("Test")
|
||||||
m.ABI.Methods = []manifest.Method{{
|
m.ABI.Methods = []manifest.Method{{
|
||||||
Name: "main",
|
Name: "main",
|
||||||
Parameters: []manifest.Parameter{
|
Parameters: []manifest.Parameter{
|
||||||
|
@ -30,21 +31,30 @@ func TestEncodeDecodeContractState(t *testing.T) {
|
||||||
ReturnType: smartcontract.BoolType,
|
ReturnType: smartcontract.BoolType,
|
||||||
}}
|
}}
|
||||||
contract := &Contract{
|
contract := &Contract{
|
||||||
ID: 123,
|
ID: 123,
|
||||||
Script: script,
|
UpdateCounter: 42,
|
||||||
Manifest: *m,
|
Hash: h,
|
||||||
|
Script: script,
|
||||||
|
Manifest: *m,
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(t, h, contract.ScriptHash())
|
|
||||||
|
|
||||||
t.Run("Serializable", func(t *testing.T) {
|
t.Run("Serializable", func(t *testing.T) {
|
||||||
contractDecoded := new(Contract)
|
contractDecoded := new(Contract)
|
||||||
testserdes.EncodeDecodeBinary(t, contract, contractDecoded)
|
testserdes.EncodeDecodeBinary(t, contract, contractDecoded)
|
||||||
assert.Equal(t, contract.ScriptHash(), contractDecoded.ScriptHash())
|
|
||||||
})
|
})
|
||||||
t.Run("JSON", func(t *testing.T) {
|
t.Run("JSON", func(t *testing.T) {
|
||||||
contractDecoded := new(Contract)
|
contractDecoded := new(Contract)
|
||||||
testserdes.MarshalUnmarshalJSON(t, contract, contractDecoded)
|
testserdes.MarshalUnmarshalJSON(t, contract, contractDecoded)
|
||||||
assert.Equal(t, contract.ScriptHash(), contractDecoded.ScriptHash())
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCreateContractHash(t *testing.T) {
|
||||||
|
var script = []byte{1, 2, 3}
|
||||||
|
var sender util.Uint160
|
||||||
|
var err error
|
||||||
|
|
||||||
|
require.Equal(t, "b4b7417195feca1cdb5a99504ab641d8c220ae99", CreateContractHash(sender, script).StringLE())
|
||||||
|
sender, err = util.Uint160DecodeStringLE("a400ff00ff00ff00ff00ff00ff00ff00ff00ff01")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, "e56e4ee87f89a70e9138432c387ad49f2ee5b55f", CreateContractHash(sender, script).StringLE())
|
||||||
|
}
|
||||||
|
|
|
@ -121,12 +121,12 @@ func (c *Client) Init() error {
|
||||||
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.ScriptHash()
|
c.cache.nativeHashes["neo"] = neoContractHash.Hash
|
||||||
gasContractHash, err := c.GetContractStateByAddressOrName("gas")
|
gasContractHash, err := c.GetContractStateByAddressOrName("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.ScriptHash()
|
c.cache.nativeHashes["gas"] = gasContractHash.Hash
|
||||||
c.initDone = true
|
c.initDone = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,8 +124,12 @@ func (c *Client) CreateNEP17MultiTransferTx(acc *wallet.Account, gas int64, reci
|
||||||
recipients[i].Address, recipients[i].Amount, nil)
|
recipients[i].Address, recipients[i].Amount, nil)
|
||||||
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
emit.Opcodes(w.BinWriter, opcode.ASSERT)
|
||||||
}
|
}
|
||||||
|
accAddr, err := address.StringToUint160(acc.Address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("bad account address: %v", err)
|
||||||
|
}
|
||||||
return c.CreateTxFromScript(w.Bytes(), acc, -1, gas, transaction.Signer{
|
return c.CreateTxFromScript(w.Bytes(), acc, -1, gas, transaction.Signer{
|
||||||
Account: acc.Contract.ScriptHash(),
|
Account: accAddr,
|
||||||
Scopes: transaction.CalledByEntry,
|
Scopes: transaction.CalledByEntry,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -622,6 +622,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.ScriptHash()
|
c.cache.nativeHashes[lowercasedName] = cs.Hash
|
||||||
return cs.ScriptHash(), nil
|
return cs.Hash, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -327,19 +327,19 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
||||||
}
|
}
|
||||||
return c.GetContractStateByHash(hash)
|
return c.GetContractStateByHash(hash)
|
||||||
},
|
},
|
||||||
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"id":0,"script":"VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==","manifest":{"name":"Test","abi":{"hash":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176","methods":[],"events":[]},"groups":[],"permissions":null,"trusts":[],"supportedstandards":[],"safemethods":[],"extra":null},"hash":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176"}}`,
|
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"id":0,"script":"VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==","manifest":{"name":"Test","abi":{"methods":[],"events":[]},"groups":[],"permissions":null,"trusts":[],"supportedstandards":[],"safemethods":[],"extra":null},"hash":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176"}}`,
|
||||||
result: func(c *Client) interface{} {
|
result: func(c *Client) interface{} {
|
||||||
script, err := base64.StdEncoding.DecodeString("VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==")
|
script, err := base64.StdEncoding.DecodeString("VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
m := manifest.NewManifest(hash.Hash160(script), "Test")
|
m := manifest.NewManifest("Test")
|
||||||
cs := &state.Contract{
|
cs := &state.Contract{
|
||||||
ID: 0,
|
ID: 0,
|
||||||
|
Hash: hash.Hash160(script),
|
||||||
Script: script,
|
Script: script,
|
||||||
Manifest: *m,
|
Manifest: *m,
|
||||||
}
|
}
|
||||||
_ = cs.ScriptHash()
|
|
||||||
return cs
|
return cs
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -348,19 +348,19 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
||||||
invoke: func(c *Client) (interface{}, error) {
|
invoke: func(c *Client) (interface{}, error) {
|
||||||
return c.GetContractStateByAddressOrName("NWiu5oejTu925aeL9Hc1LX8SvaJhE23h15")
|
return c.GetContractStateByAddressOrName("NWiu5oejTu925aeL9Hc1LX8SvaJhE23h15")
|
||||||
},
|
},
|
||||||
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"id":0,"script":"VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==","manifest":{"name":"Test","abi":{"hash":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176","methods":[],"events":[]},"groups":[],"permissions":null,"trusts":[],"supportedstandards":[],"safemethods":[],"extra":null},"hash":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176"}}`,
|
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"id":0,"script":"VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==","manifest":{"name":"Test","abi":{"methods":[],"events":[]},"groups":[],"permissions":null,"trusts":[],"supportedstandards":[],"safemethods":[],"extra":null},"hash":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176"}}`,
|
||||||
result: func(c *Client) interface{} {
|
result: func(c *Client) interface{} {
|
||||||
script, err := base64.StdEncoding.DecodeString("VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==")
|
script, err := base64.StdEncoding.DecodeString("VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
m := manifest.NewManifest(hash.Hash160(script), "Test")
|
m := manifest.NewManifest("Test")
|
||||||
cs := &state.Contract{
|
cs := &state.Contract{
|
||||||
ID: 0,
|
ID: 0,
|
||||||
|
Hash: hash.Hash160(script),
|
||||||
Script: script,
|
Script: script,
|
||||||
Manifest: *m,
|
Manifest: *m,
|
||||||
}
|
}
|
||||||
_ = cs.ScriptHash()
|
|
||||||
return cs
|
return cs
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -369,19 +369,19 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
||||||
invoke: func(c *Client) (interface{}, error) {
|
invoke: func(c *Client) (interface{}, error) {
|
||||||
return c.GetContractStateByID(0)
|
return c.GetContractStateByID(0)
|
||||||
},
|
},
|
||||||
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"id":0,"script":"VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==","manifest":{"name":"Test","abi":{"hash":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176","methods":[],"events":[]},"groups":[],"permissions":null,"trusts":[],"supportedstandards":[],"safemethods":[],"extra":null},"hash":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176"}}`,
|
serverResponse: `{"id":1,"jsonrpc":"2.0","result":{"id":0,"script":"VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==","manifest":{"name":"Test","abi":{"methods":[],"events":[]},"groups":[],"permissions":null,"trusts":[],"supportedstandards":[],"safemethods":[],"extra":null},"hash":"0x1b4357bff5a01bdf2a6581247cf9ed1e24629176"}}`,
|
||||||
result: func(c *Client) interface{} {
|
result: func(c *Client) interface{} {
|
||||||
script, err := base64.StdEncoding.DecodeString("VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==")
|
script, err := base64.StdEncoding.DecodeString("VgJXHwIMDWNvbnRyYWN0IGNhbGx4eVMTwEEFB5IWIXhKDANQdXSXJyQAAAAQVUGEGNYNIXJqeRDOeRHOU0FSoUH1IUURQCOPAgAASgwLdG90YWxTdXBwbHmXJxEAAABFAkBCDwBAI28CAABKDAhkZWNpbWFsc5cnDQAAAEUSQCNWAgAASgwEbmFtZZcnEgAAAEUMBFJ1YmxAIzwCAABKDAZzeW1ib2yXJxEAAABFDANSVUJAIyECAABKDAliYWxhbmNlT2aXJ2IAAAAQVUGEGNYNIXN5EM50bMoAFLQnIwAAAAwPaW52YWxpZCBhZGRyZXNzEVVBNtNSBiFFENsgQGtsUEEfLnsHIXUMCWJhbGFuY2VPZmxtUxPAQQUHkhYhRW1AI7IBAABKDAh0cmFuc2ZlcpcnKwEAABBVQYQY1g0hdnkQzncHbwfKABS0JyoAAAAMFmludmFsaWQgJ2Zyb20nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRHOdwhvCMoAFLQnKAAAAAwUaW52YWxpZCAndG8nIGFkZHJlc3MRVUE201IGIUUQ2yBAeRLOdwlvCRC1JyIAAAAMDmludmFsaWQgYW1vdW50EVVBNtNSBiFFENsgQG5vB1BBHy57ByF3Cm8Kbwm1JyYAAAAMEmluc3VmZmljaWVudCBmdW5kcxFVQTbTUgYhRRDbIEBvCm8Jn3cKbm8HbwpTQVKhQfUhbm8IUEEfLnsHIXcLbwtvCZ53C25vCG8LU0FSoUH1IQwIdHJhbnNmZXJvB28IbwlUFMBBBQeSFiFFEUAjewAAAEoMBGluaXSXJ1AAAAAQVUGEGNYNIXcMEFVBh8PSZCF3DQJAQg8Adw5vDG8Nbw5TQVKhQfUhDAh0cmFuc2ZlcgwA2zBvDW8OVBTAQQUHkhYhRRFAIyMAAAAMEWludmFsaWQgb3BlcmF0aW9uQTbTUgY6IwUAAABFQA==")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
m := manifest.NewManifest(hash.Hash160(script), "Test")
|
m := manifest.NewManifest("Test")
|
||||||
cs := &state.Contract{
|
cs := &state.Contract{
|
||||||
ID: 0,
|
ID: 0,
|
||||||
|
Hash: hash.Hash160(script),
|
||||||
Script: script,
|
Script: script,
|
||||||
Manifest: *m,
|
Manifest: *m,
|
||||||
}
|
}
|
||||||
_ = cs.ScriptHash()
|
|
||||||
return cs
|
return cs
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -11,6 +11,7 @@ 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/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"
|
||||||
|
@ -18,14 +19,18 @@ import (
|
||||||
|
|
||||||
// CreateDeploymentScript returns a script that deploys given smart contract
|
// CreateDeploymentScript returns a script that deploys given smart contract
|
||||||
// with its metadata.
|
// with its metadata.
|
||||||
func CreateDeploymentScript(avm []byte, manif *manifest.Manifest) ([]byte, error) {
|
func CreateDeploymentScript(ne *nef.File, manif *manifest.Manifest) ([]byte, error) {
|
||||||
script := io.NewBufBinWriter()
|
script := io.NewBufBinWriter()
|
||||||
rawManifest, err := json.Marshal(manif)
|
rawManifest, err := json.Marshal(manif)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
neb, err := ne.Bytes()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
emit.Bytes(script.BinWriter, rawManifest)
|
emit.Bytes(script.BinWriter, rawManifest)
|
||||||
emit.Bytes(script.BinWriter, avm)
|
emit.Bytes(script.BinWriter, neb)
|
||||||
emit.Syscall(script.BinWriter, interopnames.SystemContractCreate)
|
emit.Syscall(script.BinWriter, interopnames.SystemContractCreate)
|
||||||
return script.Bytes(), nil
|
return script.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,11 +56,11 @@ type rpcTestCase struct {
|
||||||
check func(t *testing.T, e *executor, result interface{})
|
check func(t *testing.T, e *executor, result interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
const testContractHash = "55b692ecc09f240355e042c6c07e8f3fe57546b1"
|
const testContractHash = "743ed26f78e29ecd595535b74a943b1f9ccbc444"
|
||||||
const deploymentTxHash = "99e40e5d169eb9a2b6faebc6fc596c050cf3f8a70ad25de8f44309bc8ccbfbfb"
|
const deploymentTxHash = "9ecf1273fe0d8868cc024c8270b569a12edd7ea9d675c88554b937134efb03f8"
|
||||||
const genesisBlockHash = "a496577895eb8c227bb866dc44f99f21c0cf06417ca8f2a877cc5d761a50dac0"
|
const genesisBlockHash = "a496577895eb8c227bb866dc44f99f21c0cf06417ca8f2a877cc5d761a50dac0"
|
||||||
|
|
||||||
const verifyContractHash = "c1213693b22cb0454a436d6e0bd76b8c0a3bfdf7"
|
const verifyContractHash = "a2eb22340979804cb10cc1add0b8822c201f4d8a"
|
||||||
const verifyContractAVM = "570300412d51083021700c14aa8acf859d4fe402b34e673f2156821796a488ebdb30716813cedb2869db289740"
|
const verifyContractAVM = "570300412d51083021700c14aa8acf859d4fe402b34e673f2156821796a488ebdb30716813cedb2869db289740"
|
||||||
const testVerifyContractAVM = "VwcADBQBDAMOBQYMDQIODw0DDgcJAAAAANswcGgRVUH4J+yMIaonBwAAABFADBQNDwMCCQACAQMHAwQFAgEADgYMCdswcWkRVUH4J+yMIaonBwAAABJAE0A="
|
const testVerifyContractAVM = "VwcADBQBDAMOBQYMDQIODw0DDgcJAAAAANswcGgRVUH4J+yMIaonBwAAABFADBQNDwMCCQACAQMHAwQFAgEADgYMCdswcWkRVUH4J+yMIaonBwAAABJAE0A="
|
||||||
|
|
||||||
|
@ -142,7 +142,7 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
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)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
assert.Equal(t, testContractHash, res.ScriptHash().StringLE())
|
assert.Equal(t, testContractHash, res.Hash.StringLE())
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1345,7 +1345,7 @@ func checkNep17Balances(t *testing.T, e *executor, acc interface{}) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Asset: e.chain.UtilityTokenHash(),
|
Asset: e.chain.UtilityTokenHash(),
|
||||||
Amount: "80009744770",
|
Amount: "80009698770",
|
||||||
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.
|
@ -2,6 +2,7 @@ package manifest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"math"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
@ -9,7 +10,7 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// MaxManifestSize is a max length for a valid contract manifest.
|
// MaxManifestSize is a max length for a valid contract manifest.
|
||||||
MaxManifestSize = 4096
|
MaxManifestSize = math.MaxUint16
|
||||||
|
|
||||||
// MethodInit is a name for default initialization method.
|
// MethodInit is a name for default initialization method.
|
||||||
MethodInit = "_initialize"
|
MethodInit = "_initialize"
|
||||||
|
@ -31,9 +32,8 @@ const (
|
||||||
|
|
||||||
// ABI represents a contract application binary interface.
|
// ABI represents a contract application binary interface.
|
||||||
type ABI struct {
|
type ABI struct {
|
||||||
Hash util.Uint160 `json:"hash"`
|
Methods []Method `json:"methods"`
|
||||||
Methods []Method `json:"methods"`
|
Events []Event `json:"events"`
|
||||||
Events []Event `json:"events"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manifest represens contract metadata.
|
// Manifest represens contract metadata.
|
||||||
|
@ -56,11 +56,10 @@ type Manifest struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewManifest returns new manifest with necessary fields initialized.
|
// NewManifest returns new manifest with necessary fields initialized.
|
||||||
func NewManifest(h util.Uint160, name string) *Manifest {
|
func NewManifest(name string) *Manifest {
|
||||||
m := &Manifest{
|
m := &Manifest{
|
||||||
Name: name,
|
Name: name,
|
||||||
ABI: ABI{
|
ABI: ABI{
|
||||||
Hash: h,
|
|
||||||
Methods: []Method{},
|
Methods: []Method{},
|
||||||
Events: []Event{},
|
Events: []Event{},
|
||||||
},
|
},
|
||||||
|
@ -73,8 +72,8 @@ func NewManifest(h util.Uint160, name string) *Manifest {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultManifest returns default contract manifest.
|
// DefaultManifest returns default contract manifest.
|
||||||
func DefaultManifest(h util.Uint160, name string) *Manifest {
|
func DefaultManifest(name string) *Manifest {
|
||||||
m := NewManifest(h, name)
|
m := NewManifest(name)
|
||||||
m.Permissions = []Permission{*NewPermission(PermissionWildcard)}
|
m.Permissions = []Permission{*NewPermission(PermissionWildcard)}
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
@ -100,26 +99,22 @@ func (a *ABI) GetEvent(name string) *Event {
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanCall returns true is current contract is allowed to call
|
// CanCall returns true is current contract is allowed to call
|
||||||
// method of another contract.
|
// method of another contract with specified hash.
|
||||||
func (m *Manifest) CanCall(toCall *Manifest, method string) bool {
|
func (m *Manifest) CanCall(hash util.Uint160, toCall *Manifest, method string) bool {
|
||||||
// this if is not present in the original code but should probably be here
|
// this if is not present in the original code but should probably be here
|
||||||
if toCall.SafeMethods.Contains(method) {
|
if toCall.SafeMethods.Contains(method) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
for i := range m.Permissions {
|
for i := range m.Permissions {
|
||||||
if m.Permissions[i].IsAllowed(toCall, method) {
|
if m.Permissions[i].IsAllowed(hash, toCall, method) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsValid checks whether the given hash is the one specified in manifest and
|
// IsValid checks whether the hash given is correct wrt manifest's groups.
|
||||||
// verifies it against all the keys in manifest groups.
|
|
||||||
func (m *Manifest) IsValid(hash util.Uint160) bool {
|
func (m *Manifest) IsValid(hash util.Uint160) bool {
|
||||||
if m.ABI.Hash != hash {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, g := range m.Groups {
|
for _, g := range m.Groups {
|
||||||
if !g.IsValid(hash) {
|
if !g.IsValid(hash) {
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -13,40 +13,40 @@ import (
|
||||||
// https://github.com/neo-project/neo/blob/master/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs#L10
|
// https://github.com/neo-project/neo/blob/master/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs#L10
|
||||||
func TestManifest_MarshalJSON(t *testing.T) {
|
func TestManifest_MarshalJSON(t *testing.T) {
|
||||||
t.Run("default", func(t *testing.T) {
|
t.Run("default", func(t *testing.T) {
|
||||||
s := `{"groups":[],"supportedstandards":[],"name":"Test","abi":{"hash":"0x0000000000000000000000000000000000000000","methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"safemethods":[],"extra":null}`
|
s := `{"groups":[],"supportedstandards":[],"name":"Test","abi":{"methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"safemethods":[],"extra":null}`
|
||||||
m := testUnmarshalMarshalManifest(t, s)
|
m := testUnmarshalMarshalManifest(t, s)
|
||||||
require.Equal(t, DefaultManifest(util.Uint160{}, "Test"), m)
|
require.Equal(t, DefaultManifest("Test"), m)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("permissions", func(t *testing.T) {
|
t.Run("permissions", func(t *testing.T) {
|
||||||
s := `{"groups":[],"supportedstandards":[],"name":"Test","abi":{"hash":"0x0000000000000000000000000000000000000000","methods":[],"events":[]},"permissions":[{"contract":"0x0000000000000000000000000000000000000000","methods":["method1","method2"]}],"trusts":[],"safemethods":[],"extra":null}`
|
s := `{"groups":[],"supportedstandards":[],"name":"Test","abi":{"methods":[],"events":[]},"permissions":[{"contract":"0x0000000000000000000000000000000000000000","methods":["method1","method2"]}],"trusts":[],"safemethods":[],"extra":null}`
|
||||||
testUnmarshalMarshalManifest(t, s)
|
testUnmarshalMarshalManifest(t, s)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("safe methods", func(t *testing.T) {
|
t.Run("safe methods", func(t *testing.T) {
|
||||||
s := `{"groups":[],"supportedstandards":[],"name":"Test","abi":{"hash":"0x0000000000000000000000000000000000000000","methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"safemethods":["balanceOf"],"extra":null}`
|
s := `{"groups":[],"supportedstandards":[],"name":"Test","abi":{"methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"safemethods":["balanceOf"],"extra":null}`
|
||||||
testUnmarshalMarshalManifest(t, s)
|
testUnmarshalMarshalManifest(t, s)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("trust", func(t *testing.T) {
|
t.Run("trust", func(t *testing.T) {
|
||||||
s := `{"groups":[],"supportedstandards":[],"name":"Test","abi":{"hash":"0x0000000000000000000000000000000000000000","methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":["0x0000000000000000000000000000000000000001"],"safemethods":[],"extra":null}`
|
s := `{"groups":[],"supportedstandards":[],"name":"Test","abi":{"methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":["0x0000000000000000000000000000000000000001"],"safemethods":[],"extra":null}`
|
||||||
testUnmarshalMarshalManifest(t, s)
|
testUnmarshalMarshalManifest(t, s)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("groups", func(t *testing.T) {
|
t.Run("groups", func(t *testing.T) {
|
||||||
s := `{"groups":[{"pubkey":"03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c","signature":"QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQQ=="}],"supportedstandards":[],"name":"Test","abi":{"hash":"0x0000000000000000000000000000000000000000","methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"safemethods":[],"extra":null}`
|
s := `{"groups":[{"pubkey":"03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c","signature":"QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQQ=="}],"supportedstandards":[],"name":"Test","abi":{"methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"safemethods":[],"extra":null}`
|
||||||
testUnmarshalMarshalManifest(t, s)
|
testUnmarshalMarshalManifest(t, s)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("extra", func(t *testing.T) {
|
t.Run("extra", func(t *testing.T) {
|
||||||
s := `{"groups":[],"supportedstandards":[],"name":"Test","abi":{"hash":"0x0000000000000000000000000000000000000000","methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"safemethods":[],"extra":{"key":"value"}}`
|
s := `{"groups":[],"supportedstandards":[],"name":"Test","abi":{"methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"safemethods":[],"extra":{"key":"value"}}`
|
||||||
testUnmarshalMarshalManifest(t, s)
|
testUnmarshalMarshalManifest(t, s)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func testUnmarshalMarshalManifest(t *testing.T, s string) *Manifest {
|
func testUnmarshalMarshalManifest(t *testing.T, s string) *Manifest {
|
||||||
js := []byte(s)
|
js := []byte(s)
|
||||||
c := NewManifest(util.Uint160{}, "Test")
|
c := NewManifest("Test")
|
||||||
require.NoError(t, json.Unmarshal(js, c))
|
require.NoError(t, json.Unmarshal(js, c))
|
||||||
|
|
||||||
data, err := json.Marshal(c)
|
data, err := json.Marshal(c)
|
||||||
|
@ -58,43 +58,43 @@ func testUnmarshalMarshalManifest(t *testing.T, s string) *Manifest {
|
||||||
|
|
||||||
func TestManifest_CanCall(t *testing.T) {
|
func TestManifest_CanCall(t *testing.T) {
|
||||||
t.Run("safe methods", func(t *testing.T) {
|
t.Run("safe methods", func(t *testing.T) {
|
||||||
man1 := NewManifest(util.Uint160{}, "Test1")
|
man1 := NewManifest("Test1")
|
||||||
man2 := DefaultManifest(util.Uint160{}, "Test2")
|
man2 := DefaultManifest("Test2")
|
||||||
require.False(t, man1.CanCall(man2, "method1"))
|
require.False(t, man1.CanCall(util.Uint160{}, man2, "method1"))
|
||||||
man2.SafeMethods.Add("method1")
|
man2.SafeMethods.Add("method1")
|
||||||
require.True(t, man1.CanCall(man2, "method1"))
|
require.True(t, man1.CanCall(util.Uint160{}, man2, "method1"))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("wildcard permission", func(t *testing.T) {
|
t.Run("wildcard permission", func(t *testing.T) {
|
||||||
man1 := DefaultManifest(util.Uint160{}, "Test1")
|
man1 := DefaultManifest("Test1")
|
||||||
man2 := DefaultManifest(util.Uint160{}, "Test2")
|
man2 := DefaultManifest("Test2")
|
||||||
require.True(t, man1.CanCall(man2, "method1"))
|
require.True(t, man1.CanCall(util.Uint160{}, man2, "method1"))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPermission_IsAllowed(t *testing.T) {
|
func TestPermission_IsAllowed(t *testing.T) {
|
||||||
manifest := DefaultManifest(util.Uint160{}, "Test")
|
manifest := DefaultManifest("Test")
|
||||||
|
|
||||||
t.Run("wildcard", func(t *testing.T) {
|
t.Run("wildcard", func(t *testing.T) {
|
||||||
perm := NewPermission(PermissionWildcard)
|
perm := NewPermission(PermissionWildcard)
|
||||||
require.True(t, perm.IsAllowed(manifest, "AAA"))
|
require.True(t, perm.IsAllowed(util.Uint160{}, manifest, "AAA"))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("hash", func(t *testing.T) {
|
t.Run("hash", func(t *testing.T) {
|
||||||
perm := NewPermission(PermissionHash, util.Uint160{})
|
perm := NewPermission(PermissionHash, util.Uint160{})
|
||||||
require.True(t, perm.IsAllowed(manifest, "AAA"))
|
require.True(t, perm.IsAllowed(util.Uint160{}, manifest, "AAA"))
|
||||||
|
|
||||||
t.Run("restrict methods", func(t *testing.T) {
|
t.Run("restrict methods", func(t *testing.T) {
|
||||||
perm.Methods.Restrict()
|
perm.Methods.Restrict()
|
||||||
require.False(t, perm.IsAllowed(manifest, "AAA"))
|
require.False(t, perm.IsAllowed(util.Uint160{}, manifest, "AAA"))
|
||||||
perm.Methods.Add("AAA")
|
perm.Methods.Add("AAA")
|
||||||
require.True(t, perm.IsAllowed(manifest, "AAA"))
|
require.True(t, perm.IsAllowed(util.Uint160{}, manifest, "AAA"))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("invalid hash", func(t *testing.T) {
|
t.Run("invalid hash", func(t *testing.T) {
|
||||||
perm := NewPermission(PermissionHash, util.Uint160{1})
|
perm := NewPermission(PermissionHash, util.Uint160{1})
|
||||||
require.False(t, perm.IsAllowed(manifest, "AAA"))
|
require.False(t, perm.IsAllowed(util.Uint160{}, manifest, "AAA"))
|
||||||
})
|
})
|
||||||
|
|
||||||
priv, err := keys.NewPrivateKey()
|
priv, err := keys.NewPrivateKey()
|
||||||
|
@ -103,29 +103,25 @@ func TestPermission_IsAllowed(t *testing.T) {
|
||||||
|
|
||||||
t.Run("group", func(t *testing.T) {
|
t.Run("group", func(t *testing.T) {
|
||||||
perm := NewPermission(PermissionGroup, priv.PublicKey())
|
perm := NewPermission(PermissionGroup, priv.PublicKey())
|
||||||
require.True(t, perm.IsAllowed(manifest, "AAA"))
|
require.True(t, perm.IsAllowed(util.Uint160{}, manifest, "AAA"))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("invalid group", func(t *testing.T) {
|
t.Run("invalid group", func(t *testing.T) {
|
||||||
priv2, err := keys.NewPrivateKey()
|
priv2, err := keys.NewPrivateKey()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
perm := NewPermission(PermissionGroup, priv2.PublicKey())
|
perm := NewPermission(PermissionGroup, priv2.PublicKey())
|
||||||
require.False(t, perm.IsAllowed(manifest, "AAA"))
|
require.False(t, perm.IsAllowed(util.Uint160{}, manifest, "AAA"))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIsValid(t *testing.T) {
|
func TestIsValid(t *testing.T) {
|
||||||
contractHash := util.Uint160{1, 2, 3}
|
contractHash := util.Uint160{1, 2, 3}
|
||||||
m := NewManifest(contractHash, "Test")
|
m := NewManifest("Test")
|
||||||
|
|
||||||
t.Run("valid, no groups", func(t *testing.T) {
|
t.Run("valid, no groups", func(t *testing.T) {
|
||||||
require.True(t, m.IsValid(contractHash))
|
require.True(t, m.IsValid(contractHash))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("invalid, no groups", func(t *testing.T) {
|
|
||||||
require.False(t, m.IsValid(util.Uint160{9, 8, 7}))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("with groups", func(t *testing.T) {
|
t.Run("with groups", func(t *testing.T) {
|
||||||
m.Groups = make([]Group, 3)
|
m.Groups = make([]Group, 3)
|
||||||
pks := make([]*keys.PrivateKey, 3)
|
pks := make([]*keys.PrivateKey, 3)
|
||||||
|
|
|
@ -84,12 +84,12 @@ func (d *PermissionDesc) Group() *keys.PublicKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsAllowed checks if method is allowed to be executed.
|
// IsAllowed checks if method is allowed to be executed.
|
||||||
func (p *Permission) IsAllowed(m *Manifest, method string) bool {
|
func (p *Permission) IsAllowed(hash util.Uint160, m *Manifest, method string) bool {
|
||||||
switch p.Contract.Type {
|
switch p.Contract.Type {
|
||||||
case PermissionWildcard:
|
case PermissionWildcard:
|
||||||
return true
|
return true
|
||||||
case PermissionHash:
|
case PermissionHash:
|
||||||
if !p.Contract.Hash().Equals(m.ABI.Hash) {
|
if !p.Contract.Hash().Equals(hash) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
case PermissionGroup:
|
case PermissionGroup:
|
||||||
|
|
|
@ -6,7 +6,6 @@ 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/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -106,7 +105,7 @@ func TestComplyValid(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCheck(t *testing.T) {
|
func TestCheck(t *testing.T) {
|
||||||
m := manifest.NewManifest(util.Uint160{}, "Test")
|
m := manifest.NewManifest("Test")
|
||||||
require.Error(t, Check(m, manifest.NEP17StandardName))
|
require.Error(t, Check(m, manifest.NEP17StandardName))
|
||||||
|
|
||||||
require.NoError(t, Check(nep17, manifest.NEP17StandardName))
|
require.NoError(t, Check(nep17, manifest.NEP17StandardName))
|
||||||
|
|
|
@ -4,14 +4,10 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
"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/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// NEO Executable Format 3 (NEF3)
|
// NEO Executable Format 3 (NEF3)
|
||||||
|
@ -22,127 +18,53 @@ import (
|
||||||
// +------------+-----------+------------------------------------------------------------+
|
// +------------+-----------+------------------------------------------------------------+
|
||||||
// | Magic | 4 bytes | Magic header |
|
// | Magic | 4 bytes | Magic header |
|
||||||
// | Compiler | 32 bytes | Compiler used |
|
// | Compiler | 32 bytes | Compiler used |
|
||||||
// | Version | 16 bytes | Compiler version (Major, Minor, Build, Version) |
|
// | Version | 32 bytes | Compiler version |
|
||||||
// | ScriptHash | 20 bytes | ScriptHash for the script (BE) |
|
|
||||||
// +------------+-----------+------------------------------------------------------------+
|
|
||||||
// | Checksum | 4 bytes | First four bytes of double SHA256 hash of the header |
|
|
||||||
// +------------+-----------+------------------------------------------------------------+
|
// +------------+-----------+------------------------------------------------------------+
|
||||||
// | Script | Var bytes | Var bytes for the payload |
|
// | Script | Var bytes | Var bytes for the payload |
|
||||||
// +------------+-----------+------------------------------------------------------------+
|
// +------------+-----------+------------------------------------------------------------+
|
||||||
|
// | Checksum | 4 bytes | First four bytes of double SHA256 hash of the header |
|
||||||
|
// +------------+-----------+------------------------------------------------------------+
|
||||||
|
|
||||||
const (
|
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 = 1024 * 1024
|
||||||
// compilerFieldSize is the length of `Compiler` File header field in bytes.
|
// compilerFieldSize is the length of `Compiler` and `Version` File header fields in bytes.
|
||||||
compilerFieldSize = 32
|
compilerFieldSize = 32
|
||||||
)
|
)
|
||||||
|
|
||||||
// File represents compiled contract file structure according to the NEF3 standard.
|
// File represents compiled contract file structure according to the NEF3 standard.
|
||||||
type File struct {
|
type File struct {
|
||||||
Header Header
|
Header Header
|
||||||
Checksum uint32
|
|
||||||
Script []byte
|
Script []byte
|
||||||
|
Checksum uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
// Header represents File header.
|
// Header represents File header.
|
||||||
type Header struct {
|
type Header struct {
|
||||||
Magic uint32
|
Magic uint32
|
||||||
Compiler string
|
Compiler string
|
||||||
Version Version
|
Version string
|
||||||
ScriptHash util.Uint160
|
|
||||||
}
|
|
||||||
|
|
||||||
// Version represents compiler version.
|
|
||||||
type Version struct {
|
|
||||||
Major int32
|
|
||||||
Minor int32
|
|
||||||
Build int32
|
|
||||||
Revision int32
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFile returns new NEF3 file with script specified.
|
// NewFile returns new NEF3 file with script specified.
|
||||||
func NewFile(script []byte) (File, error) {
|
func NewFile(script []byte) (*File, error) {
|
||||||
file := File{
|
file := &File{
|
||||||
Header: Header{
|
Header: Header{
|
||||||
Magic: Magic,
|
Magic: Magic,
|
||||||
Compiler: "neo-go",
|
Compiler: "neo-go",
|
||||||
ScriptHash: hash.Hash160(script),
|
Version: config.Version,
|
||||||
},
|
},
|
||||||
Script: script,
|
Script: script,
|
||||||
}
|
}
|
||||||
v, err := GetVersion(config.Version)
|
if len(config.Version) > compilerFieldSize {
|
||||||
if err != nil {
|
return nil, errors.New("too long version")
|
||||||
return file, err
|
|
||||||
}
|
}
|
||||||
file.Header.Version = v
|
file.Checksum = file.CalculateChecksum()
|
||||||
file.Checksum = file.Header.CalculateChecksum()
|
|
||||||
return file, nil
|
return file, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetVersion returns Version from the given string. It accepts the following formats:
|
|
||||||
// `major[-...].minor[-...].build[-...]` and `major[-...].minor[-...].build[-...].revision[-...]`
|
|
||||||
// where `major`, `minor`, `build` and `revision` are 32-bit integers with base=10
|
|
||||||
func GetVersion(version string) (Version, error) {
|
|
||||||
var (
|
|
||||||
result Version
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
versions := strings.SplitN(version, ".", 4)
|
|
||||||
if len(versions) < 3 {
|
|
||||||
return result, errors.New("invalid version format")
|
|
||||||
}
|
|
||||||
result.Major, err = parseDashedVersion(versions[0])
|
|
||||||
if err != nil {
|
|
||||||
return result, fmt.Errorf("failed to parse major version: %w", err)
|
|
||||||
}
|
|
||||||
result.Minor, err = parseDashedVersion(versions[1])
|
|
||||||
if err != nil {
|
|
||||||
return result, fmt.Errorf("failed to parse minor version: %w", err)
|
|
||||||
|
|
||||||
}
|
|
||||||
result.Build, err = parseDashedVersion(versions[2])
|
|
||||||
if err != nil {
|
|
||||||
return result, fmt.Errorf("failed to parse build version: %w", err)
|
|
||||||
}
|
|
||||||
if len(versions) == 4 {
|
|
||||||
result.Revision, err = parseDashedVersion(versions[3])
|
|
||||||
if err != nil {
|
|
||||||
return result, fmt.Errorf("failed to parse revision version: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseDashedVersion extracts int from string of the format `int[-...]` where `int` is
|
|
||||||
// a 32-bit integer with base=10.
|
|
||||||
func parseDashedVersion(version string) (int32, error) {
|
|
||||||
version = strings.SplitN(version, "-", 2)[0]
|
|
||||||
result, err := strconv.ParseInt(version, 10, 32)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return int32(result), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeBinary implements io.Serializable interface.
|
|
||||||
func (v *Version) EncodeBinary(w *io.BinWriter) {
|
|
||||||
w.WriteU32LE(uint32(v.Major))
|
|
||||||
w.WriteU32LE(uint32(v.Minor))
|
|
||||||
w.WriteU32LE(uint32(v.Build))
|
|
||||||
w.WriteU32LE(uint32(v.Revision))
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeBinary implements io.Serializable interface.
|
|
||||||
func (v *Version) DecodeBinary(r *io.BinReader) {
|
|
||||||
v.Major = int32(r.ReadU32LE())
|
|
||||||
v.Minor = int32(r.ReadU32LE())
|
|
||||||
v.Build = int32(r.ReadU32LE())
|
|
||||||
v.Revision = int32(r.ReadU32LE())
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeBinary implements io.Serializable interface.
|
// EncodeBinary implements io.Serializable interface.
|
||||||
func (h *Header) EncodeBinary(w *io.BinWriter) {
|
func (h *Header) EncodeBinary(w *io.BinWriter) {
|
||||||
w.WriteU32LE(h.Magic)
|
w.WriteU32LE(h.Magic)
|
||||||
|
@ -150,13 +72,14 @@ func (h *Header) EncodeBinary(w *io.BinWriter) {
|
||||||
w.Err = errors.New("invalid compiler name length")
|
w.Err = errors.New("invalid compiler name length")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
bytes := []byte(h.Compiler)
|
var b = make([]byte, compilerFieldSize)
|
||||||
w.WriteBytes(bytes)
|
copy(b, []byte(h.Compiler))
|
||||||
if len(bytes) < compilerFieldSize {
|
w.WriteBytes(b)
|
||||||
w.WriteBytes(make([]byte, compilerFieldSize-len(bytes)))
|
for i := range b {
|
||||||
|
b[i] = 0
|
||||||
}
|
}
|
||||||
h.Version.EncodeBinary(w)
|
copy(b, []byte(h.Version))
|
||||||
h.ScriptHash.EncodeBinary(w)
|
w.WriteBytes(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeBinary implements io.Serializable interface.
|
// DecodeBinary implements io.Serializable interface.
|
||||||
|
@ -172,39 +95,42 @@ func (h *Header) DecodeBinary(r *io.BinReader) {
|
||||||
return r == 0
|
return r == 0
|
||||||
})
|
})
|
||||||
h.Compiler = string(buf)
|
h.Compiler = string(buf)
|
||||||
h.Version.DecodeBinary(r)
|
buf = buf[:compilerFieldSize]
|
||||||
h.ScriptHash.DecodeBinary(r)
|
r.ReadBytes(buf)
|
||||||
|
buf = bytes.TrimRightFunc(buf, func(r rune) bool {
|
||||||
|
return r == 0
|
||||||
|
})
|
||||||
|
h.Version = string(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CalculateChecksum returns first 4 bytes of double-SHA256(Header) converted to uint32.
|
// CalculateChecksum returns first 4 bytes of double-SHA256(Header) converted to uint32.
|
||||||
func (h *Header) CalculateChecksum() uint32 {
|
func (n *File) CalculateChecksum() uint32 {
|
||||||
buf := io.NewBufBinWriter()
|
bb, err := n.Bytes()
|
||||||
h.EncodeBinary(buf.BinWriter)
|
if err != nil {
|
||||||
if buf.Err != nil {
|
panic(err)
|
||||||
panic(buf.Err)
|
|
||||||
}
|
}
|
||||||
return binary.LittleEndian.Uint32(hash.Checksum(buf.Bytes()))
|
return binary.LittleEndian.Uint32(hash.Checksum(bb[:len(bb)-4]))
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncodeBinary implements io.Serializable interface.
|
// EncodeBinary implements io.Serializable interface.
|
||||||
func (n *File) EncodeBinary(w *io.BinWriter) {
|
func (n *File) EncodeBinary(w *io.BinWriter) {
|
||||||
n.Header.EncodeBinary(w)
|
n.Header.EncodeBinary(w)
|
||||||
w.WriteU32LE(n.Checksum)
|
|
||||||
w.WriteVarBytes(n.Script)
|
w.WriteVarBytes(n.Script)
|
||||||
|
w.WriteU32LE(n.Checksum)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeBinary implements io.Serializable interface.
|
// DecodeBinary implements io.Serializable interface.
|
||||||
func (n *File) DecodeBinary(r *io.BinReader) {
|
func (n *File) DecodeBinary(r *io.BinReader) {
|
||||||
n.Header.DecodeBinary(r)
|
n.Header.DecodeBinary(r)
|
||||||
n.Checksum = r.ReadU32LE()
|
n.Script = r.ReadVarBytes(MaxScriptLength)
|
||||||
checksum := n.Header.CalculateChecksum()
|
if len(n.Script) == 0 {
|
||||||
if checksum != n.Checksum {
|
r.Err = errors.New("empty script")
|
||||||
r.Err = errors.New("CRC verification fail")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
n.Script = r.ReadVarBytes(MaxScriptLength)
|
n.Checksum = r.ReadU32LE()
|
||||||
if !hash.Hash160(n.Script).Equals(n.Header.ScriptHash) {
|
checksum := n.CalculateChecksum()
|
||||||
r.Err = errors.New("script hashes mismatch")
|
if checksum != n.Checksum {
|
||||||
|
r.Err = errors.New("checksum verification failure")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/internal/testserdes"
|
"github.com/nspcc-dev/neo-go/internal/testserdes"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -15,13 +13,7 @@ func TestEncodeDecodeBinary(t *testing.T) {
|
||||||
Header: Header{
|
Header: Header{
|
||||||
Magic: Magic,
|
Magic: Magic,
|
||||||
Compiler: "the best compiler ever",
|
Compiler: "the best compiler ever",
|
||||||
Version: Version{
|
Version: "1.2.3.4",
|
||||||
Major: 1,
|
|
||||||
Minor: 2,
|
|
||||||
Build: 3,
|
|
||||||
Revision: 4,
|
|
||||||
},
|
|
||||||
ScriptHash: hash.Hash160(script),
|
|
||||||
},
|
},
|
||||||
Script: script,
|
Script: script,
|
||||||
}
|
}
|
||||||
|
@ -37,25 +29,22 @@ func TestEncodeDecodeBinary(t *testing.T) {
|
||||||
checkDecodeError(t, expected)
|
checkDecodeError(t, expected)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("invalid script length", func(t *testing.T) {
|
t.Run("zero-length script", func(t *testing.T) {
|
||||||
newScript := make([]byte, MaxScriptLength+1)
|
expected.Script = make([]byte, 0)
|
||||||
expected.Script = newScript
|
expected.Checksum = expected.CalculateChecksum()
|
||||||
expected.Header.ScriptHash = hash.Hash160(newScript)
|
|
||||||
expected.Checksum = expected.Header.CalculateChecksum()
|
|
||||||
checkDecodeError(t, expected)
|
checkDecodeError(t, expected)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("invalid scripthash", func(t *testing.T) {
|
t.Run("invalid script length", func(t *testing.T) {
|
||||||
expected.Script = script
|
newScript := make([]byte, MaxScriptLength+1)
|
||||||
expected.Header.ScriptHash = util.Uint160{1, 2, 3}
|
expected.Script = newScript
|
||||||
expected.Checksum = expected.Header.CalculateChecksum()
|
expected.Checksum = expected.CalculateChecksum()
|
||||||
checkDecodeError(t, expected)
|
checkDecodeError(t, expected)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("positive", func(t *testing.T) {
|
t.Run("positive", func(t *testing.T) {
|
||||||
expected.Script = script
|
expected.Script = script
|
||||||
expected.Header.ScriptHash = hash.Hash160(script)
|
expected.Checksum = expected.CalculateChecksum()
|
||||||
expected.Checksum = expected.Header.CalculateChecksum()
|
|
||||||
expected.Header.Magic = Magic
|
expected.Header.Magic = Magic
|
||||||
testserdes.EncodeDecodeBinary(t, expected, &File{})
|
testserdes.EncodeDecodeBinary(t, expected, &File{})
|
||||||
})
|
})
|
||||||
|
@ -73,17 +62,11 @@ func TestBytesFromBytes(t *testing.T) {
|
||||||
Header: Header{
|
Header: Header{
|
||||||
Magic: Magic,
|
Magic: Magic,
|
||||||
Compiler: "the best compiler ever",
|
Compiler: "the best compiler ever",
|
||||||
Version: Version{
|
Version: "1.2.3.4",
|
||||||
Major: 1,
|
|
||||||
Minor: 2,
|
|
||||||
Build: 3,
|
|
||||||
Revision: 4,
|
|
||||||
},
|
|
||||||
ScriptHash: hash.Hash160(script),
|
|
||||||
},
|
},
|
||||||
Script: script,
|
Script: script,
|
||||||
}
|
}
|
||||||
expected.Checksum = expected.Header.CalculateChecksum()
|
expected.Checksum = expected.CalculateChecksum()
|
||||||
|
|
||||||
bytes, err := expected.Bytes()
|
bytes, err := expected.Bytes()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -91,79 +74,3 @@ func TestBytesFromBytes(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, expected, actual)
|
require.Equal(t, expected, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetVersion(t *testing.T) {
|
|
||||||
testCases := map[string]struct {
|
|
||||||
input string
|
|
||||||
fails bool
|
|
||||||
expected Version
|
|
||||||
}{
|
|
||||||
"major only": {
|
|
||||||
input: "1",
|
|
||||||
fails: true,
|
|
||||||
},
|
|
||||||
"major and minor only": {
|
|
||||||
input: "1.1",
|
|
||||||
fails: true,
|
|
||||||
},
|
|
||||||
"major, minor and revision only": {
|
|
||||||
input: "1.1.1",
|
|
||||||
expected: Version{
|
|
||||||
Major: 1,
|
|
||||||
Minor: 1,
|
|
||||||
Build: 1,
|
|
||||||
Revision: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"full version": {
|
|
||||||
input: "1.1.1.1",
|
|
||||||
expected: Version{
|
|
||||||
Major: 1,
|
|
||||||
Minor: 1,
|
|
||||||
Build: 1,
|
|
||||||
Revision: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"dashed, without revision": {
|
|
||||||
input: "1-pre.2-pre.3-pre",
|
|
||||||
expected: Version{
|
|
||||||
Major: 1,
|
|
||||||
Minor: 2,
|
|
||||||
Build: 3,
|
|
||||||
Revision: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"dashed, full version": {
|
|
||||||
input: "1-pre.2-pre.3-pre.4-pre",
|
|
||||||
expected: Version{
|
|
||||||
Major: 1,
|
|
||||||
Minor: 2,
|
|
||||||
Build: 3,
|
|
||||||
Revision: 4,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"dashed build": {
|
|
||||||
input: "1.2.3-pre.4",
|
|
||||||
expected: Version{
|
|
||||||
Major: 1,
|
|
||||||
Minor: 2,
|
|
||||||
Build: 3,
|
|
||||||
Revision: 4,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"extra versions": {
|
|
||||||
input: "1.2.3.4.5",
|
|
||||||
fails: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for name, test := range testCases {
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
actual, err := GetVersion(test.input)
|
|
||||||
if test.fails {
|
|
||||||
require.NotNil(t, err)
|
|
||||||
} else {
|
|
||||||
require.Equal(t, test.expected, actual)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -42,6 +42,9 @@ type Context struct {
|
||||||
// Caller's contract script hash.
|
// Caller's contract script hash.
|
||||||
callingScriptHash util.Uint160
|
callingScriptHash util.Uint160
|
||||||
|
|
||||||
|
// Set to true when running deployed contracts.
|
||||||
|
isDeployed bool
|
||||||
|
|
||||||
// Call flags this context was created with.
|
// Call flags this context was created with.
|
||||||
callFlag smartcontract.CallFlag
|
callFlag smartcontract.CallFlag
|
||||||
|
|
||||||
|
@ -256,6 +259,11 @@ func (c *Context) String() string {
|
||||||
return "execution context"
|
return "execution context"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsDeployed returns whether this context contains deployed contract.
|
||||||
|
func (c *Context) IsDeployed() bool {
|
||||||
|
return c.isDeployed
|
||||||
|
}
|
||||||
|
|
||||||
// getContextScriptHash returns script hash of the invocation stack element
|
// getContextScriptHash returns script hash of the invocation stack element
|
||||||
// number n.
|
// number n.
|
||||||
func (v *VM) getContextScriptHash(n int) util.Uint160 {
|
func (v *VM) getContextScriptHash(n int) util.Uint160 {
|
||||||
|
|
|
@ -284,12 +284,16 @@ func (v *VM) LoadScriptWithFlags(b []byte, f smartcontract.CallFlag) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadScriptWithHash if similar to the LoadScriptWithFlags method, but it also loads
|
// LoadScriptWithHash if similar to the LoadScriptWithFlags method, but it also loads
|
||||||
// given script hash directly into the Context to avoid its recalculations. It's
|
// given script hash directly into the Context to avoid its recalculations and to make
|
||||||
// up to user of this function to make sure the script and hash match each other.
|
// is possible to override it for deployed contracts with special hashes (the function
|
||||||
|
// assumes that it is used for deployed contracts setting context's parameters
|
||||||
|
// accordingly). It's up to user of this function to make sure the script and hash match
|
||||||
|
// each other.
|
||||||
func (v *VM) LoadScriptWithHash(b []byte, hash util.Uint160, f smartcontract.CallFlag) {
|
func (v *VM) LoadScriptWithHash(b []byte, hash util.Uint160, f smartcontract.CallFlag) {
|
||||||
shash := v.GetCurrentScriptHash()
|
shash := v.GetCurrentScriptHash()
|
||||||
v.LoadScriptWithFlags(b, f)
|
v.LoadScriptWithFlags(b, f)
|
||||||
ctx := v.Context()
|
ctx := v.Context()
|
||||||
|
ctx.isDeployed = true
|
||||||
ctx.scriptHash = hash
|
ctx.scriptHash = hash
|
||||||
ctx.callingScriptHash = shash
|
ctx.callingScriptHash = shash
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"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/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"
|
||||||
)
|
)
|
||||||
|
@ -181,8 +182,9 @@ func (w *Wallet) Close() {
|
||||||
|
|
||||||
// GetAccount returns account corresponding to the provided scripthash.
|
// GetAccount returns account corresponding to the provided scripthash.
|
||||||
func (w *Wallet) GetAccount(h util.Uint160) *Account {
|
func (w *Wallet) GetAccount(h util.Uint160) *Account {
|
||||||
|
addr := address.Uint160ToString(h)
|
||||||
for _, acc := range w.Accounts {
|
for _, acc := range w.Accounts {
|
||||||
if c := acc.Contract; c != nil && h.Equals(c.ScriptHash()) {
|
if acc.Address == addr {
|
||||||
return acc
|
return acc
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,6 +167,7 @@ func TestWallet_GetAccount(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, acc := range accounts {
|
for _, acc := range accounts {
|
||||||
|
acc.Address = address.Uint160ToString(acc.Contract.ScriptHash())
|
||||||
wallet.AddAccount(acc)
|
wallet.AddAccount(acc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
||||||
"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"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
|
@ -75,18 +74,13 @@ 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, avm, err := testchain.NewDeployTx("DumpContract", strings.NewReader(contract))
|
tx, contractHash, err := testchain.NewDeployTx("DumpContract", h, strings.NewReader(contract))
|
||||||
handleError("can't create deploy tx", err)
|
handleError("can't create deploy tx", err)
|
||||||
tx.Signers = []transaction.Signer{{
|
|
||||||
Account: h,
|
|
||||||
Scopes: transaction.CalledByEntry,
|
|
||||||
}}
|
|
||||||
tx.NetworkFee = 10_000_000
|
tx.NetworkFee = 10_000_000
|
||||||
tx.ValidUntilBlock = bc.BlockHeight() + 1
|
tx.ValidUntilBlock = bc.BlockHeight() + 1
|
||||||
handleError("can't sign deploy tx", acc.SignTx(tx))
|
handleError("can't sign deploy tx", acc.SignTx(tx))
|
||||||
lastBlock = addBlock(bc, lastBlock, valScript, tx)
|
lastBlock = addBlock(bc, lastBlock, valScript, tx)
|
||||||
|
|
||||||
contractHash := hash.Hash160(avm)
|
|
||||||
key := make([]byte, 10)
|
key := make([]byte, 10)
|
||||||
value := make([]byte, 10)
|
value := make([]byte, 10)
|
||||||
nonce := uint32(0)
|
nonce := uint32(0)
|
||||||
|
|
Loading…
Reference in a new issue