Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide additional data on deployment #1693

Merged
merged 4 commits into from
Jan 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cli/testdata/deploy/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ var key = "key"

const mgmtKey = "mgmt"

func _deploy(isUpdate bool) {
func _deploy(data interface{}, isUpdate bool) {
AnnaShaleva marked this conversation as resolved.
Show resolved Hide resolved
var value string

ctx := storage.GetContext()
Expand Down
2 changes: 1 addition & 1 deletion cli/testdata/deploy/sub/put.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import "github.com/nspcc-dev/neo-go/pkg/interop/storage"

var Key = "sub"

func _deploy(isUpdate bool) {
func _deploy(data interface{}, isUpdate bool) {
ctx := storage.GetContext()
value := "sub create"
if isUpdate {
Expand Down
2 changes: 1 addition & 1 deletion examples/runtime/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func init() {
trigger = runtime.GetTrigger()
}

func _deploy(isUpdate bool) {
func _deploy(_ interface{}, isUpdate bool) {
if isUpdate {
Log("_deploy method called before contract update")
return
Expand Down
2 changes: 1 addition & 1 deletion examples/timer/timer.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func init() {
ctx = storage.GetContext()
}

func _deploy(isUpdate bool) {
func _deploy(_ interface{}, isUpdate bool) {
if isUpdate {
ticksLeft := storage.Get(ctx, ticksKey).(int) + 1
storage.Put(ctx, ticksKey, ticksLeft)
Expand Down
6 changes: 3 additions & 3 deletions pkg/compiler/codegen.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,11 +336,11 @@ func (c *codegen) convertInitFuncs(f *ast.File, pkg *types.Package, seenBefore b

func isDeployFunc(decl *ast.FuncDecl) bool {
if decl.Name.Name != "_deploy" || decl.Recv != nil ||
decl.Type.Params.NumFields() != 1 ||
decl.Type.Params.NumFields() != 2 ||
decl.Type.Results.NumFields() != 0 {
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"
}

Expand Down Expand Up @@ -1869,7 +1869,7 @@ func (c *codegen) compile(info *buildInfo, pkg *loader.PackageInfo) error {

hasDeploy := deployLocals > -1
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.deployEndOffset = c.prog.Len()
emit.Opcodes(c.prog.BinWriter, opcode.RET)
Expand Down
17 changes: 12 additions & 5 deletions pkg/compiler/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,11 +150,18 @@ func (c *codegen) emitDebugInfo(contract []byte) *DebugInfo {
Start: uint16(c.initEndOffset + 1),
End: uint16(c.deployEndOffset),
},
Parameters: []DebugParam{{
Name: "isUpdate",
Type: "Boolean",
TypeSC: smartcontract.BoolType,
}},
Parameters: []DebugParam{
{
Name: "data",
Type: "Any",
TypeSC: smartcontract.AnyType,
},
{
Name: "isUpdate",
Type: "Boolean",
TypeSC: smartcontract.BoolType,
},
},
ReturnType: "Void",
ReturnTypeSC: smartcontract.VoidType,
SeqPoints: c.sequencePoints[manifest.MethodDeploy],
Expand Down
20 changes: 14 additions & 6 deletions pkg/compiler/debug_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func MethodParams(addr interop.Hash160, h interop.Hash256,
type MyStruct struct {}
func (ms MyStruct) MethodOnStruct() { }
func (ms *MyStruct) MethodOnPointerToStruct() { }
func _deploy(isUpdate bool) {}
func _deploy(data interface{}, isUpdate bool) {}
`

info, err := getBuildInfo("foo.go", src)
Expand Down Expand Up @@ -100,11 +100,18 @@ func _deploy(isUpdate bool) {}

t.Run("param types", func(t *testing.T) {
paramTypes := map[string][]DebugParam{
"_deploy": {{
Name: "isUpdate",
Type: "Boolean",
TypeSC: smartcontract.BoolType,
}},
"_deploy": {
{
Name: "data",
Type: "Any",
TypeSC: smartcontract.AnyType,
},
{
Name: "isUpdate",
Type: "Boolean",
TypeSC: smartcontract.BoolType,
},
},
"MethodInt": {{
Name: "a",
Type: "ByteString",
Expand Down Expand Up @@ -160,6 +167,7 @@ func _deploy(isUpdate bool) {}
Name: "_deploy",
Offset: 0,
Parameters: []manifest.Parameter{
manifest.NewParameter("data", smartcontract.AnyType),
manifest.NewParameter("isUpdate", smartcontract.BoolType),
},
ReturnType: smartcontract.VoidType,
Expand Down
39 changes: 35 additions & 4 deletions pkg/core/interop/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,15 +104,21 @@ type ContractMD struct {
ContractID int32
NEF nef.File
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.
func NewContractMD(name string, id int32) *ContractMD {
c := &ContractMD{
Name: name,
ContractID: id,
Methods: make(map[string]MethodAndPrice),
Methods: make(map[MethodAndArgCount]MethodAndPrice),
}

// NEF is now stored in contract state and affects state dump.
Expand All @@ -129,10 +135,35 @@ func NewContractMD(name string, id int32) *ContractMD {

// AddMethod adds new method to a native contract.
func (c *ContractMD) AddMethod(md *MethodAndPrice, desc *manifest.Method) {
c.Manifest.ABI.Methods = append(c.Manifest.ABI.Methods, *desc)
md.MD = desc
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
AnnaShaleva marked this conversation as resolved.
Show resolved Hide resolved

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.
Expand Down
28 changes: 24 additions & 4 deletions pkg/core/interop_system_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -479,10 +479,16 @@ func getTestContractState(bc *Blockchain) (*state.Contract, *state.Contract) {
emit.Opcodes(w.BinWriter, opcode.LDSFLD0, opcode.SUB,
opcode.CONVERT, opcode.Opcode(stackitem.BooleanT), opcode.RET)
deployOff := w.Len()
emit.Opcodes(w.BinWriter, opcode.JMPIF, 2+8+3)
emit.String(w.BinWriter, "create")
emit.Opcodes(w.BinWriter, opcode.CALL, 3+8+3, opcode.RET)
emit.String(w.BinWriter, "update")
emit.Opcodes(w.BinWriter, opcode.SWAP, opcode.JMPIF, 2+8+1+1+5+3)
emit.String(w.BinWriter, "create") // 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+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)
putValOff := w.Len()
emit.String(w.BinWriter, "initial")
Expand All @@ -501,6 +507,9 @@ func getTestContractState(bc *Blockchain) (*state.Contract, *state.Contract) {
emit.String(w.BinWriter, "LastPayment")
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeNotify)
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()
emit.Int(w.BinWriter, 2)
emit.Opcodes(w.BinWriter, opcode.PACK)
Expand Down Expand Up @@ -588,6 +597,7 @@ func getTestContractState(bc *Blockchain) (*state.Contract, *state.Contract) {
Name: manifest.MethodDeploy,
Offset: deployOff,
Parameters: []manifest.Parameter{
manifest.NewParameter("data", smartcontract.AnyType),
manifest.NewParameter("isUpdate", smartcontract.BoolType),
},
ReturnType: smartcontract.VoidType,
Expand Down Expand Up @@ -624,6 +634,16 @@ func getTestContractState(bc *Blockchain) (*state.Contract, *state.Contract) {
},
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",
Offset: destroyOff,
Expand Down
2 changes: 1 addition & 1 deletion pkg/core/native/interop.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
operation := ic.VM.Estack().Pop().String()
m, ok := c.Metadata().Methods[operation]
m, ok := c.Metadata().GetMethod(operation, ic.VM.Estack().Len())
if !ok {
return fmt.Errorf("method %s not found", operation)
}
Expand Down
42 changes: 34 additions & 8 deletions pkg/core/native/management.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,26 @@ func newManagement() *Management {
md = newMethodAndPrice(m.deploy, 0, callflag.WriteStates|callflag.AllowNotify)
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,
manifest.NewParameter("script", smartcontract.ByteArrayType),
manifest.NewParameter("manifest", smartcontract.ByteArrayType))
md = newMethodAndPrice(m.update, 0, callflag.WriteStates|callflag.AllowNotify)
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)
md = newMethodAndPrice(m.destroy, 1000000, callflag.WriteStates|callflag.AllowNotify)
m.AddMethod(md, desc)
Expand Down Expand Up @@ -204,9 +218,14 @@ func (m *Management) getNefAndManifestFromItems(ic *interop.Context, args []stac
return resNef, resManifest, nil
}

// deploy is an implementation of public deploy method, it's run under
// VM protections, so it's OK for it to panic instead of returning errors.
// deploy is an implementation of public 2-argument deploy method.
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)
if err != nil {
panic(err)
Expand All @@ -224,7 +243,7 @@ func (m *Management) deploy(ic *interop.Context, args []stackitem.Item) stackite
if err != nil {
panic(err)
}
m.callDeploy(ic, newcontract, false)
m.callDeploy(ic, newcontract, args[2], false)
m.emitNotification(ic, contractDeployNotificationName, newcontract.Hash)
return contractToStack(newcontract)
}
Expand Down Expand Up @@ -266,9 +285,13 @@ func (m *Management) Deploy(d dao.DAO, sender util.Uint160, neff *nef.File, mani
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
// 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)
if err != nil {
panic(err)
Expand All @@ -280,7 +303,7 @@ func (m *Management) update(ic *interop.Context, args []stackitem.Item) stackite
if err != nil {
panic(err)
}
m.callDeploy(ic, contract, true)
m.callDeploy(ic, contract, args[2], true)
m.emitNotification(ic, contractUpdateNotificationName, contract.Hash)
return stackitem.Null{}
}
Expand All @@ -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 manif != nil {
if manif.Name != contract.Manifest.Name {
return nil, errors.New("contract name can't be changed")
}
if !manif.IsValid(contract.Hash) {
return nil, errors.New("invalid manifest for this contract")
}
Expand Down Expand Up @@ -378,11 +404,11 @@ func (m *Management) setMinimumDeploymentFee(ic *interop.Context, args []stackit
return stackitem.Null{}
}

func (m *Management) callDeploy(ic *interop.Context, cs *state.Contract, isUpdate bool) {
md := cs.Manifest.ABI.GetMethod(manifest.MethodDeploy, 1)
func (m *Management) callDeploy(ic *interop.Context, cs *state.Contract, data stackitem.Item, isUpdate bool) {
md := cs.Manifest.ABI.GetMethod(manifest.MethodDeploy, 2)
if md != nil {
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 {
panic(err)
}
Expand Down
Loading