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.
This commit is contained in:
Roman Khimov 2022-09-07 22:37:03 +03:00
parent 4fb4f5a1ac
commit 6be9367f03
3 changed files with 104 additions and 6 deletions

View file

@ -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.

View file

@ -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)

View file

@ -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
}