[#971] morph/client: Group Invoke
and TestInvoke
params
Also: - add `validUntilBlock` and nonce calculation; - pass notification hashes to notary calls. Signed-off-by: Pavel Karpy <carpawell@nspcc.ru>
This commit is contained in:
parent
8e1f187822
commit
3849d13e0b
2 changed files with 176 additions and 29 deletions
|
@ -1,6 +1,7 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
@ -243,14 +244,31 @@ func (c *Client) GetNotaryDeposit() (res int64, err error) {
|
|||
return bigIntDeposit.Int64(), nil
|
||||
}
|
||||
|
||||
// UpdateNotaryListPrm groups parameters of UpdateNotaryList operation.
|
||||
type UpdateNotaryListPrm struct {
|
||||
list keys.PublicKeys
|
||||
hash util.Uint256
|
||||
}
|
||||
|
||||
// SetList sets list of the new notary role keys.
|
||||
func (u *UpdateNotaryListPrm) SetList(list keys.PublicKeys) {
|
||||
u.list = list
|
||||
}
|
||||
|
||||
// SetHash sets hash of the transaction that led to the update
|
||||
// of the notary role in the designate contract.
|
||||
func (u *UpdateNotaryListPrm) SetHash(hash util.Uint256) {
|
||||
u.hash = hash
|
||||
}
|
||||
|
||||
// UpdateNotaryList updates list of notary nodes in designate contract. Requires
|
||||
// committee multi signature.
|
||||
//
|
||||
// This function must be invoked with notary enabled otherwise it throws panic.
|
||||
func (c *Client) UpdateNotaryList(list keys.PublicKeys) error {
|
||||
func (c *Client) UpdateNotaryList(prm UpdateNotaryListPrm) error {
|
||||
if c.multiClient != nil {
|
||||
return c.multiClient.iterateClients(func(c *Client) error {
|
||||
return c.UpdateNotaryList(list)
|
||||
return c.UpdateNotaryList(prm)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -258,23 +276,46 @@ func (c *Client) UpdateNotaryList(list keys.PublicKeys) error {
|
|||
panic(notaryNotEnabledPanicMsg)
|
||||
}
|
||||
|
||||
nonce, vub, err := c.CalculateNonceAndVUB(prm.hash)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not calculate nonce and `valicUntilBlock` values: %w", err)
|
||||
}
|
||||
|
||||
return c.notaryInvokeAsCommittee(
|
||||
setDesignateMethod,
|
||||
1, // FIXME: do not use constant nonce for alphabet NR: #844
|
||||
nonce,
|
||||
vub,
|
||||
noderoles.P2PNotary,
|
||||
list,
|
||||
prm.list,
|
||||
)
|
||||
}
|
||||
|
||||
// UpdateAlphabetListPrm groups parameters of UpdateNeoFSAlphabetList operation.
|
||||
type UpdateAlphabetListPrm struct {
|
||||
list keys.PublicKeys
|
||||
hash util.Uint256
|
||||
}
|
||||
|
||||
// SetList sets list of the new alphabet role keys.
|
||||
func (u *UpdateAlphabetListPrm) SetList(list keys.PublicKeys) {
|
||||
u.list = list
|
||||
}
|
||||
|
||||
// SetHash sets hash of the transaction that led to the update
|
||||
// of the alphabet role in the designate contract.
|
||||
func (u *UpdateAlphabetListPrm) SetHash(hash util.Uint256) {
|
||||
u.hash = hash
|
||||
}
|
||||
|
||||
// UpdateNeoFSAlphabetList updates list of alphabet nodes in designate contract.
|
||||
// As for side chain list should contain all inner ring nodes.
|
||||
// Requires committee multi signature.
|
||||
//
|
||||
// This function must be invoked with notary enabled otherwise it throws panic.
|
||||
func (c *Client) UpdateNeoFSAlphabetList(list keys.PublicKeys) error {
|
||||
func (c *Client) UpdateNeoFSAlphabetList(prm UpdateAlphabetListPrm) error {
|
||||
if c.multiClient != nil {
|
||||
return c.multiClient.iterateClients(func(c *Client) error {
|
||||
return c.UpdateNeoFSAlphabetList(list)
|
||||
return c.UpdateNeoFSAlphabetList(prm)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -282,11 +323,17 @@ func (c *Client) UpdateNeoFSAlphabetList(list keys.PublicKeys) error {
|
|||
panic(notaryNotEnabledPanicMsg)
|
||||
}
|
||||
|
||||
nonce, vub, err := c.CalculateNonceAndVUB(prm.hash)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not calculate nonce and `valicUntilBlock` values: %w", err)
|
||||
}
|
||||
|
||||
return c.notaryInvokeAsCommittee(
|
||||
setDesignateMethod,
|
||||
1, // FIXME: do not use constant nonce for alphabet NR: #844
|
||||
nonce,
|
||||
vub,
|
||||
noderoles.NeoFSAlphabet,
|
||||
list,
|
||||
prm.list,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -294,11 +341,11 @@ func (c *Client) UpdateNeoFSAlphabetList(list keys.PublicKeys) error {
|
|||
// blockchain. Fallback tx is a `RET`. If Notary support is not enabled
|
||||
// it fallbacks to a simple `Invoke()`.
|
||||
//
|
||||
// This function must be invoked with notary enabled otherwise it throws panic.
|
||||
func (c *Client) NotaryInvoke(contract util.Uint160, fee fixedn.Fixed8, nonce uint32, method string, args ...interface{}) error {
|
||||
// `nonce` and `vub` are used only if notary is enabled.
|
||||
func (c *Client) NotaryInvoke(contract util.Uint160, fee fixedn.Fixed8, nonce uint32, vub *uint32, method string, args ...interface{}) error {
|
||||
if c.multiClient != nil {
|
||||
return c.multiClient.iterateClients(func(c *Client) error {
|
||||
return c.NotaryInvoke(contract, fee, nonce, method, args...)
|
||||
return c.NotaryInvoke(contract, fee, nonce, vub, method, args...)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -306,7 +353,7 @@ func (c *Client) NotaryInvoke(contract util.Uint160, fee fixedn.Fixed8, nonce ui
|
|||
return c.Invoke(contract, fee, method, args...)
|
||||
}
|
||||
|
||||
return c.notaryInvoke(false, true, contract, nonce, method, args...)
|
||||
return c.notaryInvoke(false, true, contract, nonce, vub, method, args...)
|
||||
}
|
||||
|
||||
// randSource is a source of random numbers.
|
||||
|
@ -328,7 +375,7 @@ func (c *Client) NotaryInvokeNotAlpha(contract util.Uint160, fee fixedn.Fixed8,
|
|||
return c.Invoke(contract, fee, method, args...)
|
||||
}
|
||||
|
||||
return c.notaryInvoke(false, false, contract, randSource.Uint32(), method, args...)
|
||||
return c.notaryInvoke(false, false, contract, randSource.Uint32(), nil, method, args...)
|
||||
}
|
||||
|
||||
// NotarySignAndInvokeTX signs and sends notary request that was received from
|
||||
|
@ -376,16 +423,16 @@ func (c *Client) NotarySignAndInvokeTX(mainTx *transaction.Transaction) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) notaryInvokeAsCommittee(method string, nonce uint32, args ...interface{}) error {
|
||||
func (c *Client) notaryInvokeAsCommittee(method string, nonce, vub uint32, args ...interface{}) error {
|
||||
designate, err := c.GetDesignateHash()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return c.notaryInvoke(true, true, designate, nonce, method, args...)
|
||||
return c.notaryInvoke(true, true, designate, nonce, &vub, method, args...)
|
||||
}
|
||||
|
||||
func (c *Client) notaryInvoke(committee, invokedByAlpha bool, contract util.Uint160, nonce uint32, method string, args ...interface{}) error {
|
||||
func (c *Client) notaryInvoke(committee, invokedByAlpha bool, contract util.Uint160, nonce uint32, vub *uint32, method string, args ...interface{}) error {
|
||||
alphabetList, err := c.notary.alphabetSource() // prepare arguments for test invocation
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -427,10 +474,16 @@ func (c *Client) notaryInvoke(committee, invokedByAlpha bool, contract util.Uint
|
|||
return err
|
||||
}
|
||||
|
||||
until, err := c.notaryTxValidationLimit()
|
||||
var until uint32
|
||||
|
||||
if vub != nil {
|
||||
until = *vub
|
||||
} else {
|
||||
until, err = c.notaryTxValidationLimit()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// prepare main tx
|
||||
mainTx := &transaction.Transaction{
|
||||
|
@ -774,3 +827,24 @@ func CalculateNotaryDepositAmount(c *Client, gasMul, gasDiv int64) (fixedn.Fixed
|
|||
|
||||
return fixedn.Fixed8(depositAmount), nil
|
||||
}
|
||||
|
||||
// CalculateNonceAndVUB calculates nonce and ValidUntilBlock values
|
||||
// based on transaction hash. Uses MurmurHash3.
|
||||
func (c *Client) CalculateNonceAndVUB(hash util.Uint256) (nonce uint32, vub uint32, err error) {
|
||||
if c.multiClient != nil {
|
||||
return nonce, vub, c.multiClient.iterateClients(func(c *Client) error {
|
||||
nonce, vub, err = c.CalculateNonceAndVUB(hash)
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: cache values since some operations uses same TX as triggers
|
||||
nonce = binary.LittleEndian.Uint32(hash.BytesLE())
|
||||
|
||||
height, err := c.client.GetTransactionHeight(hash)
|
||||
if err != nil {
|
||||
return 0, 0, fmt.Errorf("could not get transaction height: %w", err)
|
||||
}
|
||||
|
||||
return nonce, height + c.notary.txValidTime, nil
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package client
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
|
@ -71,34 +72,106 @@ func (s StaticClient) Morph() *Client {
|
|||
return s.client
|
||||
}
|
||||
|
||||
// InvokePrm groups parameters of the Invoke operation.
|
||||
type InvokePrm struct {
|
||||
// required parameters
|
||||
method string
|
||||
args []interface{}
|
||||
|
||||
// optional parameters
|
||||
InvokePrmOptional
|
||||
}
|
||||
|
||||
// InvokePrmOptional groups optional parameters of the Invoke operation.
|
||||
type InvokePrmOptional struct {
|
||||
// hash is an optional hash of the transaction
|
||||
// that generated the notification that required
|
||||
// to invoke notary request.
|
||||
// It is used to generate same but unique nonce and
|
||||
// `validUntilBlock` values by all notification
|
||||
// receivers.
|
||||
hash *util.Uint256
|
||||
}
|
||||
|
||||
// SetMethod sets method of the contract to call.
|
||||
func (i *InvokePrm) SetMethod(method string) {
|
||||
i.method = method
|
||||
}
|
||||
|
||||
// SetArgs sets arguments of the contact call.
|
||||
func (i *InvokePrm) SetArgs(args ...interface{}) {
|
||||
i.args = args
|
||||
}
|
||||
|
||||
// SetHash sets optional hash of the transaction.
|
||||
// If hash is set and notary is enabled, StaticClient
|
||||
// uses it for notary nonce and `validUntilBlock`
|
||||
// calculation.
|
||||
func (i *InvokePrmOptional) SetHash(hash util.Uint256) {
|
||||
i.hash = &hash
|
||||
}
|
||||
|
||||
// Invoke calls Invoke method of Client with static internal script hash and fee.
|
||||
// Supported args types are the same as in Client.
|
||||
//
|
||||
// If TryNotary is provided, calls NotaryInvoke on Client.
|
||||
func (s StaticClient) Invoke(method string, args ...interface{}) error {
|
||||
// If TryNotary is provided:
|
||||
// - if AsAlphabet is provided, calls NotaryInvoke;
|
||||
// - otherwise, calls NotaryInvokeNotAlpha.
|
||||
func (s StaticClient) Invoke(prm InvokePrm) error {
|
||||
if s.tryNotary {
|
||||
if s.alpha {
|
||||
// FIXME: do not use constant nonce for alphabet NR: #844
|
||||
return s.client.NotaryInvoke(s.scScriptHash, s.fee, 1, method, args...)
|
||||
var (
|
||||
nonce uint32 = 1
|
||||
vubP *uint32
|
||||
vub uint32
|
||||
err error
|
||||
)
|
||||
|
||||
if prm.hash != nil {
|
||||
nonce, vub, err = s.client.CalculateNonceAndVUB(*prm.hash)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not calculate nonce and VUB for notary alphabet invoke: %w", err)
|
||||
}
|
||||
|
||||
return s.client.NotaryInvokeNotAlpha(s.scScriptHash, s.fee, method, args...)
|
||||
vubP = &vub
|
||||
}
|
||||
|
||||
return s.client.NotaryInvoke(s.scScriptHash, s.fee, nonce, vubP, prm.method, prm.args...)
|
||||
}
|
||||
|
||||
return s.client.NotaryInvokeNotAlpha(s.scScriptHash, s.fee, prm.method, prm.args...)
|
||||
}
|
||||
|
||||
return s.client.Invoke(
|
||||
s.scScriptHash,
|
||||
s.fee,
|
||||
method,
|
||||
args...,
|
||||
prm.method,
|
||||
prm.args...,
|
||||
)
|
||||
}
|
||||
|
||||
// TestInvokePrm groups parameters of the TestInvoke operation.
|
||||
type TestInvokePrm struct {
|
||||
method string
|
||||
args []interface{}
|
||||
}
|
||||
|
||||
// SetMethod sets method of the contract to call.
|
||||
func (ti *TestInvokePrm) SetMethod(method string) {
|
||||
ti.method = method
|
||||
}
|
||||
|
||||
// SetArgs sets arguments of the contact call.
|
||||
func (ti *TestInvokePrm) SetArgs(args ...interface{}) {
|
||||
ti.args = args
|
||||
}
|
||||
|
||||
// TestInvoke calls TestInvoke method of Client with static internal script hash.
|
||||
func (s StaticClient) TestInvoke(method string, args ...interface{}) ([]stackitem.Item, error) {
|
||||
func (s StaticClient) TestInvoke(prm TestInvokePrm) ([]stackitem.Item, error) {
|
||||
return s.client.TestInvoke(
|
||||
s.scScriptHash,
|
||||
method,
|
||||
args...,
|
||||
prm.method,
|
||||
prm.args...,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue