From 6be9367f031e2a503e4d1d7984037444588982d3 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Wed, 7 Sep 2022 22:37:03 +0300 Subject: [PATCH] rpcclient/notary: add OnNEP17PaymentData and an example Update documentation as well to mention it and not mention outdated APIs. We can't link them yet, this will be done after the release. --- docs/notary.md | 12 ++--- pkg/rpcclient/notary/contract.go | 9 ++++ pkg/rpcclient/notary/doc_test.go | 89 ++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 6 deletions(-) create mode 100644 pkg/rpcclient/notary/doc_test.go diff --git a/docs/notary.md b/docs/notary.md index e9cfa6fc2..7c5e112dd 100644 --- a/docs/notary.md +++ b/docs/notary.md @@ -292,8 +292,10 @@ server via [`submitnotaryrequest` RPC call](./rpc.md#submitnotaryrequest-call). Note, that all parties must generate the same main transaction while fallbacks can differ. -To create a notary request, you can use [NeoGo RPC client](./rpc.md#Client). Follow -the steps to create a signature request: +To create a notary request, you can use [NeoGo RPC client](./rpc.md#Client). The +procedure below uses only basic RPC client functions and show all of the notary +request internals. You can use much simpler Actor interface in the notary +subpackage with an example written in Go doc. 1. Prepare a list of signers with scopes for the main transaction (i.e. the transaction that signatures are being collected for, that will be `Signers` @@ -307,8 +309,7 @@ the steps to create a signature request: Include Notary native contract in the list of signers with the following constraints: * Notary signer hash is the hash of a native Notary contract that can be fetched - from - [func (*Client) GetNativeContractHash](https://pkg.go.dev/github.com/nspcc-dev/neo-go@v0.97.2/pkg/rpcclient#Client.GetNativeContractHash). + from the notary RPC client subpackage (notary.Hash) * A notary signer must have `None` scope. * A notary signer shouldn't be placed at the beginning of the signer list because Notary contract does not pay main transaction fees. Other positions @@ -390,8 +391,7 @@ the steps to create a signature request: tries to push all associated fallbacks. Use the following rules to define `fallbackValidFor`: - `fallbackValidFor` shouldn't be more than `MaxNotValidBeforeDelta` value. - - Use [func (*Client) GetMaxNotValidBeforeDelta](https://pkg.go.dev/github.com/nspcc-dev/neo-go@v0.97.2/pkg/rpcclient#Client.GetMaxNotValidBeforeDelta) - to check `MaxNotValidBefore` value. + - Use notary package's GetMaxNotValidBeforeDelta to check `MaxNotValidBefore` value. 11. Construct a script for the fallback transaction. The script may do something useful, i.g. invoke method of a contract. However, if you don't need to perform anything special on fallback invocation, you can use simple `opcode.RET` script. diff --git a/pkg/rpcclient/notary/contract.go b/pkg/rpcclient/notary/contract.go index eee21bd0a..9bf3b6373 100644 --- a/pkg/rpcclient/notary/contract.go +++ b/pkg/rpcclient/notary/contract.go @@ -59,6 +59,15 @@ type Contract struct { actor ContractActor } +// OnNEP17PaymentData is the data set that is accepted by the notary contract +// onNEP17Payment handler. It's mandatory for GAS tranfers to this contract. +type OnNEP17PaymentData struct { + // Account can be nil, in this case transfer sender (from) account is used. + Account *util.Uint160 + // Till specifies the deposit lock time (in blocks). + Till uint32 +} + // Hash stores the hash of the native Notary contract. var Hash = state.CreateNativeContractHash(nativenames.Notary) diff --git a/pkg/rpcclient/notary/doc_test.go b/pkg/rpcclient/notary/doc_test.go new file mode 100644 index 000000000..33e2524f8 --- /dev/null +++ b/pkg/rpcclient/notary/doc_test.go @@ -0,0 +1,89 @@ +package notary_test + +import ( + "context" + "math/big" + "time" + + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/rpcclient" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/gas" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/notary" + "github.com/nspcc-dev/neo-go/pkg/rpcclient/policy" + "github.com/nspcc-dev/neo-go/pkg/vm/vmstate" + "github.com/nspcc-dev/neo-go/pkg/wallet" +) + +func ExampleActor() { + // No error checking done at all, intentionally. + w, _ := wallet.NewWalletFromFile("somewhere") + defer w.Close() + // We assume there are two accounts in the wallet --- one is a simple signature + // account and another one is committee account. The first one will send notary + // requests, while committee signatures need to be collected. + + // Create an RPC client. + c, _ := rpcclient.New(context.Background(), "url", rpcclient.Options{}) + + // An actor for the first account. + single, _ := actor.NewSimple(c, w.Accounts[0]) + + // Transfer some GAS to the Notary contract to be able to send notary requests + // from the first account. + gasSingle := gas.New(single) + txid, vub, _ := gasSingle.Transfer(single.Sender(), notary.Hash, big.NewInt(10_0000_0000), notary.OnNEP17PaymentData{Till: 10000000}) + + var depositOK bool + // Wait for transaction to be persisted, either it gets in and we get + // an application log with some result or it expires. + for height, err := c.GetBlockCount(); err == nil && height <= vub; height, err = c.GetBlockCount() { + appLog, err := c.GetApplicationLog(txid, nil) + // We can't separate "application log missing" from other errors at the moment, see #2248. + if err != nil { + time.Sleep(5 * time.Second) + continue + } + if len(appLog.Executions) == 1 && appLog.Executions[0].VMState == vmstate.Halt { + depositOK = true + } else { + break + } + } + if !depositOK { + panic("deposit failed") + } + + var opts notary.ActorOptions + // Add high priority attribute, we gonna be making committee-signed transactions anyway. + opts.MainAttributes = []transaction.Attribute{{Type: transaction.HighPriority}} + + // Create an Actor with the simple account used for paying fees and committee + // signature to be collected. + multi, _ := notary.NewTunedActor(c, []actor.SignerAccount{{ + // Sender, regular account with None scope. + Signer: transaction.Signer{ + Account: w.Accounts[0].ScriptHash(), + Scopes: transaction.None, + }, + Account: w.Accounts[0], + }, { + // Commmitee. + Signer: transaction.Signer{ + Account: w.Accounts[1].ScriptHash(), + Scopes: transaction.CalledByEntry, + }, + Account: w.Accounts[1], + }}, opts) + + // Use the Policy contract to perform something requiring committee signature. + policyContract := policy.New(multi) + + // Wrap a transaction to set storage price into a notary request. Fallback will + // be create automatically and all appropriate attributes will be added to both + // transactions. + mainTx, fbTx, vub, _ := multi.Notarize(policyContract.SetStoragePriceTransaction(10)) + _ = mainTx + _ = fbTx + _ = vub +}