Merge pull request #1693 from nspcc-dev/fix/deploy

Provide additional data on deployment
This commit is contained in:
Roman Khimov 2021-01-29 17:23:29 +03:00 committed by GitHub
commit cb56eb4696
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 199 additions and 37 deletions

View file

@ -13,7 +13,7 @@ var key = "key"
const mgmtKey = "mgmt" const mgmtKey = "mgmt"
func _deploy(isUpdate bool) { func _deploy(data interface{}, isUpdate bool) {
var value string var value string
ctx := storage.GetContext() ctx := storage.GetContext()

View file

@ -4,7 +4,7 @@ import "github.com/nspcc-dev/neo-go/pkg/interop/storage"
var Key = "sub" var Key = "sub"
func _deploy(isUpdate bool) { func _deploy(data interface{}, isUpdate bool) {
ctx := storage.GetContext() ctx := storage.GetContext()
value := "sub create" value := "sub create"
if isUpdate { if isUpdate {

View file

@ -16,7 +16,7 @@ func init() {
trigger = runtime.GetTrigger() trigger = runtime.GetTrigger()
} }
func _deploy(isUpdate bool) { func _deploy(_ interface{}, isUpdate bool) {
if isUpdate { if isUpdate {
Log("_deploy method called before contract update") Log("_deploy method called before contract update")
return return

View file

@ -25,7 +25,7 @@ func init() {
ctx = storage.GetContext() ctx = storage.GetContext()
} }
func _deploy(isUpdate bool) { func _deploy(_ interface{}, isUpdate bool) {
if isUpdate { if isUpdate {
ticksLeft := storage.Get(ctx, ticksKey).(int) + 1 ticksLeft := storage.Get(ctx, ticksKey).(int) + 1
storage.Put(ctx, ticksKey, ticksLeft) storage.Put(ctx, ticksKey, ticksLeft)

View file

@ -336,11 +336,11 @@ func (c *codegen) convertInitFuncs(f *ast.File, pkg *types.Package, seenBefore b
func isDeployFunc(decl *ast.FuncDecl) bool { func isDeployFunc(decl *ast.FuncDecl) bool {
if decl.Name.Name != "_deploy" || decl.Recv != nil || if decl.Name.Name != "_deploy" || decl.Recv != nil ||
decl.Type.Params.NumFields() != 1 || decl.Type.Params.NumFields() != 2 ||
decl.Type.Results.NumFields() != 0 { decl.Type.Results.NumFields() != 0 {
return false return false
} }
typ, ok := decl.Type.Params.List[0].Type.(*ast.Ident) typ, ok := decl.Type.Params.List[1].Type.(*ast.Ident)
return ok && typ.Name == "bool" return ok && typ.Name == "bool"
} }
@ -1869,7 +1869,7 @@ func (c *codegen) compile(info *buildInfo, pkg *loader.PackageInfo) error {
hasDeploy := deployLocals > -1 hasDeploy := deployLocals > -1
if hasDeploy { if hasDeploy {
emit.Instruction(c.prog.BinWriter, opcode.INITSLOT, []byte{byte(deployLocals), 1}) emit.Instruction(c.prog.BinWriter, opcode.INITSLOT, []byte{byte(deployLocals), 2})
c.convertDeployFuncs() c.convertDeployFuncs()
c.deployEndOffset = c.prog.Len() c.deployEndOffset = c.prog.Len()
emit.Opcodes(c.prog.BinWriter, opcode.RET) emit.Opcodes(c.prog.BinWriter, opcode.RET)

View file

@ -150,11 +150,18 @@ func (c *codegen) emitDebugInfo(contract []byte) *DebugInfo {
Start: uint16(c.initEndOffset + 1), Start: uint16(c.initEndOffset + 1),
End: uint16(c.deployEndOffset), End: uint16(c.deployEndOffset),
}, },
Parameters: []DebugParam{{ Parameters: []DebugParam{
Name: "isUpdate", {
Type: "Boolean", Name: "data",
TypeSC: smartcontract.BoolType, Type: "Any",
}}, TypeSC: smartcontract.AnyType,
},
{
Name: "isUpdate",
Type: "Boolean",
TypeSC: smartcontract.BoolType,
},
},
ReturnType: "Void", ReturnType: "Void",
ReturnTypeSC: smartcontract.VoidType, ReturnTypeSC: smartcontract.VoidType,
SeqPoints: c.sequencePoints[manifest.MethodDeploy], SeqPoints: c.sequencePoints[manifest.MethodDeploy],

View file

@ -53,7 +53,7 @@ func MethodParams(addr interop.Hash160, h interop.Hash256,
type MyStruct struct {} type MyStruct struct {}
func (ms MyStruct) MethodOnStruct() { } func (ms MyStruct) MethodOnStruct() { }
func (ms *MyStruct) MethodOnPointerToStruct() { } func (ms *MyStruct) MethodOnPointerToStruct() { }
func _deploy(isUpdate bool) {} func _deploy(data interface{}, isUpdate bool) {}
` `
info, err := getBuildInfo("foo.go", src) info, err := getBuildInfo("foo.go", src)
@ -100,11 +100,18 @@ func _deploy(isUpdate bool) {}
t.Run("param types", func(t *testing.T) { t.Run("param types", func(t *testing.T) {
paramTypes := map[string][]DebugParam{ paramTypes := map[string][]DebugParam{
"_deploy": {{ "_deploy": {
Name: "isUpdate", {
Type: "Boolean", Name: "data",
TypeSC: smartcontract.BoolType, Type: "Any",
}}, TypeSC: smartcontract.AnyType,
},
{
Name: "isUpdate",
Type: "Boolean",
TypeSC: smartcontract.BoolType,
},
},
"MethodInt": {{ "MethodInt": {{
Name: "a", Name: "a",
Type: "ByteString", Type: "ByteString",
@ -160,6 +167,7 @@ func _deploy(isUpdate bool) {}
Name: "_deploy", Name: "_deploy",
Offset: 0, Offset: 0,
Parameters: []manifest.Parameter{ Parameters: []manifest.Parameter{
manifest.NewParameter("data", smartcontract.AnyType),
manifest.NewParameter("isUpdate", smartcontract.BoolType), manifest.NewParameter("isUpdate", smartcontract.BoolType),
}, },
ReturnType: smartcontract.VoidType, ReturnType: smartcontract.VoidType,

View file

@ -104,7 +104,13 @@ type ContractMD struct {
ContractID int32 ContractID int32
NEF nef.File NEF nef.File
Hash util.Uint160 Hash util.Uint160
Methods map[string]MethodAndPrice Methods map[MethodAndArgCount]MethodAndPrice
}
// MethodAndArgCount represents method's signature.
type MethodAndArgCount struct {
Name string
ArgCount int
} }
// NewContractMD returns Contract with the specified list of methods. // NewContractMD returns Contract with the specified list of methods.
@ -112,7 +118,7 @@ func NewContractMD(name string, id int32) *ContractMD {
c := &ContractMD{ c := &ContractMD{
Name: name, Name: name,
ContractID: id, ContractID: id,
Methods: make(map[string]MethodAndPrice), Methods: make(map[MethodAndArgCount]MethodAndPrice),
} }
// NEF is now stored in contract state and affects state dump. // NEF is now stored in contract state and affects state dump.
@ -129,10 +135,35 @@ func NewContractMD(name string, id int32) *ContractMD {
// AddMethod adds new method to a native contract. // AddMethod adds new method to a native contract.
func (c *ContractMD) AddMethod(md *MethodAndPrice, desc *manifest.Method) { func (c *ContractMD) AddMethod(md *MethodAndPrice, desc *manifest.Method) {
c.Manifest.ABI.Methods = append(c.Manifest.ABI.Methods, *desc)
md.MD = desc md.MD = desc
desc.Safe = md.RequiredFlags&(callflag.All^callflag.ReadOnly) == 0 desc.Safe = md.RequiredFlags&(callflag.All^callflag.ReadOnly) == 0
c.Methods[desc.Name] = *md
index := sort.Search(len(c.Manifest.ABI.Methods), func(i int) bool {
md := c.Manifest.ABI.Methods[i]
if md.Name != desc.Name {
return md.Name >= desc.Name
}
return len(md.Parameters) > len(desc.Parameters)
})
c.Manifest.ABI.Methods = append(c.Manifest.ABI.Methods, manifest.Method{})
copy(c.Manifest.ABI.Methods[index+1:], c.Manifest.ABI.Methods[index:])
c.Manifest.ABI.Methods[index] = *desc
key := MethodAndArgCount{
Name: desc.Name,
ArgCount: len(desc.Parameters),
}
c.Methods[key] = *md
}
// GetMethod returns method `name` with specified number of parameters.
func (c *ContractMD) GetMethod(name string, paramCount int) (MethodAndPrice, bool) {
key := MethodAndArgCount{
Name: name,
ArgCount: paramCount,
}
mp, ok := c.Methods[key]
return mp, ok
} }
// AddEvent adds new event to a native contract. // AddEvent adds new event to a native contract.

View file

@ -479,10 +479,16 @@ func getTestContractState(bc *Blockchain) (*state.Contract, *state.Contract) {
emit.Opcodes(w.BinWriter, opcode.LDSFLD0, opcode.SUB, emit.Opcodes(w.BinWriter, opcode.LDSFLD0, opcode.SUB,
opcode.CONVERT, opcode.Opcode(stackitem.BooleanT), opcode.RET) opcode.CONVERT, opcode.Opcode(stackitem.BooleanT), opcode.RET)
deployOff := w.Len() deployOff := w.Len()
emit.Opcodes(w.BinWriter, opcode.JMPIF, 2+8+3) emit.Opcodes(w.BinWriter, opcode.SWAP, opcode.JMPIF, 2+8+1+1+5+3)
emit.String(w.BinWriter, "create") emit.String(w.BinWriter, "create") // 8 bytes
emit.Opcodes(w.BinWriter, opcode.CALL, 3+8+3, opcode.RET) emit.Int(w.BinWriter, 2) // 1 byte
emit.String(w.BinWriter, "update") emit.Opcodes(w.BinWriter, opcode.PACK) // 1 byte
emit.Syscall(w.BinWriter, interopnames.SystemBinarySerialize) // 5 bytes
emit.Opcodes(w.BinWriter, opcode.CALL, 3+8+1+1+5+3, opcode.RET)
emit.String(w.BinWriter, "update") // 8 bytes
emit.Int(w.BinWriter, 2) // 1 byte
emit.Opcodes(w.BinWriter, opcode.PACK) // 1 byte
emit.Syscall(w.BinWriter, interopnames.SystemBinarySerialize) // 5 bytes
emit.Opcodes(w.BinWriter, opcode.CALL, 3, opcode.RET) emit.Opcodes(w.BinWriter, opcode.CALL, 3, opcode.RET)
putValOff := w.Len() putValOff := w.Len()
emit.String(w.BinWriter, "initial") emit.String(w.BinWriter, "initial")
@ -501,6 +507,9 @@ func getTestContractState(bc *Blockchain) (*state.Contract, *state.Contract) {
emit.String(w.BinWriter, "LastPayment") emit.String(w.BinWriter, "LastPayment")
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeNotify) emit.Syscall(w.BinWriter, interopnames.SystemRuntimeNotify)
emit.Opcodes(w.BinWriter, opcode.RET) emit.Opcodes(w.BinWriter, opcode.RET)
update3Off := w.Len()
emit.Int(w.BinWriter, 3)
emit.Opcodes(w.BinWriter, opcode.JMP, 2+1)
updateOff := w.Len() updateOff := w.Len()
emit.Int(w.BinWriter, 2) emit.Int(w.BinWriter, 2)
emit.Opcodes(w.BinWriter, opcode.PACK) emit.Opcodes(w.BinWriter, opcode.PACK)
@ -588,6 +597,7 @@ func getTestContractState(bc *Blockchain) (*state.Contract, *state.Contract) {
Name: manifest.MethodDeploy, Name: manifest.MethodDeploy,
Offset: deployOff, Offset: deployOff,
Parameters: []manifest.Parameter{ Parameters: []manifest.Parameter{
manifest.NewParameter("data", smartcontract.AnyType),
manifest.NewParameter("isUpdate", smartcontract.BoolType), manifest.NewParameter("isUpdate", smartcontract.BoolType),
}, },
ReturnType: smartcontract.VoidType, ReturnType: smartcontract.VoidType,
@ -624,6 +634,16 @@ func getTestContractState(bc *Blockchain) (*state.Contract, *state.Contract) {
}, },
ReturnType: smartcontract.VoidType, ReturnType: smartcontract.VoidType,
}, },
{
Name: "update",
Offset: update3Off,
Parameters: []manifest.Parameter{
manifest.NewParameter("nef", smartcontract.ByteArrayType),
manifest.NewParameter("manifest", smartcontract.ByteArrayType),
manifest.NewParameter("data", smartcontract.AnyType),
},
ReturnType: smartcontract.VoidType,
},
{ {
Name: "destroy", Name: "destroy",
Offset: destroyOff, Offset: destroyOff,

View file

@ -28,7 +28,7 @@ func Call(ic *interop.Context) error {
return errors.New("it is not allowed to use Neo.Native.Call directly to call native contracts. System.Contract.Call should be used") return errors.New("it is not allowed to use Neo.Native.Call directly to call native contracts. System.Contract.Call should be used")
} }
operation := ic.VM.Estack().Pop().String() operation := ic.VM.Estack().Pop().String()
m, ok := c.Metadata().Methods[operation] m, ok := c.Metadata().GetMethod(operation, ic.VM.Estack().Len())
if !ok { if !ok {
return fmt.Errorf("method %s not found", operation) return fmt.Errorf("method %s not found", operation)
} }

View file

@ -77,12 +77,26 @@ func newManagement() *Management {
md = newMethodAndPrice(m.deploy, 0, callflag.WriteStates|callflag.AllowNotify) md = newMethodAndPrice(m.deploy, 0, callflag.WriteStates|callflag.AllowNotify)
m.AddMethod(md, desc) m.AddMethod(md, desc)
desc = newDescriptor("deploy", smartcontract.ArrayType,
manifest.NewParameter("script", smartcontract.ByteArrayType),
manifest.NewParameter("manifest", smartcontract.ByteArrayType),
manifest.NewParameter("data", smartcontract.AnyType))
md = newMethodAndPrice(m.deployWithData, 0, callflag.WriteStates|callflag.AllowNotify)
m.AddMethod(md, desc)
desc = newDescriptor("update", smartcontract.VoidType, desc = newDescriptor("update", smartcontract.VoidType,
manifest.NewParameter("script", smartcontract.ByteArrayType), manifest.NewParameter("script", smartcontract.ByteArrayType),
manifest.NewParameter("manifest", smartcontract.ByteArrayType)) manifest.NewParameter("manifest", smartcontract.ByteArrayType))
md = newMethodAndPrice(m.update, 0, callflag.WriteStates|callflag.AllowNotify) md = newMethodAndPrice(m.update, 0, callflag.WriteStates|callflag.AllowNotify)
m.AddMethod(md, desc) m.AddMethod(md, desc)
desc = newDescriptor("update", smartcontract.VoidType,
manifest.NewParameter("script", smartcontract.ByteArrayType),
manifest.NewParameter("manifest", smartcontract.ByteArrayType),
manifest.NewParameter("data", smartcontract.AnyType))
md = newMethodAndPrice(m.updateWithData, 0, callflag.WriteStates|callflag.AllowNotify)
m.AddMethod(md, desc)
desc = newDescriptor("destroy", smartcontract.VoidType) desc = newDescriptor("destroy", smartcontract.VoidType)
md = newMethodAndPrice(m.destroy, 1000000, callflag.WriteStates|callflag.AllowNotify) md = newMethodAndPrice(m.destroy, 1000000, callflag.WriteStates|callflag.AllowNotify)
m.AddMethod(md, desc) m.AddMethod(md, desc)
@ -204,9 +218,14 @@ func (m *Management) getNefAndManifestFromItems(ic *interop.Context, args []stac
return resNef, resManifest, nil return resNef, resManifest, nil
} }
// deploy is an implementation of public deploy method, it's run under // deploy is an implementation of public 2-argument deploy method.
// VM protections, so it's OK for it to panic instead of returning errors.
func (m *Management) deploy(ic *interop.Context, args []stackitem.Item) stackitem.Item { func (m *Management) deploy(ic *interop.Context, args []stackitem.Item) stackitem.Item {
return m.deployWithData(ic, append(args, stackitem.Null{}))
}
// deployWithData is an implementation of public 3-argument deploy method.
// It's run under VM protections, so it's OK for it to panic instead of returning errors.
func (m *Management) deployWithData(ic *interop.Context, args []stackitem.Item) stackitem.Item {
neff, manif, err := m.getNefAndManifestFromItems(ic, args, true) neff, manif, err := m.getNefAndManifestFromItems(ic, args, true)
if err != nil { if err != nil {
panic(err) panic(err)
@ -224,7 +243,7 @@ func (m *Management) deploy(ic *interop.Context, args []stackitem.Item) stackite
if err != nil { if err != nil {
panic(err) panic(err)
} }
m.callDeploy(ic, newcontract, false) m.callDeploy(ic, newcontract, args[2], false)
m.emitNotification(ic, contractDeployNotificationName, newcontract.Hash) m.emitNotification(ic, contractDeployNotificationName, newcontract.Hash)
return contractToStack(newcontract) return contractToStack(newcontract)
} }
@ -266,9 +285,13 @@ func (m *Management) Deploy(d dao.DAO, sender util.Uint160, neff *nef.File, mani
return newcontract, nil return newcontract, nil
} }
func (m *Management) update(ic *interop.Context, args []stackitem.Item) stackitem.Item {
return m.updateWithData(ic, append(args, stackitem.Null{}))
}
// update is an implementation of public update method, it's run under // update is an implementation of public update method, it's run under
// VM protections, so it's OK for it to panic instead of returning errors. // VM protections, so it's OK for it to panic instead of returning errors.
func (m *Management) update(ic *interop.Context, args []stackitem.Item) stackitem.Item { func (m *Management) updateWithData(ic *interop.Context, args []stackitem.Item) stackitem.Item {
neff, manif, err := m.getNefAndManifestFromItems(ic, args, false) neff, manif, err := m.getNefAndManifestFromItems(ic, args, false)
if err != nil { if err != nil {
panic(err) panic(err)
@ -280,7 +303,7 @@ func (m *Management) update(ic *interop.Context, args []stackitem.Item) stackite
if err != nil { if err != nil {
panic(err) panic(err)
} }
m.callDeploy(ic, contract, true) m.callDeploy(ic, contract, args[2], true)
m.emitNotification(ic, contractUpdateNotificationName, contract.Hash) m.emitNotification(ic, contractUpdateNotificationName, contract.Hash)
return stackitem.Null{} return stackitem.Null{}
} }
@ -299,6 +322,9 @@ func (m *Management) Update(d dao.DAO, hash util.Uint160, neff *nef.File, manif
} }
// if manifest was provided, update the contract manifest // if manifest was provided, update the contract manifest
if manif != nil { if manif != nil {
if manif.Name != contract.Manifest.Name {
return nil, errors.New("contract name can't be changed")
}
if !manif.IsValid(contract.Hash) { if !manif.IsValid(contract.Hash) {
return nil, errors.New("invalid manifest for this contract") return nil, errors.New("invalid manifest for this contract")
} }
@ -378,11 +404,11 @@ func (m *Management) setMinimumDeploymentFee(ic *interop.Context, args []stackit
return stackitem.Null{} return stackitem.Null{}
} }
func (m *Management) callDeploy(ic *interop.Context, cs *state.Contract, isUpdate bool) { func (m *Management) callDeploy(ic *interop.Context, cs *state.Contract, data stackitem.Item, isUpdate bool) {
md := cs.Manifest.ABI.GetMethod(manifest.MethodDeploy, 1) md := cs.Manifest.ABI.GetMethod(manifest.MethodDeploy, 2)
if md != nil { if md != nil {
err := contract.CallFromNative(ic, m.Hash, cs, manifest.MethodDeploy, err := contract.CallFromNative(ic, m.Hash, cs, manifest.MethodDeploy,
[]stackitem.Item{stackitem.NewBool(isUpdate)}, false) []stackitem.Item{data, stackitem.NewBool(isUpdate)}, false)
if err != nil { if err != nil {
panic(err) panic(err)
} }

View file

@ -71,6 +71,57 @@ func TestStartFromHeight(t *testing.T) {
checkContractState(t, bc2, cs1.Hash, cs1) checkContractState(t, bc2, cs1.Hash, cs1)
} }
func TestContractDeployAndUpdateWithParameter(t *testing.T) {
bc := newTestChain(t)
defer bc.Close()
// nef.NewFile() cares about version a lot.
config.Version = "0.90.0-test"
mgmtHash := bc.ManagementContractHash()
cs1, _ := getTestContractState(bc)
cs1.Manifest.Permissions = []manifest.Permission{*manifest.NewPermission(manifest.PermissionWildcard)}
cs1.ID = 1
cs1.Hash = state.CreateContractHash(testchain.MultisigScriptHash(), cs1.NEF.Checksum, cs1.Manifest.Name)
manif1, err := json.Marshal(cs1.Manifest)
require.NoError(t, err)
nef1b, err := cs1.NEF.Bytes()
require.NoError(t, err)
aer, err := invokeContractMethod(bc, 11_00000000, mgmtHash, "deploy", nef1b, manif1, int64(42))
require.NoError(t, err)
require.Equal(t, vm.HaltState, aer.VMState)
t.Run("_deploy called", func(t *testing.T) {
res, err := invokeContractMethod(bc, 1_00000000, cs1.Hash, "getValue")
require.NoError(t, err)
require.Equal(t, 1, len(res.Stack))
item, err := stackitem.DeserializeItem(res.Stack[0].Value().([]byte))
require.NoError(t, err)
expected := []stackitem.Item{stackitem.Make("create"), stackitem.Make(42)}
require.Equal(t, stackitem.NewArray(expected), item)
})
cs1.NEF.Script = append(cs1.NEF.Script, byte(opcode.RET))
cs1.NEF.Checksum = cs1.NEF.CalculateChecksum()
nef1b, err = cs1.NEF.Bytes()
require.NoError(t, err)
cs1.UpdateCounter++
aer, err = invokeContractMethod(bc, 10_00000000, cs1.Hash, "update", nef1b, nil, "new data")
require.NoError(t, err)
require.Equal(t, vm.HaltState, aer.VMState)
t.Run("_deploy called", func(t *testing.T) {
res, err := invokeContractMethod(bc, 1_00000000, cs1.Hash, "getValue")
require.NoError(t, err)
require.Equal(t, 1, len(res.Stack))
item, err := stackitem.DeserializeItem(res.Stack[0].Value().([]byte))
require.NoError(t, err)
expected := []stackitem.Item{stackitem.Make("update"), stackitem.Make("new data")}
require.Equal(t, stackitem.NewArray(expected), item)
})
}
func TestContractDeploy(t *testing.T) { func TestContractDeploy(t *testing.T) {
bc := newTestChain(t) bc := newTestChain(t)
defer bc.Close() defer bc.Close()
@ -166,7 +217,10 @@ func TestContractDeploy(t *testing.T) {
res, err := invokeContractMethod(bc, 1_00000000, cs1.Hash, "getValue") res, err := invokeContractMethod(bc, 1_00000000, cs1.Hash, "getValue")
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(res.Stack)) require.Equal(t, 1, len(res.Stack))
require.Equal(t, []byte("create"), res.Stack[0].Value()) item, err := stackitem.DeserializeItem(res.Stack[0].Value().([]byte))
require.NoError(t, err)
expected := []stackitem.Item{stackitem.Make("create"), stackitem.Null{}}
require.Equal(t, stackitem.NewArray(expected), item)
}) })
t.Run("get after deploy", func(t *testing.T) { t.Run("get after deploy", func(t *testing.T) {
checkContractState(t, bc, cs1.Hash, cs1) checkContractState(t, bc, cs1.Hash, cs1)
@ -198,6 +252,7 @@ func TestContractDeploy(t *testing.T) {
Name: manifest.MethodDeploy, Name: manifest.MethodDeploy,
Offset: 0, Offset: 0,
Parameters: []manifest.Parameter{ Parameters: []manifest.Parameter{
manifest.NewParameter("data", smartcontract.AnyType),
manifest.NewParameter("isUpdate", smartcontract.BoolType), manifest.NewParameter("isUpdate", smartcontract.BoolType),
}, },
ReturnType: smartcontract.VoidType, ReturnType: smartcontract.VoidType,
@ -226,6 +281,7 @@ func TestContractDeploy(t *testing.T) {
Name: manifest.MethodDeploy, Name: manifest.MethodDeploy,
Offset: 0, Offset: 0,
Parameters: []manifest.Parameter{ Parameters: []manifest.Parameter{
manifest.NewParameter("data", smartcontract.AnyType),
manifest.NewParameter("isUpdate", smartcontract.BoolType), manifest.NewParameter("isUpdate", smartcontract.BoolType),
}, },
ReturnType: smartcontract.ArrayType, ReturnType: smartcontract.ArrayType,
@ -319,6 +375,17 @@ func TestContractUpdate(t *testing.T) {
checkFAULTState(t, res) checkFAULTState(t, res)
}) })
t.Run("change name", func(t *testing.T) {
var badManifest = cs1.Manifest
badManifest.Name += "tail"
manifB, err := json.Marshal(badManifest)
require.NoError(t, err)
res, err := invokeContractMethod(bc, 10_00000000, cs1.Hash, "update", nef1b, manifB)
require.NoError(t, err)
checkFAULTState(t, res)
})
cs1.NEF.Script = append(cs1.NEF.Script, byte(opcode.RET)) cs1.NEF.Script = append(cs1.NEF.Script, byte(opcode.RET))
cs1.NEF.Checksum = cs1.NEF.CalculateChecksum() cs1.NEF.Checksum = cs1.NEF.CalculateChecksum()
nef1b, err = cs1.NEF.Bytes() nef1b, err = cs1.NEF.Bytes()
@ -346,7 +413,10 @@ func TestContractUpdate(t *testing.T) {
res, err := invokeContractMethod(bc, 1_00000000, cs1.Hash, "getValue") res, err := invokeContractMethod(bc, 1_00000000, cs1.Hash, "getValue")
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 1, len(res.Stack)) require.Equal(t, 1, len(res.Stack))
require.Equal(t, []byte("update"), res.Stack[0].Value()) item, err := stackitem.DeserializeItem(res.Stack[0].Value().([]byte))
require.NoError(t, err)
expected := []stackitem.Item{stackitem.Make("update"), stackitem.Null{}}
require.Equal(t, stackitem.NewArray(expected), item)
}) })
t.Run("check contract", func(t *testing.T) { t.Run("check contract", func(t *testing.T) {
checkContractState(t, bc, cs1.Hash, cs1) checkContractState(t, bc, cs1.Hash, cs1)