package util import ( "fmt" "time" "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-adm/internal/commonflags" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/vm/vmstate" "github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/spf13/cobra" "github.com/spf13/viper" ) var ErrTooManyAlphabetNodes = fmt.Errorf("too many alphabet nodes (maximum allowed is %d)", MaxAlphabetNodes) func AwaitTx(cmd *cobra.Command, c Client, txs []HashVUBPair) error { cmd.Println("Waiting for transactions to persist...") at := trigger.Application var retErr error loop: for i := range txs { var it int var pollInterval time.Duration var pollIntervalChanged bool for { // 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) } continue loop } if txs[i].Vub < currBlock { return fmt.Errorf("tx was not persisted: Vub=%d, height=%d", txs[i].Vub, currBlock) } pollInterval, pollIntervalChanged = NextPollInterval(it, pollInterval) if pollIntervalChanged && viper.GetBool(commonflags.Verbose) { cmd.Printf("Pool interval to check transaction persistence changed: %s\n", pollInterval.String()) } timer := time.NewTimer(pollInterval) select { case <-cmd.Context().Done(): return cmd.Context().Err() case <-timer.C: } it++ } } return retErr } func NextPollInterval(it int, previous time.Duration) (time.Duration, bool) { const minPollInterval = 1 * time.Second const maxPollInterval = 16 * time.Second const changeAfter = 5 if it == 0 { return minPollInterval, true } if it%changeAfter != 0 { return previous, false } nextInterval := previous * 2 if nextInterval > maxPollInterval { return maxPollInterval, previous != maxPollInterval } return nextInterval, true } func GetWalletAccount(w *wallet.Wallet, typ string) (*wallet.Account, error) { for i := range w.Accounts { if w.Accounts[i].Label == typ { return w.Accounts[i], nil } } return nil, fmt.Errorf("account for '%s' not found", typ) }