Skip to content

Commit

Permalink
Merge pull request #14 from iotaledger/refactor-await
Browse files Browse the repository at this point in the history
Refactor await methods
  • Loading branch information
jkrvivian authored Nov 2, 2023
2 parents 0211982 + 9907343 commit bfe298f
Show file tree
Hide file tree
Showing 10 changed files with 172 additions and 205 deletions.
2 changes: 1 addition & 1 deletion accountwallet/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ var accountConfigFile = "config.json"

var (
dockerAccountConfigJSON = `{
"bindAddress": "http://localhost:8080",
"bindAddress": "http://localhost:8050",
"faucetBindAddress": "http://localhost:8088",
"accountStatesFile": "wallet.dat",
"genesisSeed": "7R1itJx5hVuo9w9hjg5cwKFmek4HMSoBDgJZN8hKGxih",
Expand Down
29 changes: 3 additions & 26 deletions accountwallet/faucet.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/mr-tron/base58"

"github.com/iotaledger/evil-tools/models"
"github.com/iotaledger/evil-tools/utils"
"github.com/iotaledger/hive.go/ierrors"
"github.com/iotaledger/hive.go/lo"
"github.com/iotaledger/iota-core/pkg/blockhandler"
Expand Down Expand Up @@ -39,38 +40,14 @@ func (a *AccountWallet) RequestBlockBuiltData(clt *nodeclient.Client, issuerID i
}

func (a *AccountWallet) RequestFaucetFunds(clt models.Client, receiveAddr iotago.Address) (*models.Output, error) {
//nolint:all,forcetypassert
err := clt.RequestFaucetFunds(receiveAddr)
if err != nil {
return nil, ierrors.Wrap(err, "failed to request funds from faucet")
}

indexer, err := clt.Indexer()
outputID, outputStruct, err := utils.AwaitAddressUnspentOutputToBeAccepted(clt, receiveAddr)
if err != nil {
return nil, ierrors.Wrap(err, "failed to get indexer client")
}

addrBech := receiveAddr.Bech32(clt.CommittedAPI().ProtocolParameters().Bech32HRP())

time.Sleep(10 * time.Second)

res, err := indexer.Outputs(context.Background(), &apimodels.BasicOutputsQuery{
AddressBech32: addrBech,
})
if err != nil {
return nil, ierrors.Wrap(err, "indexer request failed in request faucet funds")
}

var outputStruct iotago.Output
var outputID iotago.OutputID
for res.Next() {
unspents, err := res.Outputs(context.TODO())
if err != nil {
return nil, ierrors.Wrap(err, "failed to get faucet unspent outputs")
}

outputStruct = unspents[0]
outputID = lo.Return1(res.Response.Items.OutputIDs())[0]
return nil, ierrors.Wrap(err, "failed to await faucet funds")
}

return &models.Output{
Expand Down
2 changes: 1 addition & 1 deletion config.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
// Nodes used during the test, use at least two nodes to be able to double spend.
var (
// urls = []string{"http://bootstrap-01.feature.shimmer.iota.cafe:8080", "http://vanilla-01.feature.shimmer.iota.cafe:8080", "http://drng-01.feature.shimmer.iota.cafe:8080"}
urls = []string{"http://localhost:8080", "http://localhost:8090"} //, "http://localhost:8070", "http://localhost:8040"}
urls = []string{"http://localhost:8050", "http://localhost:8060"} //, "http://localhost:8070", "http://localhost:8040"}
)

var (
Expand Down
14 changes: 6 additions & 8 deletions evilwallet/evilwallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/iotaledger/evil-tools/accountwallet"
evillogger "github.com/iotaledger/evil-tools/logger"
"github.com/iotaledger/evil-tools/models"
"github.com/iotaledger/evil-tools/utils"
"github.com/iotaledger/hive.go/ds/types"
"github.com/iotaledger/hive.go/ierrors"
"github.com/iotaledger/hive.go/lo"
Expand All @@ -24,13 +25,10 @@ const (
// FaucetRequestSplitNumber defines the number of outputs to split from a faucet request.
FaucetRequestSplitNumber = 120
faucetTokensPerRequest iotago.BaseToken = 432_000_000

waitForAcceptance = 20 * time.Second
awaitAcceptationSleep = 1 * time.Second
)

var (
defaultClientsURLs = []string{"http://localhost:8080", "http://localhost:8090"}
defaultClientsURLs = []string{"http://localhost:8050", "http://localhost:8060"}
defaultFaucetURL = "http://localhost:8088"
)

Expand Down Expand Up @@ -278,14 +276,14 @@ func (e *EvilWallet) requestAndSplitFaucetFunds(initWallet, receiveWallet *Walle

func (e *EvilWallet) requestFaucetFunds(wallet *Wallet) (output *models.Output, err error) {
receiveAddr := wallet.AddressOnIndex(0)
clt := e.connector.GetClient()
clt := e.connector.GetIndexerClient()

err = clt.RequestFaucetFunds(receiveAddr)
if err != nil {
return nil, ierrors.Wrap(err, "failed to request funds from faucet")
}

outputID, iotaOutput, err := e.outputManager.AwaitAddressUnspentOutputToBeAccepted(receiveAddr, 10*time.Second)
outputID, iotaOutput, err := utils.AwaitAddressUnspentOutputToBeAccepted(clt, receiveAddr)
if err != nil {
return nil, ierrors.Wrap(err, "failed to await faucet output acceptance")
}
Expand Down Expand Up @@ -371,7 +369,7 @@ func (e *EvilWallet) splitOutputs(inputWallet, outputWallet *Wallet) ([]iotago.T
}

func (e *EvilWallet) createSplitOutputs(input *models.Output, splitNumber int, receiveWallet *Wallet) []*OutputOption {
balances := SplitBalanceEqually(splitNumber, input.Balance)
balances := utils.SplitBalanceEqually(splitNumber, input.Balance)
outputs := make([]*OutputOption, splitNumber)
for i, bal := range balances {
outputs[i] = &OutputOption{amount: bal, address: receiveWallet.Address(), outputType: iotago.OutputBasic}
Expand Down Expand Up @@ -744,7 +742,7 @@ func (e *EvilWallet) updateOutputBalances(buildOptions *Options) (err error) {
totalBalance += in.Balance
}
}
balances := SplitBalanceEqually(len(buildOptions.outputs)+len(buildOptions.aliasOutputs), totalBalance)
balances := utils.SplitBalanceEqually(len(buildOptions.outputs)+len(buildOptions.aliasOutputs), totalBalance)
i := 0
for out, output := range buildOptions.aliasOutputs {
switch output.Type() {
Expand Down
132 changes: 7 additions & 125 deletions evilwallet/output_manager.go
Original file line number Diff line number Diff line change
@@ -1,24 +1,16 @@
package evilwallet

import (
"context"
"sync"
"time"

"go.uber.org/atomic"

"github.com/iotaledger/evil-tools/models"
"github.com/iotaledger/evil-tools/utils"
"github.com/iotaledger/hive.go/ds/types"
"github.com/iotaledger/hive.go/ierrors"
"github.com/iotaledger/hive.go/lo"
"github.com/iotaledger/hive.go/logger"
"github.com/iotaledger/hive.go/runtime/syncutils"
iotago "github.com/iotaledger/iota.go/v4"
"github.com/iotaledger/iota.go/v4/nodeclient/apimodels"
)

const (
awaitOutputToBeConfirmed = 10 * time.Second
)

// OutputManager keeps track of the output statuses.
Expand Down Expand Up @@ -117,13 +109,13 @@ func (o *OutputManager) Track(outputIDs ...iotago.OutputID) (allConfirmed bool)
for _, ID := range outputIDs {
wg.Add(1)

go func(id iotago.OutputID) {
go func(id iotago.OutputID, clt models.Client) {
defer wg.Done()

if !o.AwaitOutputToBeAccepted(id, awaitOutputToBeConfirmed) {
if !utils.AwaitOutputToBeAccepted(clt, id) {
unconfirmedOutputFound.Store(true)
}
}(ID)
}(ID, o.connector.GetClient())
}
wg.Wait()

Expand Down Expand Up @@ -206,22 +198,6 @@ func (o *OutputManager) getOutputFromWallet(outputID iotago.OutputID) (output *m
return
}

// RequestOutputsByTxID adds the outputs of a given transaction to the output status map.
func (o *OutputManager) RequestOutputsByTxID(txID iotago.TransactionID) (outputIDs iotago.OutputIDs) {
clt := o.connector.GetClient()

tx, err := clt.GetTransaction(txID)
if err != nil {
return
}

for index := range tx.Transaction.Outputs {
outputIDs = append(outputIDs, iotago.OutputIDFromTransactionIDAndIndex(txID, uint16(index)))
}

return outputIDs
}

// AwaitWalletOutputsToBeConfirmed awaits for all outputs in the wallet are confirmed.
func (o *OutputManager) AwaitWalletOutputsToBeConfirmed(wallet *Wallet) {
wg := sync.WaitGroup{}
Expand All @@ -243,59 +219,6 @@ func (o *OutputManager) AwaitWalletOutputsToBeConfirmed(wallet *Wallet) {
wg.Wait()
}

// AwaitOutputToBeAccepted awaits for output from a provided outputID is accepted. Timeout is waitFor.
// Useful when we have only an address and no transactionID, e.g. faucet funds request.
func (o *OutputManager) AwaitOutputToBeAccepted(outputID iotago.OutputID, waitFor time.Duration) (accepted bool) {
s := time.Now()
clt := o.connector.GetClient()
accepted = false
for ; time.Since(s) < waitFor; time.Sleep(awaitAcceptationSleep) {
confirmationState := clt.GetOutputConfirmationState(outputID)
if confirmationState == "confirmed" {
accepted = true
break
}
}

return accepted
}

func (o *OutputManager) AwaitAddressUnspentOutputToBeAccepted(addr *iotago.Ed25519Address, waitFor time.Duration) (outputID iotago.OutputID, output iotago.Output, err error) {
clt := o.connector.GetIndexerClient()
indexer, err := clt.Indexer()
if err != nil {
return iotago.EmptyOutputID, nil, ierrors.Wrap(err, "failed to get indexer client")
}

s := time.Now()
addrBech := addr.Bech32(clt.CommittedAPI().ProtocolParameters().Bech32HRP())

for ; time.Since(s) < waitFor; time.Sleep(awaitAcceptationSleep) {
res, err := indexer.Outputs(context.Background(), &apimodels.BasicOutputsQuery{
AddressBech32: addrBech,
})
if err != nil {
return iotago.EmptyOutputID, nil, ierrors.Wrap(err, "indexer request failed in request faucet funds")
}

for res.Next() {
unspents, err := res.Outputs(context.TODO())
if err != nil {
return iotago.EmptyOutputID, nil, ierrors.Wrap(err, "failed to get faucet unspent outputs")
}

if len(unspents) == 0 {
o.log.Debugf("no unspent outputs found in indexer for address: %s", addrBech)
break
}

return lo.Return1(res.Response.Items.OutputIDs())[0], unspents[0], nil
}
}

return iotago.EmptyOutputID, nil, ierrors.Errorf("no unspent outputs found for address %s due to timeout", addrBech)
}

// AwaitTransactionsAcceptance awaits for transaction confirmation and updates wallet with outputIDs.
func (o *OutputManager) AwaitTransactionsAcceptance(txIDs ...iotago.TransactionID) {
wg := sync.WaitGroup{}
Expand All @@ -305,61 +228,20 @@ func (o *OutputManager) AwaitTransactionsAcceptance(txIDs ...iotago.TransactionI

for _, txID := range txIDs {
wg.Add(1)
go func(txID iotago.TransactionID) {
go func(txID iotago.TransactionID, clt models.Client) {
defer wg.Done()
semaphore <- true
defer func() {
<-semaphore
}()
err := o.AwaitTransactionToBeAccepted(txID, waitForAcceptance, txLeft)
err := utils.AwaitTransactionToBeAccepted(clt, txID, txLeft)
txLeft.Dec()
if err != nil {
o.log.Errorf("Error awaiting transaction %s to be accepted: %s", txID.String(), err)

return
}
}(txID)
}(txID, o.connector.GetClient())
}
wg.Wait()
}

// AwaitTransactionToBeAccepted awaits for acceptance of a single transaction.
func (o *OutputManager) AwaitTransactionToBeAccepted(txID iotago.TransactionID, waitFor time.Duration, txLeft *atomic.Int64) error {
s := time.Now()
clt := o.connector.GetClient()
var accepted bool
for ; time.Since(s) < waitFor; time.Sleep(awaitAcceptationSleep) {
resp, err := clt.GetBlockState(txID)
if resp == nil {
o.log.Debugf("Block state API error: %v", err)

continue
}
if resp.BlockState == apimodels.BlockStateFailed.String() || resp.BlockState == apimodels.BlockStateRejected.String() {
failureReason, _, _ := apimodels.BlockFailureReasonFromBytes(lo.PanicOnErr(resp.BlockFailureReason.Bytes()))

return ierrors.Errorf("tx %s failed because block failure: %d", txID, failureReason)
}

if resp.TransactionState == apimodels.TransactionStateFailed.String() {
failureReason, _, _ := apimodels.TransactionFailureReasonFromBytes(lo.PanicOnErr(resp.TransactionFailureReason.Bytes()))

return ierrors.Errorf("transaction %s failed: %d", txID, failureReason)
}

confirmationState := resp.TransactionState

o.log.Debugf("Tx %s confirmationState: %s, tx left: %d", txID.ToHex(), confirmationState, txLeft.Load())
if confirmationState == "accepted" || confirmationState == "confirmed" || confirmationState == "finalized" {
accepted = true
break
}
}
if !accepted {
return ierrors.Errorf("transaction %s not accepted in time", txID)
}

o.log.Debugf("Transaction %s accepted", txID)

return nil
}
23 changes: 0 additions & 23 deletions evilwallet/utils.go

This file was deleted.

2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ require (
github.com/AlecAivazis/survey/v2 v2.3.7
github.com/google/martian v2.1.0+incompatible
github.com/iotaledger/hive.go/app v0.0.0-20231020115340-13da292c580b
github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231020115340-13da292c580b
github.com/iotaledger/hive.go/ds v0.0.0-20231020115340-13da292c580b
github.com/iotaledger/hive.go/ierrors v0.0.0-20231024193930-47c6046e38a8
github.com/iotaledger/hive.go/lo v0.0.0-20231020115340-13da292c580b
Expand Down Expand Up @@ -40,6 +39,7 @@ require (
github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7 // indirect
github.com/iotaledger/hive.go/ads v0.0.0-20231020115340-13da292c580b // indirect
github.com/iotaledger/hive.go/constraints v0.0.0-20231020115340-13da292c580b // indirect
github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231020115340-13da292c580b // indirect
github.com/iotaledger/hive.go/crypto v0.0.0-20231020115340-13da292c580b // indirect
github.com/iotaledger/hive.go/kvstore v0.0.0-20231020115340-13da292c580b // indirect
github.com/iotaledger/hive.go/log v0.0.0-20231020115340-13da292c580b // indirect
Expand Down
10 changes: 5 additions & 5 deletions models/connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,8 @@ type Client interface {
PostData(data []byte) (blkID string, err error)
// GetBlockConfirmationState returns the AcceptanceState of a given block ID.
GetBlockConfirmationState(blkID iotago.BlockID) string
// GetBlockState returns the AcceptanceState of a given transaction ID.
GetBlockState(txID iotago.TransactionID) (resp *apimodels.BlockMetadataResponse, err error)
// GetBlockStateFromTransaction returns the AcceptanceState of a given transaction ID.
GetBlockStateFromTransaction(txID iotago.TransactionID) (resp *apimodels.BlockMetadataResponse, err error)
// GetOutput gets the output of a given outputID.
GetOutput(outputID iotago.OutputID) iotago.Output
// GetOutputConfirmationState gets the first unspent outputs of a given address.
Expand Down Expand Up @@ -335,7 +335,7 @@ func (c *WebClient) PostData(data []byte) (blkID string, err error) {
// GetOutputConfirmationState gets the first unspent outputs of a given address.
func (c *WebClient) GetOutputConfirmationState(outputID iotago.OutputID) string {
txID := outputID.TransactionID()
resp, err := c.GetBlockState(txID)
resp, err := c.GetBlockStateFromTransaction(txID)
if err != nil {
return ""
}
Expand Down Expand Up @@ -363,8 +363,8 @@ func (c *WebClient) GetBlockConfirmationState(blkID iotago.BlockID) string {
return resp.BlockState
}

// GetBlockState returns the AcceptanceState of a given transaction ID.
func (c *WebClient) GetBlockState(txID iotago.TransactionID) (*apimodels.BlockMetadataResponse, error) {
// GetBlockStateFromTransaction returns the AcceptanceState of a given transaction ID.
func (c *WebClient) GetBlockStateFromTransaction(txID iotago.TransactionID) (*apimodels.BlockMetadataResponse, error) {
return c.client.TransactionIncludedBlockMetadata(context.Background(), txID)
}

Expand Down
Loading

0 comments on commit bfe298f

Please sign in to comment.