diff --git a/cmd/neofs-adm/internal/modules/morph/initialize.go b/cmd/neofs-adm/internal/modules/morph/initialize.go index dabc833b..348e563d 100644 --- a/cmd/neofs-adm/internal/modules/morph/initialize.go +++ b/cmd/neofs-adm/internal/modules/morph/initialize.go @@ -315,7 +315,7 @@ func (c *initializeContext) getSigner(tryGroup bool) transaction.Signer { } func (c *clientContext) awaitTx(cmd *cobra.Command) error { - if len(c.Hashes) == 0 { + if len(c.SentTxs) == 0 { return nil } @@ -325,33 +325,32 @@ func (c *clientContext) awaitTx(cmd *cobra.Command) error { } } - err := awaitTx(cmd, c.Client, c.Hashes) - c.Hashes = c.Hashes[:0] + err := awaitTx(cmd, c.Client, c.SentTxs) + c.SentTxs = c.SentTxs[:0] return err } -func awaitTx(cmd *cobra.Command, c Client, hashes []util.Uint256) error { +func awaitTx(cmd *cobra.Command, c Client, txs []hashVUBPair) error { cmd.Println("Waiting for transactions to persist...") - // improve TX awaiting process: - // https://github.com/nspcc-dev/neofs-node/issues/1741 const pollInterval = time.Second - const waitDuration = 30 * time.Second tick := time.NewTicker(pollInterval) defer tick.Stop() - timer := time.NewTimer(waitDuration) - defer timer.Stop() - at := trigger.Application var retErr error + currBlock, err := c.GetBlockCount() + if err != nil { + return fmt.Errorf("can't fetch current block height: %w", err) + } + loop: - for i := range hashes { - res, err := c.GetApplicationLog(hashes[i], &at) + for i := range txs { + res, err := c.GetApplicationLog(txs[i].hash, &at) if err == nil { if retErr == nil && len(res.Executions) > 0 && res.Executions[0].VMState != vmstate.Halt { retErr = fmt.Errorf("tx %d persisted in %s state: %s", @@ -359,19 +358,25 @@ loop: } continue loop } - for { - select { - case <-tick.C: - res, err := c.GetApplicationLog(hashes[i], &at) - if err == nil { - if retErr == nil && len(res.Executions) > 0 && res.Executions[0].VMState != vmstate.Halt { - retErr = fmt.Errorf("tx %d persisted in %s state: %s", - i, res.Executions[0].VMState, res.Executions[0].FaultException) - } - continue loop + if txs[i].vub < currBlock { + return fmt.Errorf("tx was not persisted: vub=%d, height=%d", txs[i].vub, currBlock) + } + for range tick.C { + // We must fetch current height before application log, to avoid race condition. + currBlock, err = c.GetBlockCount() + if err != nil { + return fmt.Errorf("can't fetch current block height: %w", err) + } + res, err := c.GetApplicationLog(txs[i].hash, &at) + if err == nil { + if retErr == nil && len(res.Executions) > 0 && res.Executions[0].VMState != vmstate.Halt { + retErr = fmt.Errorf("tx %d persisted in %s state: %s", + i, res.Executions[0].VMState, res.Executions[0].FaultException) } - case <-timer.C: - return errors.New("timeout while waiting for transaction to persist") + continue loop + } + if txs[i].vub < currBlock { + return fmt.Errorf("tx was not persisted: vub=%d, height=%d", txs[i].vub, currBlock) } } } diff --git a/cmd/neofs-adm/internal/modules/morph/initialize_deploy.go b/cmd/neofs-adm/internal/modules/morph/initialize_deploy.go index d8e26469..da33f5d6 100644 --- a/cmd/neofs-adm/internal/modules/morph/initialize_deploy.go +++ b/cmd/neofs-adm/internal/modules/morph/initialize_deploy.go @@ -312,12 +312,12 @@ func (c *initializeContext) deployContracts() error { return fmt.Errorf("could not create actor: %w", err) } - txHash, _, err := act.SendCall(mgmtHash, deployMethodName, params...) + txHash, vub, err := act.SendCall(mgmtHash, deployMethodName, params...) if err != nil { return fmt.Errorf("can't deploy alphabet #%d contract: %w", i, err) } - c.Hashes = append(c.Hashes, txHash) + c.SentTxs = append(c.SentTxs, hashVUBPair{hash: txHash, vub: vub}) } for _, ctrName := range contractList { diff --git a/cmd/neofs-adm/internal/modules/morph/n3client.go b/cmd/neofs-adm/internal/modules/morph/n3client.go index 13b98715..419791a3 100644 --- a/cmd/neofs-adm/internal/modules/morph/n3client.go +++ b/cmd/neofs-adm/internal/modules/morph/n3client.go @@ -48,11 +48,16 @@ type Client interface { SignAndPushP2PNotaryRequest(*transaction.Transaction, []byte, int64, int64, uint32, *wallet.Account) (*payload.P2PNotaryRequest, error) } +type hashVUBPair struct { + hash util.Uint256 + vub uint32 +} + type clientContext struct { Client Client // a raw neo-go client OR a local chain implementation CommitteeAct *actor.Actor // committee actor with the Global witness scope ReadOnlyInvoker *invoker.Invoker // R/O contract invoker, does not contain any signer - Hashes []util.Uint256 + SentTxs []hashVUBPair } func getN3Client(v *viper.Viper) (Client, error) { @@ -110,7 +115,7 @@ func (c *clientContext) sendTx(tx *transaction.Transaction, cmd *cobra.Command, return fmt.Errorf("sent and actual tx hashes mismatch:\n\tsent: %v\n\tactual: %v", tx.Hash().StringLE(), h.StringLE()) } - c.Hashes = append(c.Hashes, h) + c.SentTxs = append(c.SentTxs, hashVUBPair{hash: h, vub: tx.ValidUntilBlock}) if await { return c.awaitTx(cmd) diff --git a/cmd/neofs-adm/internal/modules/morph/notary.go b/cmd/neofs-adm/internal/modules/morph/notary.go index abcfb3eb..6445b5ec 100644 --- a/cmd/neofs-adm/internal/modules/morph/notary.go +++ b/cmd/neofs-adm/internal/modules/morph/notary.go @@ -12,7 +12,6 @@ import ( "github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/rpcclient/actor" "github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17" - "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -117,7 +116,7 @@ func depositNotary(cmd *cobra.Command, _ []string) error { gas := nep17.New(act, gasHash) - txHash, _, err := gas.Transfer( + txHash, vub, err := gas.Transfer( accHash, notaryHash, big.NewInt(int64(gasAmount)), @@ -127,5 +126,5 @@ func depositNotary(cmd *cobra.Command, _ []string) error { return fmt.Errorf("could not send tx: %w", err) } - return awaitTx(cmd, c, []util.Uint256{txHash}) + return awaitTx(cmd, c, []hashVUBPair{{hash: txHash, vub: vub}}) }