From 130608ac67f95dfc07fb1d7f373ab44fd67630f6 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Tue, 8 Nov 2022 16:36:38 +0300 Subject: [PATCH] rpcbinding: support writer-only wrappers "verify" contract doesn't have any safe methods. --- cli/smartcontract/generate_test.go | 3 + cli/smartcontract/testdata/gas/gas.go | 3 +- cli/smartcontract/testdata/nameservice/nns.go | 3 +- cli/smartcontract/testdata/nex/nex.go | 4 +- .../testdata/verifyrpc/verify.go | 93 +++++++++++++++++++ pkg/smartcontract/rpcbinding/binding.go | 33 +++++-- 6 files changed, 127 insertions(+), 12 deletions(-) create mode 100644 cli/smartcontract/testdata/verifyrpc/verify.go diff --git a/cli/smartcontract/generate_test.go b/cli/smartcontract/generate_test.go index e3e649a48..df595f83f 100644 --- a/cli/smartcontract/generate_test.go +++ b/cli/smartcontract/generate_test.go @@ -357,6 +357,9 @@ func TestGenerateRPCBindings(t *testing.T) { checkBinding(filepath.Join("testdata", "gas", "gas.manifest.json"), "0xd2a4cff31913016155e38e474a2c06d08be276cf", filepath.Join("testdata", "gas", "gas.go")) + checkBinding(filepath.Join("testdata", "verify.manifest.json"), + "0x00112233445566778899aabbccddeeff00112233", + filepath.Join("testdata", "verifyrpc", "verify.go")) } func TestGenerate_Errors(t *testing.T) { diff --git a/cli/smartcontract/testdata/gas/gas.go b/cli/smartcontract/testdata/gas/gas.go index e81155f40..d3c938c29 100644 --- a/cli/smartcontract/testdata/gas/gas.go +++ b/cli/smartcontract/testdata/gas/gas.go @@ -17,6 +17,7 @@ type Invoker interface { // Actor is used by Contract to call state-changing methods. type Actor interface { Invoker + nep17.Actor } @@ -41,6 +42,6 @@ func NewReader(invoker Invoker) *ContractReader { // New creates an instance of Contract using Hash and the given Actor. func New(actor Actor) *Contract { var nep17t = nep17.New(actor, Hash) - return &Contract{ContractReader{nep17t.TokenReader, actor},nep17t.TokenWriter, actor} + return &Contract{ContractReader{nep17t.TokenReader, actor}, nep17t.TokenWriter, actor} } diff --git a/cli/smartcontract/testdata/nameservice/nns.go b/cli/smartcontract/testdata/nameservice/nns.go index 2e72c192c..5c849b8e7 100644 --- a/cli/smartcontract/testdata/nameservice/nns.go +++ b/cli/smartcontract/testdata/nameservice/nns.go @@ -22,6 +22,7 @@ type Invoker interface { // Actor is used by Contract to call state-changing methods. type Actor interface { Invoker + nep11.Actor MakeCall(contract util.Uint160, method string, params ...interface{}) (*transaction.Transaction, error) @@ -53,7 +54,7 @@ func NewReader(invoker Invoker) *ContractReader { // New creates an instance of Contract using Hash and the given Actor. func New(actor Actor) *Contract { var nep11ndt = nep11.NewNonDivisible(actor, Hash) - return &Contract{ContractReader{nep11ndt.NonDivisibleReader, actor},nep11ndt.BaseWriter, actor} + return &Contract{ContractReader{nep11ndt.NonDivisibleReader, actor}, nep11ndt.BaseWriter, actor} } diff --git a/cli/smartcontract/testdata/nex/nex.go b/cli/smartcontract/testdata/nex/nex.go index 8c67fe731..2ad6941db 100644 --- a/cli/smartcontract/testdata/nex/nex.go +++ b/cli/smartcontract/testdata/nex/nex.go @@ -21,7 +21,9 @@ type Invoker interface { // Actor is used by Contract to call state-changing methods. type Actor interface { Invoker + nep17.Actor + MakeCall(contract util.Uint160, method string, params ...interface{}) (*transaction.Transaction, error) MakeRun(script []byte) (*transaction.Transaction, error) MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...interface{}) (*transaction.Transaction, error) @@ -51,7 +53,7 @@ func NewReader(invoker Invoker) *ContractReader { // New creates an instance of Contract using Hash and the given Actor. func New(actor Actor) *Contract { var nep17t = nep17.New(actor, Hash) - return &Contract{ContractReader{nep17t.TokenReader, actor},nep17t.TokenWriter, actor} + return &Contract{ContractReader{nep17t.TokenReader, actor}, nep17t.TokenWriter, actor} } diff --git a/cli/smartcontract/testdata/verifyrpc/verify.go b/cli/smartcontract/testdata/verifyrpc/verify.go new file mode 100644 index 000000000..0725c3836 --- /dev/null +++ b/cli/smartcontract/testdata/verifyrpc/verify.go @@ -0,0 +1,93 @@ +// Package verify contains RPC wrappers for verify contract. +package verify + +import ( + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/util" + "math/big" +) + +// Hash contains contract hash. +var Hash = util.Uint160{0x33, 0x22, 0x11, 0x0, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x0} + +// Actor is used by Contract to call state-changing methods. +type Actor interface { + MakeCall(contract util.Uint160, method string, params ...interface{}) (*transaction.Transaction, error) + MakeRun(script []byte) (*transaction.Transaction, error) + MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...interface{}) (*transaction.Transaction, error) + MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error) + SendCall(contract util.Uint160, method string, params ...interface{}) (util.Uint256, uint32, error) + SendRun(script []byte) (util.Uint256, uint32, error) +} + +// Contract implements all contract methods. +type Contract struct { + actor Actor +} + +// New creates an instance of Contract using Hash and the given Actor. +func New(actor Actor) *Contract { + return &Contract{actor} +} + + +func scriptForVerify() ([]byte, error) { + return smartcontract.CreateCallWithAssertScript(Hash, "verify") +} + +// Verify creates a transaction invoking `verify` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) Verify() (util.Uint256, uint32, error) { + script, err := scriptForVerify() + if err != nil { + return util.Uint256{}, 0, err + } + return c.actor.SendRun(script) +} + +// VerifyTransaction creates a transaction invoking `verify` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) VerifyTransaction() (*transaction.Transaction, error) { + script, err := scriptForVerify() + if err != nil { + return nil, err + } + return c.actor.MakeRun(script) +} + +// VerifyUnsigned creates a transaction invoking `verify` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) VerifyUnsigned() (*transaction.Transaction, error) { + script, err := scriptForVerify() + if err != nil { + return nil, err + } + return c.actor.MakeUnsignedRun(script, nil) +} + +// OnNEP17Payment creates a transaction invoking `onNEP17Payment` method of the contract. +// This transaction is signed and immediately sent to the network. +// The values returned are its hash, ValidUntilBlock value and error if any. +func (c *Contract) OnNEP17Payment(from []byte, amount *big.Int, data interface{}) (util.Uint256, uint32, error) { + return c.actor.SendCall(Hash, "onNEP17Payment", from, amount, data) +} + +// OnNEP17PaymentTransaction creates a transaction invoking `onNEP17Payment` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) OnNEP17PaymentTransaction(from []byte, amount *big.Int, data interface{}) (*transaction.Transaction, error) { + return c.actor.MakeCall(Hash, "onNEP17Payment", from, amount, data) +} + +// OnNEP17PaymentUnsigned creates a transaction invoking `onNEP17Payment` method of the contract. +// This transaction is not signed, it's simply returned to the caller. +// Any fields of it that do not affect fees can be changed (ValidUntilBlock, +// Nonce), fee values (NetworkFee, SystemFee) can be increased as well. +func (c *Contract) OnNEP17PaymentUnsigned(from []byte, amount *big.Int, data interface{}) (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(Hash, "onNEP17Payment", nil, from, amount, data) +} diff --git a/pkg/smartcontract/rpcbinding/binding.go b/pkg/smartcontract/rpcbinding/binding.go index 749e6a252..93b6266b9 100644 --- a/pkg/smartcontract/rpcbinding/binding.go +++ b/pkg/smartcontract/rpcbinding/binding.go @@ -90,7 +90,7 @@ import ( // Hash contains contract hash. var Hash = {{ .Hash }} -// Invoker is used by ContractReader to call various safe methods. +{{if .HasReader}}// Invoker is used by ContractReader to call various safe methods. type Invoker interface { {{if or .IsNep11D .IsNep11ND}} nep11.Invoker {{else if .IsNep17}} nep17.Invoker @@ -98,12 +98,19 @@ type Invoker interface { {{end -}} } +{{end -}} {{if .HasWriter}}// Actor is used by Contract to call state-changing methods. type Actor interface { +{{- if .HasReader}} Invoker -{{if or .IsNep11D .IsNep11ND}} nep11.Actor -{{else if .IsNep17}} nep17.Actor{{end}} -{{if len .Methods}} MakeCall(contract util.Uint160, method string, params ...interface{}) (*transaction.Transaction, error) +{{end}} +{{- if or .IsNep11D .IsNep11ND}} + nep11.Actor +{{else if .IsNep17}} + nep17.Actor +{{end}} +{{- if len .Methods}} + MakeCall(contract util.Uint160, method string, params ...interface{}) (*transaction.Transaction, error) MakeRun(script []byte) (*transaction.Transaction, error) MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...interface{}) (*transaction.Transaction, error) MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error) @@ -113,7 +120,7 @@ type Actor interface { } {{end -}} -// ContractReader implements safe contract methods. +{{if .HasReader}}// ContractReader implements safe contract methods. type ContractReader struct { {{if .IsNep11D}}nep11.DivisibleReader {{end -}} @@ -124,9 +131,11 @@ type ContractReader struct { invoker Invoker } +{{end -}} {{if .HasWriter}}// Contract implements all contract methods. type Contract struct { - ContractReader + {{if .HasReader}}ContractReader + {{end -}} {{if .IsNep11D}}nep11.DivisibleWriter {{end -}} {{if .IsNep11ND}}nep11.BaseWriter @@ -137,7 +146,7 @@ type Contract struct { } {{end -}} -// NewReader creates an instance of ContractReader using Hash and the given Invoker. +{{if .HasReader}}// NewReader creates an instance of ContractReader using Hash and the given Invoker. func NewReader(invoker Invoker) *ContractReader { return &ContractReader{ {{- if .IsNep11D}}*nep11.NewDivisibleReader(invoker, Hash), {{end}} @@ -146,6 +155,7 @@ func NewReader(invoker Invoker) *ContractReader { invoker} } +{{end -}} {{if .HasWriter}}// New creates an instance of Contract using Hash and the given Actor. func New(actor Actor) *Contract { {{if .IsNep11D}}var nep11dt = nep11.NewDivisible(actor, Hash) @@ -154,11 +164,12 @@ func New(actor Actor) *Contract { {{end -}} {{if .IsNep17}}var nep17t = nep17.New(actor, Hash) {{end -}} - return &Contract{ContractReader{ + return &Contract{ + {{- if .HasReader}}ContractReader{ {{- if .IsNep11D}}nep11dt.DivisibleReader, {{end -}} {{- if .IsNep11ND}}nep11ndt.NonDivisibleReader, {{end -}} {{- if .IsNep17}}nep17t.TokenReader, {{end -}} - actor}, + actor}, {{end -}} {{- if .IsNep11D}}nep11dt.DivisibleWriter, {{end -}} {{- if .IsNep11ND}}nep11ndt.BaseWriter, {{end -}} {{- if .IsNep17}}nep17t.TokenWriter, {{end -}} @@ -185,6 +196,7 @@ type ( IsNep11ND bool IsNep17 bool + HasReader bool HasWriter bool } ) @@ -353,6 +365,9 @@ func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]st if len(ctr.Methods) > 0 || ctr.IsNep17 || ctr.IsNep11D || ctr.IsNep11ND { ctr.HasWriter = true } + if len(ctr.SafeMethods) > 0 || ctr.IsNep17 || ctr.IsNep11D || ctr.IsNep11ND { + ctr.HasReader = true + } ctr.Imports = ctr.Imports[:0] for imp := range imports { ctr.Imports = append(ctr.Imports, imp)