Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes for the deep scenarios #54

Merged
merged 10 commits into from
Apr 24, 2024
2 changes: 0 additions & 2 deletions pkg/evilwallet/customscenarios.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,6 @@ func Scenario1() EvilBatch {
[]ScenarioAlias{
{Inputs: []string{"2"}, Outputs: []string{"4"}},
{Inputs: []string{"2"}, Outputs: []string{"5"}},
},
[]ScenarioAlias{
{Inputs: []string{"3"}, Outputs: []string{"6"}},
{Inputs: []string{"3"}, Outputs: []string{"7"}},
},
Expand Down
79 changes: 48 additions & 31 deletions pkg/evilwallet/evilwallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,20 @@ func (e *EvilWallet) PrepareAndPostBlockWithPayload(ctx context.Context, clt mod
return blockID, nil
}

func (e *EvilWallet) PrepareAndPostBlockWithTxBuildDataAwait(ctx context.Context, clt models.Client, txBuilder *builder.TransactionBuilder, issuer wallet.Account) (iotago.BlockID, error) {
blockID, _, err := e.PrepareAndPostBlockWithTxBuildData(ctx, clt, txBuilder, issuer)
if err != nil {
return iotago.EmptyBlockID, err
}

err = utils.AwaitBlockAcceptance(ctx, e.Logger, clt, blockID)
if err != nil {
return iotago.EmptyBlockID, err
}

return blockID, nil
}

func (e *EvilWallet) PrepareAndPostBlockWithTxBuildData(ctx context.Context, clt models.Client, txBuilder *builder.TransactionBuilder, issuer wallet.Account) (iotago.BlockID, *iotago.Transaction, error) {
congestionResp, issuerResp, version, err := e.accManager.RequestBlockIssuanceData(ctx, clt, issuer)
if err != nil {
Expand Down Expand Up @@ -203,17 +217,13 @@ func (e *EvilWallet) ClearAllAliases() {
e.aliasManager.ClearAllAliases()
}

func (e *EvilWallet) PrepareCustomConflicts(ctx context.Context, conflictsMaps []ConflictSlice) (conflictBatch [][]*models.PayloadIssuanceData, err error) {
for _, conflictMap := range conflictsMaps {
var txsData []*models.PayloadIssuanceData
for _, conflictOptions := range conflictMap {
issuanceData, err2 := e.CreateTransaction(ctx, conflictOptions...)
if err2 != nil {
return nil, err2
}
txsData = append(txsData, issuanceData)
func (e *EvilWallet) PrepareCustomConflicts(ctx context.Context, conflicts ConflictSlice) (conflictBatch []*models.PayloadIssuanceData, err error) {
for _, conflictOptions := range conflicts {
issuanceData, err2 := e.CreateTransaction(ctx, conflictOptions...)
if err2 != nil {
return nil, err2
}
conflictBatch = append(conflictBatch, txsData)
conflictBatch = append(conflictBatch, issuanceData)
}

return conflictBatch, nil
Expand Down Expand Up @@ -242,32 +252,32 @@ func (e *EvilWallet) CreateTransaction(ctx context.Context, options ...Option) (
return nil, err
}

outputs, addrAliasMap, tempAddresses, err := e.prepareOutputs(ctx, buildOptions, tempWallet)
outputs, tempIDAlias, tempAddresses, err := e.prepareOutputs(ctx, buildOptions, tempWallet)
if err != nil {
return nil, err
}

alias, remainder, hasRemainder := e.prepareRemainderOutput(inputs, outputs)
if hasRemainder {
outputs = append(outputs, remainder)
if alias != "" && addrAliasMap != nil {
if alias != "" && tempIDAlias != nil {
tempID := lo.PanicOnErr(models.NewTempOutputID(e.connector.GetClient().LatestAPI(), remainder))
addrAliasMap[tempID] = alias
tempIDAlias[tempID] = alias
}
}
issuanceData := &models.PayloadIssuanceData{
Type: iotago.PayloadSignedTransaction,
TransactionBuilder: e.prepareTransactionBuild(inputs, outputs, buildOptions.inputWallet),
}

addedOutputs := e.addOutputsToOutputManager(outputs, buildOptions.outputWallet, tempWallet, tempAddresses)
e.registerOutputAliases(addedOutputs, addrAliasMap)
addedOutputs := e.addOutputsToOutputManager(outputs, buildOptions.outputWallet, tempWallet, tempAddresses, tempIDAlias)
e.registerOutputAliases(addedOutputs, tempIDAlias)

return issuanceData, nil
}

// addOutputsToOutputManager adds output to the OutputManager.
func (e *EvilWallet) addOutputsToOutputManager(outputs []iotago.Output, outWallet, tmpWallet *Wallet, tempAddresses map[string]types.Empty) []*models.OutputData {
func (e *EvilWallet) addOutputsToOutputManager(outputs []iotago.Output, outWallet, tmpWallet *Wallet, tempAddresses map[string]types.Empty, tempIDAliasMap map[models.TempOutputID]string) []*models.OutputData {
modelOutputs := make([]*models.OutputData, 0)
for _, out := range outputs {
if out.UnlockConditionSet().Address() == nil {
Expand All @@ -282,6 +292,9 @@ func (e *EvilWallet) addOutputsToOutputManager(outputs []iotago.Output, outWalle
//only outputs that are not used in the scenario structure are added to the outWaller and can be reused.
if _, ok := tempAddresses[addr.String()]; ok {
output = e.outputManager.AddOutput(e.connector.GetClient().LatestAPI(), tmpWallet, out)
if _, found := tempIDAliasMap[output.TempID]; !found {
panic("tempID is different than provided in tempIDAliasMap")
}
} else {
output = e.outputManager.AddOutput(e.connector.GetClient().LatestAPI(), outWallet, out)
}
Expand Down Expand Up @@ -325,6 +338,9 @@ func (e *EvilWallet) registerOutputAliases(outputs []*models.OutputData, idAlias

for _, out := range outputs {
tempID := lo.PanicOnErr(models.NewTempOutputID(e.connector.GetClient().LatestAPI(), out.OutputStruct))
if tempID != out.TempID {
panic("tempID is different than provided in output")
}
// register output alias
e.aliasManager.AddOutputAlias(out, idAliasMap[tempID])

Expand Down Expand Up @@ -416,37 +432,40 @@ func (e *EvilWallet) useFreshIfInputWalletNotProvided(buildOptions *Options) (*W
// Thus, they are tracker in address to alias map. If the scenario is used, the outputBatchAliases map is provided
// that indicates which outputs should be saved to the outputWallet. All other outputs are created with temporary wallet,
// and their addresses are stored in tempAddresses.
func (e *EvilWallet) matchOutputsWithAliases(ctx context.Context, buildOptions *Options, tempWallet *Wallet) ([]iotago.Output, map[models.TempOutputID]string, map[string]types.Empty, error) {
func (e *EvilWallet) matchOutputsWithAliases(ctx context.Context, buildOptions *Options, batchWallet *Wallet) ([]iotago.Output, map[models.TempOutputID]string, map[string]types.Empty, error) {
if err := e.updateOutputBalances(ctx, buildOptions); err != nil {
return nil, nil, nil, err
}

outputs := make([]iotago.Output, 0)
idAliasMap := make(map[models.TempOutputID]string)
tempAddresses := make(map[string]types.Empty)
batchReuseAddresses := make(map[string]types.Empty)
for alias, output := range buildOptions.aliasOutputs {
var addr *iotago.Ed25519Address
if _, ok := buildOptions.outputBatchAliases[alias]; ok {
addr = buildOptions.outputWallet.Address()
} else {
addr = tempWallet.Address()
tempAddresses[addr.String()] = types.Void
} else { // case for inner batch reuse, this output will be consumed in some of the next transaction of this batch
addr = batchWallet.Address()
batchReuseAddresses[addr.String()] = types.Void
}

var builtAgainOutput iotago.Output
switch output.Type() {
case iotago.OutputBasic:
outputBuilder := builder.NewBasicOutputBuilder(addr, output.BaseTokenAmount()).
Mana(output.StoredMana())
outputs = append(outputs, outputBuilder.MustBuild())
// TODO why do we build for the second time? Maybe we can reuse the one built in NewOptions
builtAgainOutput = outputBuilder.MustBuild()
outputs = append(outputs, builtAgainOutput)
case iotago.OutputAccount:
outputBuilder := builder.NewAccountOutputBuilder(addr, output.BaseTokenAmount())
outputs = append(outputs, outputBuilder.MustBuild())
builtAgainOutput = outputBuilder.MustBuild()
outputs = append(outputs, builtAgainOutput)
}
tempID := lo.PanicOnErr(models.NewTempOutputID(e.connector.GetClient().LatestAPI(), output))
tempID := lo.PanicOnErr(models.NewTempOutputID(e.connector.GetClient().LatestAPI(), builtAgainOutput))
idAliasMap[tempID] = alias
}

return outputs, idAliasMap, tempAddresses, nil
return outputs, idAliasMap, batchReuseAddresses, nil
}

func (e *EvilWallet) prepareRemainderOutput(inputs []*models.OutputData, outputs []iotago.Output) (alias string, remainderOutput iotago.Output, added bool) {
Expand Down Expand Up @@ -562,11 +581,8 @@ func (e *EvilWallet) prepareTransactionBuild(inputs []*models.OutputData, output
return txBuilder
}

func (e *EvilWallet) PrepareCustomConflictsSpam(ctx context.Context, scenario *EvilScenario) (txsData [][]*models.PayloadIssuanceData, allAliases ScenarioAlias, err error) {
conflicts, allAliases := e.prepareConflictSliceForScenario(scenario)
txsData, err = e.PrepareCustomConflicts(ctx, conflicts)

return txsData, allAliases, err
func (e *EvilWallet) PrepareCustomConflictsSpam(scenario *EvilScenario) ([]ConflictSlice, ScenarioAlias) {
return e.prepareConflictSliceForScenario(scenario)
}

func (e *EvilWallet) PrepareAccountSpam(ctx context.Context, scenario *EvilScenario) (*models.PayloadIssuanceData, ScenarioAlias, error) {
Expand Down Expand Up @@ -594,6 +610,7 @@ func (e *EvilWallet) prepareConflictSliceForScenario(scenario *EvilScenario) ([]
conflicts := make([][]Option, 0)
for _, aliases := range conflictMap {
outs := genOutputOptions(aliases.Outputs)
// WithOutputBatchAliases indicates outputs that can be added to reuse wallet
option := []Option{WithInputs(aliases.Inputs), WithOutputs(outs), WithOutputBatchAliases(batchOutputs)}
if scenario.OutputWallet != nil {
option = append(option, WithOutputWallet(scenario.OutputWallet))
Expand Down
14 changes: 9 additions & 5 deletions pkg/spammer/spammer.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ func (s *Spammer) Spam(ctx context.Context) {
s.LogInfo("Maximum spam duration exceeded, stopping spammer....")
return
case <-s.State.spamTicker.C:
if goroutineCount.Load() > 100 {
if goroutineCount.Load() > 1500 {
break
}
go func(newContext context.Context, s *Spammer) {
Expand Down Expand Up @@ -243,7 +243,7 @@ func (s *Spammer) PrepareBlock(ctx context.Context, issuanceData *models.Payload
}
block, err := s.EvilWallet.CreateBlock(ctx, clt, issuanceData.Payload, issuerAccount, strongParents...)
if err != nil {
s.logError(ierrors.Wrap(err, ErrFailPostBlock.Error()))
s.LogDebugf("failed at PrepareBlock: %v", ierrors.Wrap(err, ErrFailPostBlock.Error()))
s.ErrCounter.CountError(ierrors.Wrap(err, ErrFailPostBlock.Error()))

return nil
Expand All @@ -252,7 +252,7 @@ func (s *Spammer) PrepareBlock(ctx context.Context, issuanceData *models.Payload
return block
}

func (s *Spammer) PrepareAndPostBlock(ctx context.Context, issuanceData *models.PayloadIssuanceData, clt models.Client) iotago.BlockID {
func (s *Spammer) PrepareAndPostBlock(ctx context.Context, issuanceData *models.PayloadIssuanceData, clt models.Client, awaitAcceptance ...bool) iotago.BlockID {
if issuanceData.Payload == nil && issuanceData.TransactionBuilder == nil {
s.logError(ErrPayloadIsNil)
s.ErrCounter.CountError(ErrPayloadIsNil)
Expand All @@ -273,7 +273,11 @@ func (s *Spammer) PrepareAndPostBlock(ctx context.Context, issuanceData *models.
case iotago.PayloadTaggedData:
blockID, err = s.EvilWallet.PrepareAndPostBlockWithPayload(ctx, clt, issuanceData.Payload, issuerAccount)
case iotago.PayloadSignedTransaction:
blockID, _, err = s.EvilWallet.PrepareAndPostBlockWithTxBuildData(ctx, clt, issuanceData.TransactionBuilder, issuerAccount)
if awaitAcceptance != nil && awaitAcceptance[0] {
blockID, err = s.EvilWallet.PrepareAndPostBlockWithTxBuildDataAwait(ctx, clt, issuanceData.TransactionBuilder, issuerAccount)
} else {
blockID, _, err = s.EvilWallet.PrepareAndPostBlockWithTxBuildData(ctx, clt, issuanceData.TransactionBuilder, issuerAccount)
}
default:
// unknown payload type
s.logError(ErrUnknownPayloadType)
Expand All @@ -283,7 +287,7 @@ func (s *Spammer) PrepareAndPostBlock(ctx context.Context, issuanceData *models.
}

if err != nil {
s.logError(ierrors.Wrap(err, ErrFailPostBlock.Error()))
// for the conflicting spams we will see errors and that's fine, so that's why we are not printing them
s.ErrCounter.CountError(ierrors.Wrap(err, ErrFailPostBlock.Error()))

return iotago.EmptyBlockID
Expand Down
20 changes: 12 additions & 8 deletions pkg/spammer/spamming_functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"sync"
"time"

"github.com/iotaledger/evil-tools/pkg/evilwallet"
"github.com/iotaledger/evil-tools/pkg/models"
"github.com/iotaledger/evil-tools/pkg/utils"
"github.com/iotaledger/hive.go/ierrors"
Expand All @@ -32,15 +33,18 @@ func DataSpammingFunction(ctx context.Context, s *Spammer) error {
}

func CustomConflictSpammingFunc(ctx context.Context, s *Spammer) error {
conflictBatch, aliases, err := s.EvilWallet.PrepareCustomConflictsSpam(ctx, s.EvilScenario)
if err != nil {
s.LogDebug(ierrors.Wrap(ErrFailToPrepareBatch, err.Error()).Error())
s.ErrCounter.CountError(ierrors.Wrap(ErrFailToPrepareBatch, err.Error()))
conflictBatch, aliases := s.EvilWallet.PrepareCustomConflictsSpam(s.EvilScenario)

return err
}
s.Logger.LogDebugf("Check the fresh output consumption: Starting next batch, Unspent outputs: %d\n", s.EvilWallet.UnspentOutputsLeft(evilwallet.Fresh))
for _, conflictSlice := range conflictBatch {
payloadsIssuanceData, err := s.EvilWallet.PrepareCustomConflicts(ctx, conflictSlice)
if err != nil {
s.LogDebug(ierrors.Wrap(ErrFailToPrepareBatch, err.Error()).Error())
s.ErrCounter.CountError(ierrors.Wrap(ErrFailToPrepareBatch, err.Error()))

return err
}

for _, payloadsIssuanceData := range conflictBatch {
clients := s.Clients.GetClients(len(payloadsIssuanceData))
if len(payloadsIssuanceData) > len(clients) {
s.LogDebug(ErrFailToPrepareBatch.Error())
Expand All @@ -58,7 +62,7 @@ func CustomConflictSpammingFunc(ctx context.Context, s *Spammer) error {
//nolint:gosec
time.Sleep(time.Duration(rand.Float64()*100) * time.Millisecond)

s.PrepareAndPostBlock(ctx, tx, clt)
s.PrepareAndPostBlock(ctx, tx, clt, true)
}(clients[i], issuanceData)
}
wg.Wait()
Expand Down
26 changes: 26 additions & 0 deletions pkg/utils/await.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,32 @@ func AwaitBlockWithTransactionToBeAccepted(ctx context.Context, logger log.Logge
return ierrors.Errorf("Transaction %s not accepted in time", txID)
}

func AwaitBlockAcceptance(ctx context.Context, logger log.Logger, clt models.Client, blockID iotago.BlockID) error {
logger.LogDebugf("Await for the acceptance of block %s", blockID.String())
for t := time.Now(); time.Since(t) < MaxAcceptanceAwait; time.Sleep(AwaitInterval) {
resp, err := clt.GetBlockConfirmationState(ctx, blockID)
if err != nil {
logger.LogDebugf("Failed to get block confirmation state: %s", err)

continue
}

if isBlockStateAtLeastAccepted(resp.BlockState) {
logger.LogDebugf("Block %s issuance success, status: %s", blockID.ToHex(), resp.BlockState)

return nil
}

if isBlockStateFailure(resp.BlockState) {
logger.LogDebugf("Block %s issuance failure, block failure reason: %s", blockID.ToHex(), resp.BlockState.String())

return ierrors.Errorf("block %s issuance failure, block failure reason: %s", blockID.ToHex(), resp.BlockState.String())
}
}

return ierrors.Errorf("failed to await block %s acceptance", blockID.ToHex())
}

// AwaitAddressUnspentOutputToBeAccepted awaits for acceptance of an output created for an address, based on the status of the transaction.
func AwaitAddressUnspentOutputToBeAccepted(ctx context.Context, logger log.Logger, clt models.Client, addr iotago.Address) (outputID iotago.OutputID, output iotago.Output, err error) {
addrBech := addr.Bech32(clt.CommittedAPI().ProtocolParameters().Bech32HRP())
Expand Down
4 changes: 0 additions & 4 deletions pkg/walletmanager/faucet.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,15 +93,11 @@ func (m *Manager) RequestFaucetFunds(ctx context.Context, clt models.Client, rec
func (m *Manager) PostWithBlock(ctx context.Context, clt models.Client, payload iotago.Payload, issuer wallet.Account, congestionResp *api.CongestionResponse, issuerResp *api.IssuanceBlockHeaderResponse, version iotago.Version, strongParents ...iotago.BlockID) (iotago.BlockID, error) {
signedBlock, err := m.CreateBlock(clt, payload, issuer, congestionResp, issuerResp, version, strongParents...)
if err != nil {
m.LogErrorf("failed to create block: %s", err)

return iotago.EmptyBlockID, err
}

blockID, err := clt.PostBlock(ctx, signedBlock)
if err != nil {
m.LogErrorf("failed to post block: %s", err)

return iotago.EmptyBlockID, err
}

Expand Down