From 1ba7015955bf271b01d10e2da9e7762e9b550e6b Mon Sep 17 00:00:00 2001 From: ice-coldbell Date: Mon, 24 Feb 2025 02:00:22 +0900 Subject: [PATCH 1/3] [waitForTransaction] Change WaitForTransaction with long polling --- nodeClient.go | 58 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/nodeClient.go b/nodeClient.go index 7f67563..6b343fc 100644 --- a/nodeClient.go +++ b/nodeClient.go @@ -2,6 +2,7 @@ package aptos import ( "bytes" + "context" "encoding/json" "errors" "fmt" @@ -202,6 +203,17 @@ func (rc *NodeClient) TransactionByHash(txnHash string) (data *api.Transaction, return data, nil } +// Waits for a transaction to be confirmed by its hash. +// This function allows you to monitor the status of a transaction until it is finalized. +func (rc *NodeClient) WaitTransactionByHash(txnHash string) (data *api.Transaction, err error) { + restUrl := rc.baseUrl.JoinPath("transactions/wait_by_hash", txnHash) + data, err = Get[*api.Transaction](rc, restUrl.String()) + if err != nil { + return data, fmt.Errorf("get transaction api err: %w", err) + } + return data, nil +} + // TransactionByVersion gets info on a transaction by version number // The transaction will have been committed. The response will not be of the type [api.PendingTransaction]. func (rc *NodeClient) TransactionByVersion(version uint64) (data *api.CommittedTransaction, err error) { @@ -316,22 +328,50 @@ func getTransactionPollOptions(defaultPeriod, defaultTimeout time.Duration, opti // Accepts options PollPeriod and PollTimeout which should wrap time.Duration values. // Not just a degenerate case of PollForTransactions, it may return additional information for the single transaction polled. func (rc *NodeClient) PollForTransaction(hash string, options ...any) (*api.UserTransaction, error) { + // Check if the transaction is already done + txn, err := rc.TransactionByHash(hash) + if err != nil { + return nil, err + } + if txn.Type == api.TransactionVariantUser { + return txn.UserTransaction() + } + + // Wait for the transaction to be done + txn, err = rc.WaitTransactionByHash(hash) + if err != nil { + return nil, err + } + if txn.Type == api.TransactionVariantUser { + return txn.UserTransaction() + } + + // Poll for the transaction to be done period, timeout, err := getTransactionPollOptions(100*time.Millisecond, 10*time.Second, options...) if err != nil { return nil, err } - start := time.Now() - deadline := start.Add(timeout) + + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + ticker := time.NewTicker(period) + defer ticker.Stop() + for { - if time.Now().After(deadline) { + select { + case <-ctx.Done(): return nil, errors.New("PollForTransaction timeout") - } - time.Sleep(period) - txn, err := rc.TransactionByHash(hash) - if err == nil { - if txn.Type == api.TransactionVariantPending { + case <-ticker.C: + txn, err := rc.TransactionByHash(hash) + if err != nil { + continue + } + switch txn.Type { + case api.TransactionVariantPending: // not done yet! - } else if txn.Type == api.TransactionVariantUser { + continue + case api.TransactionVariantUser: // done! slog.Debug("txn done", "hash", hash) return txn.UserTransaction() From 4d4937de44c2bf938d5468c530deab53de1b3768 Mon Sep 17 00:00:00 2001 From: ice-coldbell Date: Fri, 28 Feb 2025 13:22:59 +0900 Subject: [PATCH 2/3] Remove redundant initial transaction check in PollForTransaction, directly calling WaitTransactionByHash --- nodeClient.go | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/nodeClient.go b/nodeClient.go index 6b343fc..509ae34 100644 --- a/nodeClient.go +++ b/nodeClient.go @@ -328,17 +328,8 @@ func getTransactionPollOptions(defaultPeriod, defaultTimeout time.Duration, opti // Accepts options PollPeriod and PollTimeout which should wrap time.Duration values. // Not just a degenerate case of PollForTransactions, it may return additional information for the single transaction polled. func (rc *NodeClient) PollForTransaction(hash string, options ...any) (*api.UserTransaction, error) { - // Check if the transaction is already done - txn, err := rc.TransactionByHash(hash) - if err != nil { - return nil, err - } - if txn.Type == api.TransactionVariantUser { - return txn.UserTransaction() - } - // Wait for the transaction to be done - txn, err = rc.WaitTransactionByHash(hash) + txn, err := rc.WaitTransactionByHash(hash) if err != nil { return nil, err } From f4537bba2c0bf03d31f1c811c3acffb13704c4d3 Mon Sep 17 00:00:00 2001 From: ice-coldbell Date: Fri, 28 Feb 2025 13:28:41 +0900 Subject: [PATCH 3/3] Simplify PollForTransaction error handling and user transaction extraction --- nodeClient.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/nodeClient.go b/nodeClient.go index 509ae34..a277ce0 100644 --- a/nodeClient.go +++ b/nodeClient.go @@ -330,10 +330,7 @@ func getTransactionPollOptions(defaultPeriod, defaultTimeout time.Duration, opti func (rc *NodeClient) PollForTransaction(hash string, options ...any) (*api.UserTransaction, error) { // Wait for the transaction to be done txn, err := rc.WaitTransactionByHash(hash) - if err != nil { - return nil, err - } - if txn.Type == api.TransactionVariantUser { + if err == nil && txn.Type == api.TransactionVariantUser { return txn.UserTransaction() }