Compare commits

...

4 commits

Author SHA1 Message Date
d82f0d1926
[#1496] node/control: Await until SetNetmapStatus() persists
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-11-15 16:36:07 +03:00
acd5babd86
[#1496] morph: Merge InvokeRes and WaitParams
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-11-15 16:36:07 +03:00
b65874d1c3
[#1496] morph: Return InvokeRes from all invoke*() methods
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-11-15 16:36:07 +03:00
69c63006da
[#1496] morph: Move tx waiter to morph package
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2024-11-15 16:36:07 +03:00
10 changed files with 90 additions and 82 deletions

View file

@ -17,11 +17,7 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/rand" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/rand"
"github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/waiter"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/vmstate"
"go.uber.org/zap" "go.uber.org/zap"
) )
@ -164,49 +160,14 @@ func makeNotaryDeposit(ctx context.Context, c *cfg) (util.Uint256, uint32, error
return c.cfgMorph.client.DepositEndlessNotary(ctx, depositAmount) return c.cfgMorph.client.DepositEndlessNotary(ctx, depositAmount)
} }
var (
errNotaryDepositFail = errors.New("notary deposit tx has faulted")
errNotaryDepositTimeout = errors.New("notary deposit tx has not appeared in the network")
)
type waiterClient struct {
c *client.Client
}
func (w *waiterClient) Context() context.Context {
return context.Background()
}
func (w *waiterClient) GetApplicationLog(hash util.Uint256, trig *trigger.Type) (*result.ApplicationLog, error) {
return w.c.GetApplicationLog(hash, trig)
}
func (w *waiterClient) GetBlockCount() (uint32, error) {
return w.c.BlockCount()
}
func (w *waiterClient) GetVersion() (*result.Version, error) {
return w.c.GetVersion()
}
func waitNotaryDeposit(ctx context.Context, c *cfg, tx util.Uint256, vub uint32) error { func waitNotaryDeposit(ctx context.Context, c *cfg, tx util.Uint256, vub uint32) error {
w, err := waiter.NewPollingBased(&waiterClient{c: c.cfgMorph.client}) if err := c.cfgMorph.client.WaitTxHalt(ctx, client.InvokeRes{Hash: tx, VUB: vub}); err != nil {
if err != nil { return err
return fmt.Errorf("could not create notary deposit waiter: %w", err)
} }
res, err := w.WaitAny(ctx, vub, tx)
if err != nil {
if errors.Is(err, waiter.ErrTxNotAccepted) {
return errNotaryDepositTimeout
}
return fmt.Errorf("could not wait for notary deposit persists in chain: %w", err)
}
if res.Execution.VMState.HasFlag(vmstate.Halt) {
c.log.Info(ctx, logs.ClientNotaryDepositTransactionWasSuccessfullyPersisted) c.log.Info(ctx, logs.ClientNotaryDepositTransactionWasSuccessfullyPersisted)
return nil return nil
} }
return errNotaryDepositFail
}
func listenMorphNotifications(ctx context.Context, c *cfg) { func listenMorphNotifications(ctx context.Context, c *cfg) {
var ( var (

View file

@ -421,9 +421,12 @@ func (c *cfg) updateNetMapState(ctx context.Context, stateSetter func(*nmClient.
prm.SetKey(c.key.PublicKey().Bytes()) prm.SetKey(c.key.PublicKey().Bytes())
stateSetter(&prm) stateSetter(&prm)
_, err := c.cfgNetmap.wrapper.UpdatePeerState(ctx, prm) res, err := c.cfgNetmap.wrapper.UpdatePeerState(ctx, prm)
if err != nil {
return err return err
} }
return c.cfgNetmap.wrapper.Morph().WaitTxHalt(ctx, res)
}
type netInfo struct { type netInfo struct {
netState netmap.State netState netmap.State

View file

@ -7,6 +7,7 @@ import (
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring/processors/alphabet" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring/processors/alphabet"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring/timers" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring/timers"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger/test" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger/test"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
@ -247,7 +248,7 @@ type testMorphClient struct {
batchTransferedGas []batchTransferGas batchTransferedGas []batchTransferGas
} }
func (c *testMorphClient) Invoke(_ context.Context, contract util.Uint160, fee fixedn.Fixed8, method string, args ...any) (uint32, error) { func (c *testMorphClient) Invoke(_ context.Context, contract util.Uint160, fee fixedn.Fixed8, method string, args ...any) (client.InvokeRes, error) {
c.invokedMethods = append(c.invokedMethods, c.invokedMethods = append(c.invokedMethods,
invokedMethod{ invokedMethod{
contract: contract, contract: contract,
@ -255,7 +256,7 @@ func (c *testMorphClient) Invoke(_ context.Context, contract util.Uint160, fee f
method: method, method: method,
args: args, args: args,
}) })
return 0, nil return client.InvokeRes{}, nil
} }
func (c *testMorphClient) TransferGas(receiver util.Uint160, amount fixedn.Fixed8) error { func (c *testMorphClient) TransferGas(receiver util.Uint160, amount fixedn.Fixed8) error {

View file

@ -8,6 +8,7 @@ import (
"time" "time"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring/metrics" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/innerring/metrics"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/event"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap" "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap"
@ -39,7 +40,7 @@ type (
} }
morphClient interface { morphClient interface {
Invoke(ctx context.Context, contract util.Uint160, fee fixedn.Fixed8, method string, args ...any) (uint32, error) Invoke(ctx context.Context, contract util.Uint160, fee fixedn.Fixed8, method string, args ...any) (client.InvokeRes, error)
TransferGas(receiver util.Uint160, amount fixedn.Fixed8) error TransferGas(receiver util.Uint160, amount fixedn.Fixed8) error
BatchTransferGas(receivers []util.Uint160, amount fixedn.Fixed8) error BatchTransferGas(receivers []util.Uint160, amount fixedn.Fixed8) error
} }

View file

@ -180,7 +180,7 @@ func wrapFrostFSError(err error) error {
// Invoke invokes contract method by sending transaction into blockchain. // Invoke invokes contract method by sending transaction into blockchain.
// Returns valid until block value. // Returns valid until block value.
// Supported args types: int64, string, util.Uint160, []byte and bool. // Supported args types: int64, string, util.Uint160, []byte and bool.
func (c *Client) Invoke(ctx context.Context, contract util.Uint160, fee fixedn.Fixed8, method string, args ...any) (uint32, error) { func (c *Client) Invoke(ctx context.Context, contract util.Uint160, fee fixedn.Fixed8, method string, args ...any) (InvokeRes, error) {
start := time.Now() start := time.Now()
success := false success := false
defer func() { defer func() {
@ -191,12 +191,12 @@ func (c *Client) Invoke(ctx context.Context, contract util.Uint160, fee fixedn.F
defer c.switchLock.RUnlock() defer c.switchLock.RUnlock()
if c.inactive { if c.inactive {
return 0, ErrConnectionLost return InvokeRes{}, ErrConnectionLost
} }
txHash, vub, err := c.rpcActor.SendTunedCall(contract, method, nil, addFeeCheckerModifier(int64(fee)), args...) txHash, vub, err := c.rpcActor.SendTunedCall(contract, method, nil, addFeeCheckerModifier(int64(fee)), args...)
if err != nil { if err != nil {
return 0, fmt.Errorf("could not invoke %s: %w", method, err) return InvokeRes{}, fmt.Errorf("could not invoke %s: %w", method, err)
} }
c.logger.Debug(ctx, logs.ClientNeoClientInvoke, c.logger.Debug(ctx, logs.ClientNeoClientInvoke,
@ -205,7 +205,7 @@ func (c *Client) Invoke(ctx context.Context, contract util.Uint160, fee fixedn.F
zap.Stringer("tx_hash", txHash.Reverse())) zap.Stringer("tx_hash", txHash.Reverse()))
success = true success = true
return vub, nil return InvokeRes{Hash: txHash, VUB: vub}, nil
} }
// TestInvokeIterator invokes contract method returning an iterator and executes cb on each element. // TestInvokeIterator invokes contract method returning an iterator and executes cb on each element.

View file

@ -58,9 +58,9 @@ func (c *Client) ForceRemovePeer(ctx context.Context, nodeInfo netmap.NodeInfo,
prm.SetControlTX(true) prm.SetControlTX(true)
prm.SetVUB(vub) prm.SetVUB(vub)
vub, err := c.UpdatePeerState(ctx, prm) res, err := c.UpdatePeerState(ctx, prm)
if err != nil { if err != nil {
return 0, fmt.Errorf("updating peer state: %v", err) return 0, fmt.Errorf("updating peer state: %v", err)
} }
return vub, nil return res.VUB, nil
} }

View file

@ -2,7 +2,6 @@ package netmap
import ( import (
"context" "context"
"fmt"
"git.frostfs.info/TrueCloudLab/frostfs-contract/netmap" "git.frostfs.info/TrueCloudLab/frostfs-contract/netmap"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client"
@ -37,7 +36,7 @@ func (u *UpdatePeerPrm) SetMaintenance() {
} }
// UpdatePeerState changes peer status through Netmap contract call. // UpdatePeerState changes peer status through Netmap contract call.
func (c *Client) UpdatePeerState(ctx context.Context, p UpdatePeerPrm) (uint32, error) { func (c *Client) UpdatePeerState(ctx context.Context, p UpdatePeerPrm) (client.InvokeRes, error) {
method := updateStateMethod method := updateStateMethod
if c.client.WithNotary() && c.client.IsAlpha() { if c.client.WithNotary() && c.client.IsAlpha() {
@ -56,9 +55,5 @@ func (c *Client) UpdatePeerState(ctx context.Context, p UpdatePeerPrm) (uint32,
prm.SetArgs(int64(p.state), p.key) prm.SetArgs(int64(p.state), p.key)
prm.InvokePrmOptional = p.InvokePrmOptional prm.InvokePrmOptional = p.InvokePrmOptional
res, err := c.client.Invoke(ctx, prm) return c.client.Invoke(ctx, prm)
if err != nil {
return 0, fmt.Errorf("could not invoke smart contract: %w", err)
}
return res.VUB, nil
} }

View file

@ -358,12 +358,12 @@ func (c *Client) UpdateNeoFSAlphabetList(ctx context.Context, prm UpdateAlphabet
// Returns valid until block value. // Returns valid until block value.
// //
// `nonce` and `vub` are used only if notary is enabled. // `nonce` and `vub` are used only if notary is enabled.
func (c *Client) NotaryInvoke(ctx context.Context, contract util.Uint160, fee fixedn.Fixed8, nonce uint32, vub *uint32, method string, args ...any) (uint32, error) { func (c *Client) NotaryInvoke(ctx context.Context, contract util.Uint160, fee fixedn.Fixed8, nonce uint32, vub *uint32, method string, args ...any) (InvokeRes, error) {
c.switchLock.RLock() c.switchLock.RLock()
defer c.switchLock.RUnlock() defer c.switchLock.RUnlock()
if c.inactive { if c.inactive {
return 0, ErrConnectionLost return InvokeRes{}, ErrConnectionLost
} }
if c.notary == nil { if c.notary == nil {
@ -378,12 +378,12 @@ func (c *Client) NotaryInvoke(ctx context.Context, contract util.Uint160, fee fi
// not expected to be signed by the current node. // not expected to be signed by the current node.
// //
// Considered to be used by non-IR nodes. // Considered to be used by non-IR nodes.
func (c *Client) NotaryInvokeNotAlpha(ctx context.Context, contract util.Uint160, fee fixedn.Fixed8, vubP *uint32, method string, args ...any) (uint32, error) { func (c *Client) NotaryInvokeNotAlpha(ctx context.Context, contract util.Uint160, fee fixedn.Fixed8, vubP *uint32, method string, args ...any) (InvokeRes, error) {
c.switchLock.RLock() c.switchLock.RLock()
defer c.switchLock.RUnlock() defer c.switchLock.RUnlock()
if c.inactive { if c.inactive {
return 0, ErrConnectionLost return InvokeRes{}, ErrConnectionLost
} }
if c.notary == nil { if c.notary == nil {
@ -446,7 +446,7 @@ func (c *Client) notaryInvokeAsCommittee(ctx context.Context, method string, non
return err return err
} }
func (c *Client) notaryInvoke(ctx context.Context, committee, invokedByAlpha bool, contract util.Uint160, nonce uint32, vub *uint32, method string, args ...any) (uint32, error) { func (c *Client) notaryInvoke(ctx context.Context, committee, invokedByAlpha bool, contract util.Uint160, nonce uint32, vub *uint32, method string, args ...any) (InvokeRes, error) {
start := time.Now() start := time.Now()
success := false success := false
defer func() { defer func() {
@ -455,22 +455,22 @@ func (c *Client) notaryInvoke(ctx context.Context, committee, invokedByAlpha boo
alphabetList, err := c.notary.alphabetSource() alphabetList, err := c.notary.alphabetSource()
if err != nil { if err != nil {
return 0, err return InvokeRes{}, err
} }
until, err := c.getUntilValue(vub) until, err := c.getUntilValue(vub)
if err != nil { if err != nil {
return 0, err return InvokeRes{}, err
} }
cosigners, err := c.notaryCosigners(invokedByAlpha, alphabetList, committee) cosigners, err := c.notaryCosigners(invokedByAlpha, alphabetList, committee)
if err != nil { if err != nil {
return 0, err return InvokeRes{}, err
} }
nAct, err := notary.NewActor(c.client, cosigners, c.acc) nAct, err := notary.NewActor(c.client, cosigners, c.acc)
if err != nil { if err != nil {
return 0, err return InvokeRes{}, err
} }
mainH, fbH, untilActual, err := nAct.Notarize(nAct.MakeTunedCall(contract, method, nil, func(r *result.Invoke, t *transaction.Transaction) error { mainH, fbH, untilActual, err := nAct.Notarize(nAct.MakeTunedCall(contract, method, nil, func(r *result.Invoke, t *transaction.Transaction) error {
@ -485,7 +485,7 @@ func (c *Client) notaryInvoke(ctx context.Context, committee, invokedByAlpha boo
}, args...)) }, args...))
if err != nil && !alreadyOnChainError(err) { if err != nil && !alreadyOnChainError(err) {
return 0, err return InvokeRes{}, err
} }
c.logger.Debug(ctx, logs.ClientNotaryRequestInvoked, c.logger.Debug(ctx, logs.ClientNotaryRequestInvoked,
@ -495,7 +495,7 @@ func (c *Client) notaryInvoke(ctx context.Context, committee, invokedByAlpha boo
zap.String("fallback_hash", fbH.StringLE())) zap.String("fallback_hash", fbH.StringLE()))
success = true success = true
return until, nil return InvokeRes{Hash: mainH, VUB: until}, nil
} }
func (c *Client) notaryCosignersFromTx(mainTx *transaction.Transaction, alphabetList keys.PublicKeys) ([]actor.SignerAccount, error) { func (c *Client) notaryCosignersFromTx(mainTx *transaction.Transaction, alphabetList keys.PublicKeys) ([]actor.SignerAccount, error) {

View file

@ -129,6 +129,7 @@ func (i *InvokePrmOptional) SetVUB(v uint32) {
} }
type InvokeRes struct { type InvokeRes struct {
Hash util.Uint256
VUB uint32 VUB uint32
} }
@ -142,8 +143,6 @@ type InvokeRes struct {
// If fee for the operation executed using specified method is customized, then StaticClient uses it. // If fee for the operation executed using specified method is customized, then StaticClient uses it.
// Otherwise, default fee is used. // Otherwise, default fee is used.
func (s StaticClient) Invoke(ctx context.Context, prm InvokePrm) (InvokeRes, error) { func (s StaticClient) Invoke(ctx context.Context, prm InvokePrm) (InvokeRes, error) {
var res InvokeRes
var err error
var vubP *uint32 var vubP *uint32
if s.tryNotary { if s.tryNotary {
if s.alpha { if s.alpha {
@ -170,26 +169,23 @@ func (s StaticClient) Invoke(ctx context.Context, prm InvokePrm) (InvokeRes, err
vubP = &prm.vub vubP = &prm.vub
} }
res.VUB, err = s.client.NotaryInvoke(ctx, s.scScriptHash, s.fee, nonce, vubP, prm.method, prm.args...) return s.client.NotaryInvoke(ctx, s.scScriptHash, s.fee, nonce, vubP, prm.method, prm.args...)
return res, err
} }
if prm.vub > 0 { if prm.vub > 0 {
vubP = &prm.vub vubP = &prm.vub
} }
res.VUB, err = s.client.NotaryInvokeNotAlpha(ctx, s.scScriptHash, s.fee, vubP, prm.method, prm.args...) return s.client.NotaryInvokeNotAlpha(ctx, s.scScriptHash, s.fee, vubP, prm.method, prm.args...)
return res, err
} }
res.VUB, err = s.client.Invoke( return s.client.Invoke(
ctx, ctx,
s.scScriptHash, s.scScriptHash,
s.fee, s.fee,
prm.method, prm.method,
prm.args..., prm.args...,
) )
return res, err
} }
// TestInvokePrm groups parameters of the TestInvoke operation. // TestInvokePrm groups parameters of the TestInvoke operation.

View file

@ -0,0 +1,51 @@
package client
import (
"context"
"fmt"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/waiter"
"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/vmstate"
)
type waiterClient struct {
c *Client
}
func (w *waiterClient) Context() context.Context {
return context.Background()
}
func (w *waiterClient) GetApplicationLog(hash util.Uint256, trig *trigger.Type) (*result.ApplicationLog, error) {
return w.c.GetApplicationLog(hash, trig)
}
func (w *waiterClient) GetBlockCount() (uint32, error) {
return w.c.BlockCount()
}
func (w *waiterClient) GetVersion() (*result.Version, error) {
return w.c.GetVersion()
}
// WaitTxHalt waits until transaction with the specified hash persists on the blockchain.
// It also checks execution result to finish in HALT state.
func (c *Client) WaitTxHalt(ctx context.Context, p InvokeRes) error {
w, err := waiter.NewPollingBased(&waiterClient{c: c})
if err != nil {
return fmt.Errorf("create tx waiter: %w", err)
}
res, err := w.WaitAny(ctx, p.VUB, p.Hash)
if err != nil {
return fmt.Errorf("wait until tx persists: %w", err)
}
if res.VMState.HasFlag(vmstate.Halt) {
return nil
}
return wrapFrostFSError(&notHaltStateError{state: res.VMState.String(), exception: res.FaultException})
}