[#744] morph: Revert commit ddcc156ecc
All checks were successful
DCO action / DCO (pull_request) Successful in 3m2s
Vulncheck / Vulncheck (pull_request) Successful in 6m27s
Tests and linters / Lint (pull_request) Successful in 9m4s
Tests and linters / Tests (1.20) (pull_request) Successful in 10m17s
Tests and linters / Tests (1.21) (pull_request) Successful in 10m16s
Tests and linters / Staticcheck (pull_request) Successful in 10m15s
Tests and linters / Tests with -race (pull_request) Successful in 10m26s
Build / Build Components (1.20) (pull_request) Successful in 3m13s
Build / Build Components (1.21) (pull_request) Successful in 6m13s
All checks were successful
DCO action / DCO (pull_request) Successful in 3m2s
Vulncheck / Vulncheck (pull_request) Successful in 6m27s
Tests and linters / Lint (pull_request) Successful in 9m4s
Tests and linters / Tests (1.20) (pull_request) Successful in 10m17s
Tests and linters / Tests (1.21) (pull_request) Successful in 10m16s
Tests and linters / Staticcheck (pull_request) Successful in 10m15s
Tests and linters / Tests with -race (pull_request) Successful in 10m26s
Build / Build Components (1.20) (pull_request) Successful in 3m13s
Build / Build Components (1.21) (pull_request) Successful in 6m13s
Required to unblock storage node upgrade from 0.36 to 0.37 Signed-off-by: Anton Nikiforov <an.nikiforov@yadro.com>
This commit is contained in:
parent
5acc13fa94
commit
1bf57815f3
3 changed files with 348 additions and 151 deletions
|
@ -24,6 +24,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/rolemgmt"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
|
||||
sc "github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
|
@ -157,6 +158,8 @@ func (e *notHaltStateError) Error() string {
|
|||
)
|
||||
}
|
||||
|
||||
var errEmptyInvocationScript = errors.New("got empty invocation script from neo node")
|
||||
|
||||
// implementation of error interface for FrostFS-specific errors.
|
||||
type frostfsError struct {
|
||||
err error
|
||||
|
@ -470,6 +473,64 @@ func (c *Client) roleList(r noderoles.Role) (keys.PublicKeys, error) {
|
|||
return c.rolemgmt.GetDesignatedByRole(r, height)
|
||||
}
|
||||
|
||||
// tries to resolve sc.Parameter from the arg.
|
||||
//
|
||||
// Wraps any error to frostfsError.
|
||||
func toStackParameter(value any) (sc.Parameter, error) {
|
||||
var res = sc.Parameter{
|
||||
Value: value,
|
||||
}
|
||||
|
||||
switch v := value.(type) {
|
||||
case []byte:
|
||||
res.Type = sc.ByteArrayType
|
||||
case int:
|
||||
res.Type = sc.IntegerType
|
||||
res.Value = big.NewInt(int64(v))
|
||||
case int64:
|
||||
res.Type = sc.IntegerType
|
||||
res.Value = big.NewInt(v)
|
||||
case uint64:
|
||||
res.Type = sc.IntegerType
|
||||
res.Value = new(big.Int).SetUint64(v)
|
||||
case [][]byte:
|
||||
arr := make([]sc.Parameter, 0, len(v))
|
||||
for i := range v {
|
||||
elem, err := toStackParameter(v[i])
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
arr = append(arr, elem)
|
||||
}
|
||||
|
||||
res.Type = sc.ArrayType
|
||||
res.Value = arr
|
||||
case string:
|
||||
res.Type = sc.StringType
|
||||
case util.Uint160:
|
||||
res.Type = sc.ByteArrayType
|
||||
res.Value = v.BytesBE()
|
||||
case noderoles.Role:
|
||||
res.Type = sc.IntegerType
|
||||
res.Value = big.NewInt(int64(v))
|
||||
case keys.PublicKeys:
|
||||
arr := make([][]byte, 0, len(v))
|
||||
for i := range v {
|
||||
arr = append(arr, v[i].Bytes())
|
||||
}
|
||||
|
||||
return toStackParameter(arr)
|
||||
case bool:
|
||||
res.Type = sc.BoolType
|
||||
res.Value = v
|
||||
default:
|
||||
return res, wrapFrostFSError(fmt.Errorf("chain/client: unsupported parameter %v", value))
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// MagicNumber returns the magic number of the network
|
||||
// to which the underlying RPC node client is connected.
|
||||
func (c *Client) MagicNumber() (uint64, error) {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
@ -20,12 +19,10 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/encoding/fixedn"
|
||||
"github.com/nspcc-dev/neo-go/pkg/neorpc"
|
||||
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/actor"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/notary"
|
||||
sc "github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
@ -34,6 +31,7 @@ type (
|
|||
notaryInfo struct {
|
||||
txValidTime uint32 // minimum amount of blocks when mainTx will be valid
|
||||
roundTime uint32 // extra amount of blocks to synchronize sidechain height diff of inner ring nodes
|
||||
fallbackTime uint32 // mainTx's ValidUntilBlock - fallbackTime + 1 is when fallbackTx is sent
|
||||
|
||||
alphabetSource AlphabetKeys // source of alphabet node keys to prepare witness
|
||||
|
||||
|
@ -44,7 +42,7 @@ type (
|
|||
notaryCfg struct {
|
||||
proxy util.Uint160
|
||||
|
||||
txValidTime, roundTime uint32
|
||||
txValidTime, roundTime, fallbackTime uint32
|
||||
|
||||
alphabetSource AlphabetKeys
|
||||
}
|
||||
|
@ -56,6 +54,7 @@ type (
|
|||
const (
|
||||
defaultNotaryValidTime = 50
|
||||
defaultNotaryRoundTime = 100
|
||||
defaultNotaryFallbackTime = 40
|
||||
|
||||
notaryBalanceOfMethod = "balanceOf"
|
||||
notaryExpirationOfMethod = "expirationOf"
|
||||
|
@ -71,6 +70,7 @@ func defaultNotaryConfig(c *Client) *notaryCfg {
|
|||
return ¬aryCfg{
|
||||
txValidTime: defaultNotaryValidTime,
|
||||
roundTime: defaultNotaryRoundTime,
|
||||
fallbackTime: defaultNotaryFallbackTime,
|
||||
alphabetSource: c.Committee,
|
||||
}
|
||||
}
|
||||
|
@ -105,6 +105,7 @@ func (c *Client) EnableNotarySupport(opts ...NotaryOption) error {
|
|||
proxy: cfg.proxy,
|
||||
txValidTime: cfg.txValidTime,
|
||||
roundTime: cfg.roundTime,
|
||||
fallbackTime: cfg.fallbackTime,
|
||||
alphabetSource: cfg.alphabetSource,
|
||||
notary: notary.Hash,
|
||||
}
|
||||
|
@ -408,32 +409,33 @@ func (c *Client) NotarySignAndInvokeTX(mainTx *transaction.Transaction) error {
|
|||
return fmt.Errorf("could not fetch current alphabet keys: %w", err)
|
||||
}
|
||||
|
||||
cosigners, err := c.notaryCosignersFromTx(mainTx, alphabetList)
|
||||
multiaddrAccount, err := c.notaryMultisigAccount(alphabetList, false, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nAct, err := notary.NewActor(c.client, cosigners, c.acc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// mainTX is expected to be pre-validated: second witness must exist and be empty
|
||||
mainTx.Scripts[1].VerificationScript = multiaddrAccount.GetVerificationScript()
|
||||
mainTx.Scripts[1].InvocationScript = append(
|
||||
[]byte{byte(opcode.PUSHDATA1), 64},
|
||||
multiaddrAccount.SignHashable(c.rpcActor.GetNetwork(), mainTx)...,
|
||||
)
|
||||
|
||||
// Sign exactly the same transaction we've got from the received Notary request.
|
||||
err = nAct.Sign(mainTx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("faield to sign notary request: %w", err)
|
||||
}
|
||||
|
||||
mainH, fbH, untilActual, err := nAct.Notarize(mainTx, nil)
|
||||
//lint:ignore SA1019 https://git.frostfs.info/TrueCloudLab/frostfs-node/issues/202
|
||||
resp, err := c.client.SignAndPushP2PNotaryRequest(mainTx,
|
||||
[]byte{byte(opcode.RET)},
|
||||
-1,
|
||||
0,
|
||||
c.notary.fallbackTime,
|
||||
c.acc)
|
||||
|
||||
if err != nil && !alreadyOnChainError(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
c.logger.Debug(logs.ClientNotaryRequestWithPreparedMainTXInvoked,
|
||||
zap.String("tx_hash", mainH.StringLE()),
|
||||
zap.Uint32("valid_until_block", untilActual),
|
||||
zap.String("fallback_hash", fbH.StringLE()))
|
||||
zap.Uint32("fallback_valid_for", c.notary.fallbackTime),
|
||||
zap.Stringer("tx_hash", resp.Hash().Reverse()))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -444,159 +446,75 @@ func (c *Client) notaryInvokeAsCommittee(method string, nonce, vub uint32, args
|
|||
}
|
||||
|
||||
func (c *Client) notaryInvoke(committee, invokedByAlpha bool, contract util.Uint160, nonce uint32, vub *uint32, method string, args ...any) error {
|
||||
start := time.Now()
|
||||
success := false
|
||||
defer func() {
|
||||
c.metrics.ObserveInvoke("notaryInvoke", contract.String(), method, success, time.Since(start))
|
||||
}()
|
||||
|
||||
alphabetList, err := c.notary.alphabetSource()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
until, err := c.getUntilValue(vub)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cosigners, err := c.notaryCosigners(invokedByAlpha, alphabetList, committee)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
nAct, err := notary.NewActor(c.client, cosigners, c.acc)
|
||||
params, err := invocationParams(args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mainH, fbH, untilActual, err := nAct.Notarize(nAct.MakeTunedCall(contract, method, nil, func(r *result.Invoke, t *transaction.Transaction) error {
|
||||
if r.State != vmstate.Halt.String() {
|
||||
return wrapFrostFSError(¬HaltStateError{state: r.State, exception: r.FaultException})
|
||||
test, err := c.makeTestInvocation(contract, method, params, cosigners)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.ValidUntilBlock = until
|
||||
t.Nonce = nonce
|
||||
multiaddrAccount, err := c.notaryMultisigAccount(alphabetList, committee, invokedByAlpha)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}, args...))
|
||||
until, err := c.getUntilValue(vub)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mainTx, err := c.buildMainTx(invokedByAlpha, nonce, alphabetList, test, cosigners, multiaddrAccount, until)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//lint:ignore SA1019 https://git.frostfs.info/TrueCloudLab/frostfs-node/issues/202
|
||||
resp, err := c.client.SignAndPushP2PNotaryRequest(mainTx,
|
||||
[]byte{byte(opcode.RET)},
|
||||
-1,
|
||||
0,
|
||||
c.notary.fallbackTime,
|
||||
c.acc)
|
||||
if err != nil && !alreadyOnChainError(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
c.logger.Debug(logs.ClientNotaryRequestInvoked,
|
||||
zap.String("method", method),
|
||||
zap.Uint32("valid_until_block", untilActual),
|
||||
zap.String("tx_hash", mainH.StringLE()),
|
||||
zap.String("fallback_hash", fbH.StringLE()))
|
||||
zap.Uint32("valid_until_block", until),
|
||||
zap.Uint32("fallback_valid_for", c.notary.fallbackTime),
|
||||
zap.Stringer("tx_hash", resp.Hash().Reverse()))
|
||||
|
||||
success = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) notaryCosignersFromTx(mainTx *transaction.Transaction, alphabetList keys.PublicKeys) ([]actor.SignerAccount, error) {
|
||||
multiaddrAccount, err := c.notaryMultisigAccount(alphabetList, false, true)
|
||||
func (c *Client) makeTestInvocation(contract util.Uint160, method string, params []sc.Parameter, cosigners []transaction.Signer) (*result.Invoke, error) {
|
||||
test, err := c.client.InvokeFunction(contract, method, params, cosigners)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Here we need to add a committee signature (second witness) to the pre-validated
|
||||
// main transaction without creating a new one. However, Notary actor demands the
|
||||
// proper set of signers for constructor, thus, fill it from the main transaction's signers list.
|
||||
s := make([]actor.SignerAccount, 2, 3)
|
||||
s[0] = actor.SignerAccount{
|
||||
// Proxy contract that will pay for the execution.
|
||||
Signer: mainTx.Signers[0],
|
||||
Account: notary.FakeContractAccount(mainTx.Signers[0].Account),
|
||||
}
|
||||
s[1] = actor.SignerAccount{
|
||||
// Inner ring multisignature.
|
||||
Signer: mainTx.Signers[1],
|
||||
Account: multiaddrAccount,
|
||||
}
|
||||
if len(mainTx.Signers) > 3 {
|
||||
// Invoker signature (simple signature account of storage node is expected).
|
||||
var acc *wallet.Account
|
||||
script := mainTx.Scripts[2].VerificationScript
|
||||
if len(script) == 0 {
|
||||
acc = notary.FakeContractAccount(mainTx.Signers[2].Account)
|
||||
} else {
|
||||
pubBytes, ok := vm.ParseSignatureContract(script)
|
||||
if ok {
|
||||
pub, err := keys.NewPublicKeyFromBytes(pubBytes, elliptic.P256())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse verification script of signer #2: invalid public key: %w", err)
|
||||
}
|
||||
acc = notary.FakeSimpleAccount(pub)
|
||||
} else {
|
||||
m, pubsBytes, ok := vm.ParseMultiSigContract(script)
|
||||
if !ok {
|
||||
return nil, errors.New("failed to parse verification script of signer #2: unknown witness type")
|
||||
}
|
||||
pubs := make(keys.PublicKeys, len(pubsBytes))
|
||||
for i := range pubs {
|
||||
pubs[i], err = keys.NewPublicKeyFromBytes(pubsBytes[i], elliptic.P256())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse verification script of signer #2: invalid public key #%d: %w", i, err)
|
||||
}
|
||||
}
|
||||
acc, err = notary.FakeMultisigAccount(m, pubs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create fake account for signer #2: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
s = append(s, actor.SignerAccount{
|
||||
Signer: mainTx.Signers[2],
|
||||
Account: acc,
|
||||
})
|
||||
if test.State != HaltState {
|
||||
return nil, wrapFrostFSError(¬HaltStateError{state: test.State, exception: test.FaultException})
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (c *Client) notaryCosigners(invokedByAlpha bool, ir []*keys.PublicKey, committee bool) ([]actor.SignerAccount, error) {
|
||||
multiaddrAccount, err := c.notaryMultisigAccount(ir, committee, invokedByAlpha)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if len(test.Script) == 0 {
|
||||
return nil, wrapFrostFSError(errEmptyInvocationScript)
|
||||
}
|
||||
s := make([]actor.SignerAccount, 2, 3)
|
||||
// Proxy contract that will pay for the execution.
|
||||
s[0] = actor.SignerAccount{
|
||||
Signer: transaction.Signer{
|
||||
Account: c.notary.proxy,
|
||||
Scopes: transaction.None,
|
||||
},
|
||||
Account: notary.FakeContractAccount(c.notary.proxy),
|
||||
}
|
||||
// Inner ring multisignature.
|
||||
s[1] = actor.SignerAccount{
|
||||
Signer: transaction.Signer{
|
||||
Account: multiaddrAccount.ScriptHash(),
|
||||
Scopes: c.cfg.signer.Scopes,
|
||||
AllowedContracts: c.cfg.signer.AllowedContracts,
|
||||
AllowedGroups: c.cfg.signer.AllowedGroups,
|
||||
},
|
||||
Account: multiaddrAccount,
|
||||
}
|
||||
|
||||
if !invokedByAlpha {
|
||||
// Invoker signature.
|
||||
s = append(s, actor.SignerAccount{
|
||||
Signer: transaction.Signer{
|
||||
Account: hash.Hash160(c.acc.GetVerificationScript()),
|
||||
Scopes: c.cfg.signer.Scopes,
|
||||
AllowedContracts: c.cfg.signer.AllowedContracts,
|
||||
AllowedGroups: c.cfg.signer.AllowedGroups,
|
||||
},
|
||||
Account: c.acc,
|
||||
})
|
||||
}
|
||||
|
||||
// The last one is Notary contract that will be added to the signers list
|
||||
// by Notary actor automatically.
|
||||
return s, nil
|
||||
return test, nil
|
||||
}
|
||||
|
||||
func (c *Client) getUntilValue(vub *uint32) (uint32, error) {
|
||||
|
@ -606,6 +524,195 @@ func (c *Client) getUntilValue(vub *uint32) (uint32, error) {
|
|||
return c.notaryTxValidationLimit()
|
||||
}
|
||||
|
||||
func (c *Client) buildMainTx(invokedByAlpha bool, nonce uint32, alphabetList keys.PublicKeys, test *result.Invoke,
|
||||
cosigners []transaction.Signer, multiaddrAccount *wallet.Account, until uint32) (*transaction.Transaction, error) {
|
||||
// after test invocation we build main multisig transaction
|
||||
|
||||
u8n := uint8(len(alphabetList))
|
||||
|
||||
if !invokedByAlpha {
|
||||
u8n++
|
||||
}
|
||||
|
||||
// prepare main tx
|
||||
mainTx := &transaction.Transaction{
|
||||
Nonce: nonce,
|
||||
SystemFee: test.GasConsumed,
|
||||
ValidUntilBlock: until,
|
||||
Script: test.Script,
|
||||
Attributes: []transaction.Attribute{
|
||||
{
|
||||
Type: transaction.NotaryAssistedT,
|
||||
Value: &transaction.NotaryAssisted{NKeys: u8n},
|
||||
},
|
||||
},
|
||||
Signers: cosigners,
|
||||
}
|
||||
|
||||
// calculate notary fee
|
||||
//lint:ignore SA1019 https://git.frostfs.info/TrueCloudLab/frostfs-node/issues/202
|
||||
notaryFee, err := c.client.CalculateNotaryFee(u8n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// add network fee for cosigners
|
||||
//nolint:staticcheck // waits for neo-go v0.99.3 with notary actors
|
||||
//lint:ignore SA1019 https://git.frostfs.info/TrueCloudLab/frostfs-node/issues/202
|
||||
err = c.client.AddNetworkFee(
|
||||
mainTx,
|
||||
notaryFee,
|
||||
c.notaryAccounts(invokedByAlpha, multiaddrAccount)...,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// define witnesses
|
||||
mainTx.Scripts = c.notaryWitnesses(invokedByAlpha, multiaddrAccount, mainTx)
|
||||
|
||||
return mainTx, nil
|
||||
}
|
||||
|
||||
func (c *Client) notaryCosigners(invokedByAlpha bool, ir []*keys.PublicKey, committee bool) ([]transaction.Signer, error) {
|
||||
s := make([]transaction.Signer, 0, 4)
|
||||
|
||||
// first we have proxy contract signature, as it will pay for the execution
|
||||
s = append(s, transaction.Signer{
|
||||
Account: c.notary.proxy,
|
||||
Scopes: transaction.None,
|
||||
})
|
||||
|
||||
// then we have inner ring multiaddress signature
|
||||
m := sigCount(ir, committee)
|
||||
|
||||
multisigScript, err := sc.CreateMultiSigRedeemScript(m, ir)
|
||||
if err != nil {
|
||||
// wrap error as FrostFS-specific since the call is not related to any client
|
||||
return nil, wrapFrostFSError(fmt.Errorf("can't create ir multisig redeem script: %w", err))
|
||||
}
|
||||
|
||||
s = append(s, transaction.Signer{
|
||||
Account: hash.Hash160(multisigScript),
|
||||
Scopes: c.cfg.signer.Scopes,
|
||||
AllowedContracts: c.cfg.signer.AllowedContracts,
|
||||
AllowedGroups: c.cfg.signer.AllowedGroups,
|
||||
})
|
||||
|
||||
if !invokedByAlpha {
|
||||
// then we have invoker signature
|
||||
s = append(s, transaction.Signer{
|
||||
Account: hash.Hash160(c.acc.GetVerificationScript()),
|
||||
Scopes: c.cfg.signer.Scopes,
|
||||
AllowedContracts: c.cfg.signer.AllowedContracts,
|
||||
AllowedGroups: c.cfg.signer.AllowedGroups,
|
||||
})
|
||||
}
|
||||
|
||||
// last one is a placeholder for notary contract signature
|
||||
s = append(s, transaction.Signer{
|
||||
Account: c.notary.notary,
|
||||
Scopes: transaction.None,
|
||||
})
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (c *Client) notaryAccounts(invokedByAlpha bool, multiaddr *wallet.Account) []*wallet.Account {
|
||||
if multiaddr == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
a := make([]*wallet.Account, 0, 4)
|
||||
|
||||
// first we have proxy account, as it will pay for the execution
|
||||
a = append(a, notary.FakeContractAccount(c.notary.proxy))
|
||||
|
||||
// then we have inner ring multiaddress account
|
||||
a = append(a, multiaddr)
|
||||
|
||||
if !invokedByAlpha {
|
||||
// then we have invoker account
|
||||
a = append(a, c.acc)
|
||||
}
|
||||
|
||||
// last one is a placeholder for notary contract account
|
||||
a = append(a, &wallet.Account{
|
||||
Contract: &wallet.Contract{},
|
||||
})
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
func (c *Client) notaryWitnesses(invokedByAlpha bool, multiaddr *wallet.Account, tx *transaction.Transaction) []transaction.Witness {
|
||||
if multiaddr == nil || tx == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
w := make([]transaction.Witness, 0, 4)
|
||||
|
||||
// first we have empty proxy witness, because notary will execute `Verify`
|
||||
// method on the proxy contract to check witness
|
||||
w = append(w, transaction.Witness{
|
||||
InvocationScript: []byte{},
|
||||
VerificationScript: []byte{},
|
||||
})
|
||||
|
||||
// then we have inner ring multiaddress witness
|
||||
|
||||
// invocation script should be of the form:
|
||||
// { PUSHDATA1, 64, signatureBytes... }
|
||||
// to pass Notary module verification
|
||||
var invokeScript []byte
|
||||
|
||||
magicNumber := c.rpcActor.GetNetwork()
|
||||
|
||||
if invokedByAlpha {
|
||||
invokeScript = append(
|
||||
[]byte{byte(opcode.PUSHDATA1), 64},
|
||||
multiaddr.SignHashable(magicNumber, tx)...,
|
||||
)
|
||||
} else {
|
||||
// we can't provide alphabet node signature
|
||||
// because Storage Node doesn't own alphabet's
|
||||
// private key. Thus, add dummy witness with
|
||||
// empty bytes instead of signature
|
||||
invokeScript = append(
|
||||
[]byte{byte(opcode.PUSHDATA1), 64},
|
||||
make([]byte, 64)...,
|
||||
)
|
||||
}
|
||||
|
||||
w = append(w, transaction.Witness{
|
||||
InvocationScript: invokeScript,
|
||||
VerificationScript: multiaddr.GetVerificationScript(),
|
||||
})
|
||||
|
||||
if !invokedByAlpha {
|
||||
// then we have invoker witness
|
||||
invokeScript = append(
|
||||
[]byte{byte(opcode.PUSHDATA1), 64},
|
||||
c.acc.SignHashable(magicNumber, tx)...,
|
||||
)
|
||||
|
||||
w = append(w, transaction.Witness{
|
||||
InvocationScript: invokeScript,
|
||||
VerificationScript: c.acc.GetVerificationScript(),
|
||||
})
|
||||
}
|
||||
|
||||
// last one is a placeholder for notary contract witness
|
||||
w = append(w, transaction.Witness{
|
||||
InvocationScript: append(
|
||||
[]byte{byte(opcode.PUSHDATA1), 64},
|
||||
make([]byte, 64)...,
|
||||
),
|
||||
VerificationScript: []byte{},
|
||||
})
|
||||
|
||||
return w
|
||||
}
|
||||
|
||||
func (c *Client) notaryMultisigAccount(ir []*keys.PublicKey, committee, invokedByAlpha bool) (*wallet.Account, error) {
|
||||
m := sigCount(ir, committee)
|
||||
|
||||
|
@ -662,6 +769,21 @@ func (c *Client) depositExpirationOf() (int64, error) {
|
|||
return currentTillBig.Int64(), nil
|
||||
}
|
||||
|
||||
func invocationParams(args ...any) ([]sc.Parameter, error) {
|
||||
params := make([]sc.Parameter, 0, len(args))
|
||||
|
||||
for i := range args {
|
||||
param, err := toStackParameter(args[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
params = append(params, param)
|
||||
}
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
// sigCount returns the number of required signature.
|
||||
// For FrostFS Alphabet M is a 2/3+1 of it (like in dBFT).
|
||||
// If committee is true, returns M as N/2+1.
|
||||
|
@ -699,6 +821,15 @@ func WithAlphabetSource(t AlphabetKeys) NotaryOption {
|
|||
}
|
||||
}
|
||||
|
||||
// WithFallbackTime returns a notary support option for client
|
||||
// that specifies amount of blocks before fallbackTx will be sent.
|
||||
// Should be less than TxValidTime.
|
||||
func WithFallbackTime(t uint32) NotaryOption {
|
||||
return func(c *notaryCfg) {
|
||||
c.fallbackTime = t
|
||||
}
|
||||
}
|
||||
|
||||
// WithProxyContract sets proxy contract hash.
|
||||
func WithProxyContract(h util.Uint160) NotaryOption {
|
||||
return func(c *notaryCfg) {
|
||||
|
|
|
@ -192,15 +192,15 @@ func (p Preparator) validateNotaryRequest(nr *payload.P2PNotaryRequest) error {
|
|||
}
|
||||
invokerWitness := ln == 4
|
||||
|
||||
// alphabet node should handle only notary requests that do not yet have inner
|
||||
// ring multisignature filled => such main TXs either have empty invocation script
|
||||
// of the inner ring witness (in case if Notary Actor is used to create request)
|
||||
// or have it filled with dummy bytes (if request was created manually with the old
|
||||
// neo-go API)
|
||||
multiInvScript := nr.MainTransaction.Scripts[1].InvocationScript
|
||||
|
||||
// alphabet node should handle only notary requests
|
||||
// that have been sent unsigned (by storage nodes) =>
|
||||
// such main TXs should have either a dummy or an
|
||||
// empty script as an invocation script
|
||||
//
|
||||
// this check prevents notary flow recursion
|
||||
if !(len(nr.MainTransaction.Scripts[1].InvocationScript) == 0 ||
|
||||
bytes.Equal(nr.MainTransaction.Scripts[1].InvocationScript, p.dummyInvocationScript)) { // compatibility with old version
|
||||
if len(multiInvScript) > 0 && !bytes.Equal(nr.MainTransaction.Scripts[1].InvocationScript, p.dummyInvocationScript) {
|
||||
return ErrTXAlreadyHandled
|
||||
}
|
||||
|
||||
|
@ -227,7 +227,12 @@ func (p Preparator) validateNotaryRequest(nr *payload.P2PNotaryRequest) error {
|
|||
}
|
||||
|
||||
// validate main TX expiration
|
||||
return p.validateExpiration(nr.FallbackTransaction)
|
||||
err = p.validateExpiration(nr.FallbackTransaction)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p Preparator) validateParameterOpcodes(ops []Op) error {
|
||||
|
|
Loading…
Reference in a new issue