diff --git a/.github/workflows/build_tools.yml b/.github/workflows/build_tools.yml index 89c14f9dc..262c56563 100644 --- a/.github/workflows/build_tools.yml +++ b/.github/workflows/build_tools.yml @@ -3,7 +3,6 @@ name: Build internal tools on: pull_request: paths: - - 'tools/evil-spammer/**' - 'tools/genesis-snapshot/**' jobs: @@ -23,10 +22,6 @@ jobs: - name: Print Go version run: go version - - name: Build evil-spammer tool - working-directory: tools/evil-spammer - run: go mod tidy && go build . - - name: Build genesis-snapshot tool working-directory: tools/genesis-snapshot run: go mod tidy && go build . diff --git a/.gitignore b/.gitignore index b404776d6..fea8c46c9 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,3 @@ dist/ # snapshot and settings file *.bin tools/docker-network/docker-network.snapshot -tools/evil-spammer/evil-spammer - - diff --git a/.golangci.yml b/.golangci.yml index f9885c6fa..6000e9feb 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -2,8 +2,6 @@ run: tests: true skip-dirs: - components/dashboard - - tools/evilwallet - - tools/evil-spammer skip-files: - ".*_test.go$" - "testframework.go" diff --git a/scripts/go_mod_tidy.sh b/scripts/go_mod_tidy.sh index cd28ef1b6..1166ea010 100755 --- a/scripts/go_mod_tidy.sh +++ b/scripts/go_mod_tidy.sh @@ -11,8 +11,4 @@ pushd tools/genesis-snapshot go mod tidy popd -pushd tools/evil-spammer -go mod tidy -popd - popd \ No newline at end of file diff --git a/tools/evil-spammer/.gitignore b/tools/evil-spammer/.gitignore deleted file mode 100644 index 301d5d62d..000000000 --- a/tools/evil-spammer/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.log -*.json -*.dat \ No newline at end of file diff --git a/tools/evil-spammer/accountwallet/commands.go b/tools/evil-spammer/accountwallet/commands.go deleted file mode 100644 index 37a7843cb..000000000 --- a/tools/evil-spammer/accountwallet/commands.go +++ /dev/null @@ -1,40 +0,0 @@ -package accountwallet - -import ( - "fmt" - - "github.com/iotaledger/hive.go/ierrors" - iotago "github.com/iotaledger/iota.go/v4" -) - -func (a *AccountWallet) CreateAccount(params *CreateAccountParams) (iotago.AccountID, error) { - implicitAccountOutput, privateKey, err := a.getFunds(params.Amount, iotago.AddressImplicitAccountCreation) - if err != nil { - return iotago.EmptyAccountID, ierrors.Wrap(err, "Failed to create account") - } - - accountID := a.registerAccount(params.Alias, implicitAccountOutput.OutputID, a.latestUsedIndex, privateKey) - - fmt.Printf("Created account %s with %d tokens\n", accountID.ToHex(), params.Amount) - - return accountID, nil -} - -func (a *AccountWallet) DestroyAccount(params *DestroyAccountParams) error { - return a.destroyAccount(params.AccountAlias) -} - -func (a *AccountWallet) ListAccount() error { - fmt.Printf("%-10s \t%-33s\n\n", "Alias", "AccountID") - for _, accData := range a.accountsAliases { - fmt.Printf("%-10s \t", accData.Alias) - fmt.Printf("%-33s ", accData.Account.ID().ToHex()) - fmt.Printf("\n") - } - - return nil -} - -func (a *AccountWallet) AllotToAccount(params *AllotAccountParams) error { - return nil -} diff --git a/tools/evil-spammer/accountwallet/config.go b/tools/evil-spammer/accountwallet/config.go deleted file mode 100644 index a363cb8ad..000000000 --- a/tools/evil-spammer/accountwallet/config.go +++ /dev/null @@ -1,219 +0,0 @@ -package accountwallet - -import ( - "encoding/json" - "os" - - "github.com/iotaledger/hive.go/ds/types" - "github.com/iotaledger/iota-core/tools/evil-spammer/models" -) - -// commands - -type AccountOperation int - -const ( - OperationCreateAccount AccountOperation = iota - OperationConvertAccount - OperationDestroyAccound - OperationAllotAccount - OperationDelegateAccount - OperationStakeAccount - OperationListAccounts - OperationUpdateAccount - - CmdNameCreateAccount = "create" - CmdNameConvertAccount = "convert" - CmdNameDestroyAccount = "destroy" - CmdNameAllotAccount = "allot" - CmdNameDelegateAccount = "delegate" - CmdNameStakeAccount = "stake" - CmdNameListAccounts = "list" - CmdNameUpdateAccount = "update" -) - -func (a AccountOperation) String() string { - return []string{ - CmdNameCreateAccount, - CmdNameConvertAccount, - CmdNameDestroyAccount, - CmdNameAllotAccount, - CmdNameDelegateAccount, - CmdNameStakeAccount, - CmdNameListAccounts, - CmdNameUpdateAccount, - }[a] -} - -func AvailableCommands(cmd string) bool { - availableCommands := map[string]types.Empty{ - CmdNameCreateAccount: types.Void, - CmdNameConvertAccount: types.Void, - CmdNameDestroyAccount: types.Void, - CmdNameAllotAccount: types.Void, - CmdNameDelegateAccount: types.Void, - CmdNameStakeAccount: types.Void, - CmdNameListAccounts: types.Void, - CmdNameUpdateAccount: types.Void, - } - - _, ok := availableCommands[cmd] - return ok -} - -type Configuration struct { - BindAddress string `json:"bindAddress,omitempty"` - AccountStatesFile string `json:"accountStatesFile,omitempty"` - GenesisSeed string `json:"genesisSeed,omitempty"` - BlockIssuerPrivateKey string `json:"blockIssuerPrivateKey,omitempty"` - AccountID string `json:"accountID,omitempty"` -} - -var accountConfigFile = "config.json" - -var ( - dockerAccountConfigJSON = `{ - "bindAddress": "http://localhost:8080", - "accountStatesFile": "wallet.dat", - "genesisSeed": "7R1itJx5hVuo9w9hjg5cwKFmek4HMSoBDgJZN8hKGxih", - "blockIssuerPrivateKey": "db39d2fde6301d313b108dc9db1ee724d0f405f6fde966bd776365bc5f4a5fb31e4b21eb51dcddf65c20db1065e1f1514658b23a3ddbf48d30c0efc926a9a648", - "accountID": "0x6aee704f25558e8aa7630fed0121da53074188abc423b3c5810f80be4936eb6e"}` -) - -// LoadConfiguration loads the config file. -func LoadConfiguration() *Configuration { - // open config file - config := new(Configuration) - file, err := os.Open(accountConfigFile) - if err != nil { - if !os.IsNotExist(err) { - panic(err) - } - - //nolint:gosec // users should be able to read the file - if err = os.WriteFile(accountConfigFile, []byte(dockerAccountConfigJSON), 0o644); err != nil { - panic(err) - } - if file, err = os.Open(accountConfigFile); err != nil { - panic(err) - } - } - defer file.Close() - - // decode config file - if err = json.NewDecoder(file).Decode(config); err != nil { - panic(err) - } - - return config -} - -func SaveConfiguration(config *Configuration) { - // open config file - file, err := os.Open(accountConfigFile) - if err != nil { - panic(err) - } - defer file.Close() - - jsonConfigs, err := json.MarshalIndent(config, "", " ") - - if err != nil { - log.Errorf("failed to write configs to file %s", err) - } - - //nolint:gosec // users should be able to read the file - if err = os.WriteFile(accountConfigFile, jsonConfigs, 0o644); err != nil { - panic(err) - } -} - -type AccountSubcommands interface { - Type() AccountOperation -} - -type CreateAccountParams struct { - Alias string - Amount uint64 - NoBIF bool - Implicit bool -} - -func (c *CreateAccountParams) Type() AccountOperation { - return OperationCreateAccount -} - -type DestroyAccountParams struct { - AccountAlias string - ExpirySlot uint64 -} - -func (d *DestroyAccountParams) Type() AccountOperation { - return OperationDestroyAccound -} - -type AllotAccountParams struct { - Amount uint64 - To string - From string // if not set we use faucet -} - -func (a *AllotAccountParams) Type() AccountOperation { - return OperationAllotAccount -} - -type ConvertAccountParams struct { - AccountAlias string -} - -func (d *ConvertAccountParams) Type() AccountOperation { - return OperationConvertAccount -} - -type DelegateAccountParams struct { - Amount uint64 - To string - From string // if not set we use faucet -} - -func (a *DelegateAccountParams) Type() AccountOperation { - return OperationDelegateAccount -} - -type StakeAccountParams struct { - Alias string - Amount uint64 - FixedCost uint64 - StartEpoch uint64 - EndEpoch uint64 -} - -func (a *StakeAccountParams) Type() AccountOperation { - return OperationStakeAccount -} - -type UpdateAccountParams struct { - Alias string - BlockIssuerKey string - Mana uint64 - Amount uint64 - ExpirySlot uint64 -} - -func (a *UpdateAccountParams) Type() AccountOperation { - return OperationUpdateAccount -} - -type NoAccountParams struct { - Operation AccountOperation -} - -func (a *NoAccountParams) Type() AccountOperation { - return a.Operation -} - -type StateData struct { - Seed string `serix:"0,mapKey=seed,lengthPrefixType=uint8"` - LastUsedIndex uint64 `serix:"1,mapKey=lastUsedIndex"` - AccountsData []*models.AccountState `serix:"2,mapKey=accounts,lengthPrefixType=uint8"` -} diff --git a/tools/evil-spammer/accountwallet/faucet.go b/tools/evil-spammer/accountwallet/faucet.go deleted file mode 100644 index 6f67fdb8f..000000000 --- a/tools/evil-spammer/accountwallet/faucet.go +++ /dev/null @@ -1,284 +0,0 @@ -package accountwallet - -import ( - "context" - "fmt" - "sync" - "time" - - "github.com/mr-tron/base58" - - "github.com/iotaledger/hive.go/core/safemath" - "github.com/iotaledger/hive.go/ierrors" - "github.com/iotaledger/hive.go/lo" - "github.com/iotaledger/iota-core/pkg/blockhandler" - "github.com/iotaledger/iota-core/pkg/testsuite/mock" - "github.com/iotaledger/iota-core/tools/evil-spammer/models" - iotago "github.com/iotaledger/iota.go/v4" - "github.com/iotaledger/iota.go/v4/builder" - "github.com/iotaledger/iota.go/v4/nodeclient" - "github.com/iotaledger/iota.go/v4/nodeclient/apimodels" -) - -const ( - FaucetAccountAlias = "faucet" -) - -func (a *AccountWallet) RequestBlockBuiltData(clt *nodeclient.Client, issuerID iotago.AccountID) (*apimodels.CongestionResponse, *apimodels.IssuanceBlockHeaderResponse, iotago.Version, error) { - congestionResp, err := clt.Congestion(context.Background(), issuerID) - if err != nil { - return nil, nil, 0, ierrors.Wrapf(err, "failed to get congestion data for issuer %s", issuerID.ToHex()) - } - - issuerResp, err := clt.BlockIssuance(context.Background(), congestionResp.Slot) - if err != nil { - return nil, nil, 0, ierrors.Wrap(err, "failed to get block issuance data") - } - - version := clt.APIForSlot(congestionResp.Slot).Version() - - return congestionResp, issuerResp, version, nil -} - -func (a *AccountWallet) RequestFaucetFunds(clt models.Client, receiveAddr iotago.Address, amount iotago.BaseToken) (*models.Output, error) { - congestionResp, issuerResp, version, err := a.RequestBlockBuiltData(clt.Client(), a.faucet.account.ID()) - if err != nil { - return nil, ierrors.Wrapf(err, "failed to get block built data for issuer %s", a.faucet.account.ID().ToHex()) - } - - signedTx, err := a.faucet.prepareFaucetRequest(receiveAddr, amount, congestionResp.ReferenceManaCost) - if err != nil { - log.Errorf("failed to prepare faucet request: %s", err) - - return nil, err - } - - blkID, err := a.PostWithBlock(clt, signedTx, a.faucet.account, congestionResp, issuerResp, version) - if err != nil { - log.Errorf("failed to create block: %s", err) - - return nil, err - } - fmt.Println("block sent:", blkID.ToHex()) - - // set remainder output to be reused by the Faucet wallet - a.faucet.unspentOutput = &models.Output{ - OutputID: iotago.OutputIDFromTransactionIDAndIndex(lo.PanicOnErr(signedTx.Transaction.ID()), 1), - Address: a.faucet.genesisHdWallet.Address(iotago.AddressEd25519).(*iotago.Ed25519Address), - Index: 0, - Balance: signedTx.Transaction.Outputs[1].BaseTokenAmount(), - OutputStruct: signedTx.Transaction.Outputs[1], - } - - return &models.Output{ - OutputID: iotago.OutputIDFromTransactionIDAndIndex(lo.PanicOnErr(signedTx.Transaction.ID()), 0), - Address: receiveAddr, - Index: 0, - Balance: signedTx.Transaction.Outputs[0].BaseTokenAmount(), - OutputStruct: signedTx.Transaction.Outputs[0], - }, nil -} - -func (a *AccountWallet) PostWithBlock(clt models.Client, payload iotago.Payload, issuer blockhandler.Account, congestionResp *apimodels.CongestionResponse, issuerResp *apimodels.IssuanceBlockHeaderResponse, version iotago.Version) (iotago.BlockID, error) { - signedBlock, err := a.CreateBlock(payload, issuer, congestionResp, issuerResp, version) - if err != nil { - log.Errorf("failed to create block: %s", err) - - return iotago.EmptyBlockID, err - } - - blockID, err := clt.PostBlock(signedBlock) - if err != nil { - log.Errorf("failed to post block: %s", err) - - return iotago.EmptyBlockID, err - } - - return blockID, nil -} - -func (a *AccountWallet) CreateBlock(payload iotago.Payload, issuer blockhandler.Account, congestionResp *apimodels.CongestionResponse, issuerResp *apimodels.IssuanceBlockHeaderResponse, version iotago.Version) (*iotago.ProtocolBlock, error) { - issuingTime := time.Now() - issuingSlot := a.client.LatestAPI().TimeProvider().SlotFromTime(issuingTime) - apiForSlot := a.client.APIForSlot(issuingSlot) - - blockBuilder := builder.NewBasicBlockBuilder(apiForSlot) - - commitmentID, err := issuerResp.Commitment.ID() - if err != nil { - return nil, ierrors.Wrap(err, "failed to get commitment id") - } - - blockBuilder.ProtocolVersion(version) - blockBuilder.SlotCommitmentID(commitmentID) - blockBuilder.LatestFinalizedSlot(issuerResp.LatestFinalizedSlot) - blockBuilder.IssuingTime(time.Now()) - blockBuilder.StrongParents(issuerResp.StrongParents) - blockBuilder.WeakParents(issuerResp.WeakParents) - blockBuilder.ShallowLikeParents(issuerResp.ShallowLikeParents) - - blockBuilder.Payload(payload) - blockBuilder.CalculateAndSetMaxBurnedMana(congestionResp.ReferenceManaCost) - blockBuilder.Sign(issuer.ID(), issuer.PrivateKey()) - - blk, err := blockBuilder.Build() - if err != nil { - return nil, ierrors.Errorf("failed to build block: %w", err) - } - - return blk, nil -} - -type faucetParams struct { - faucetPrivateKey string - faucetAccountID string - genesisSeed string -} - -type faucet struct { - unspentOutput *models.Output - account blockhandler.Account - genesisHdWallet *mock.HDWallet - - clt models.Client - - sync.Mutex -} - -func newFaucet(clt models.Client, faucetParams *faucetParams) (*faucet, error) { - genesisSeed, err := base58.Decode(faucetParams.genesisSeed) - if err != nil { - log.Warnf("failed to decode base58 seed, using the default one: %v", err) - } - faucetAddr := mock.NewHDWallet("", genesisSeed, 0).Address(iotago.AddressEd25519) - - f := &faucet{ - clt: clt, - account: blockhandler.AccountFromParams(faucetParams.faucetAccountID, faucetParams.faucetPrivateKey), - genesisHdWallet: mock.NewHDWallet("", genesisSeed, 0), - } - - faucetUnspentOutput, faucetUnspentOutputID, faucetAmount, err := f.getGenesisOutputFromIndexer(clt, faucetAddr) - if err != nil { - return nil, ierrors.Wrap(err, "failed to get faucet output from indexer") - } - - f.unspentOutput = &models.Output{ - Address: faucetAddr.(*iotago.Ed25519Address), - Index: 0, - OutputID: faucetUnspentOutputID, - Balance: faucetAmount, - OutputStruct: faucetUnspentOutput, - } - - return f, nil -} - -func (f *faucet) getGenesisOutputFromIndexer(clt models.Client, faucetAddr iotago.DirectUnlockableAddress) (iotago.Output, iotago.OutputID, iotago.BaseToken, error) { - indexer, err := clt.Indexer() - if err != nil { - log.Errorf("account wallet failed due to failure in connecting to indexer") - - return nil, iotago.EmptyOutputID, 0, ierrors.Wrapf(err, "failed to get indexer from client") - } - - results, err := indexer.Outputs(context.Background(), &apimodels.BasicOutputsQuery{ - AddressBech32: faucetAddr.Bech32(iotago.PrefixTestnet), - }) - if err != nil { - return nil, iotago.EmptyOutputID, 0, ierrors.Wrap(err, "failed to prepare faucet unspent outputs indexer request") - } - - var ( - faucetUnspentOutput iotago.Output - faucetUnspentOutputID iotago.OutputID - faucetAmount iotago.BaseToken - ) - for results.Next() { - unspents, err := results.Outputs(context.TODO()) - if err != nil { - return nil, iotago.EmptyOutputID, 0, ierrors.Wrap(err, "failed to get faucet unspent outputs") - } - - faucetUnspentOutput = unspents[0] - faucetAmount = faucetUnspentOutput.BaseTokenAmount() - faucetUnspentOutputID = lo.Return1(results.Response.Items.OutputIDs())[0] - } - - return faucetUnspentOutput, faucetUnspentOutputID, faucetAmount, nil -} - -func (f *faucet) prepareFaucetRequest(receiveAddr iotago.Address, amount iotago.BaseToken, rmc iotago.Mana) (*iotago.SignedTransaction, error) { - remainderAmount, err := safemath.SafeSub(f.unspentOutput.Balance, amount) - if err != nil { - panic(err) - } - - txBuilder, remainderIndex, err := f.createFaucetTransactionNoManaHandling(receiveAddr, amount, remainderAmount) - if err != nil { - return nil, err - } - - rmcAllotedTxBuilder := txBuilder.Clone() - // faucet will allot exact mana to be burnt, rest of the mana is alloted to faucet output remainder - rmcAllotedTxBuilder.AllotRequiredManaAndStoreRemainingManaInOutput(txBuilder.CreationSlot(), rmc, f.account.ID(), remainderIndex) - - var signedTx *iotago.SignedTransaction - signedTx, err = rmcAllotedTxBuilder.Build(f.genesisHdWallet.AddressSigner()) - if err != nil { - log.Infof("WARN: failed to build tx with min required mana allotted, genesis potential mana was not enough, fallback to faucet account") - txBuilder.AllotAllMana(txBuilder.CreationSlot(), f.account.ID()) - if signedTx, err = txBuilder.Build(f.genesisHdWallet.AddressSigner()); err != nil { - return nil, ierrors.Wrapf(err, "failed to build transaction with all mana allotted, after not having enough mana required based on RMC") - } - } - - return signedTx, nil -} - -func (f *faucet) createFaucetTransactionNoManaHandling(receiveAddr iotago.Address, amount iotago.BaseToken, remainderAmount iotago.BaseToken) (*builder.TransactionBuilder, int, error) { - currentTime := time.Now() - currentSlot := f.clt.LatestAPI().TimeProvider().SlotFromTime(currentTime) - - apiForSlot := f.clt.APIForSlot(currentSlot) - txBuilder := builder.NewTransactionBuilder(apiForSlot) - - txBuilder.AddInput(&builder.TxInput{ - UnlockTarget: f.genesisHdWallet.Address(iotago.AddressEd25519).(*iotago.Ed25519Address), - InputID: f.unspentOutput.OutputID, - Input: f.unspentOutput.OutputStruct, - }) - - switch receiveAddr.(type) { - case *iotago.Ed25519Address: - txBuilder.AddOutput(&iotago.BasicOutput{ - Amount: amount, - Conditions: iotago.BasicOutputUnlockConditions{ - &iotago.AddressUnlockCondition{Address: receiveAddr}, - }, - }) - case *iotago.ImplicitAccountCreationAddress: - log.Infof("creating account %s", receiveAddr) - accOutputBuilder := builder.NewAccountOutputBuilder(receiveAddr, receiveAddr, amount) - output, err := accOutputBuilder.Build() - if err != nil { - log.Errorf("failed to build account output: %s", err) - - return nil, 0, err - } - txBuilder.AddOutput(output) - } - - // remainder output - remainderIndex := 1 - txBuilder.AddOutput(&iotago.BasicOutput{ - Amount: remainderAmount, - Conditions: iotago.BasicOutputUnlockConditions{ - &iotago.AddressUnlockCondition{Address: f.genesisHdWallet.Address(iotago.AddressEd25519).(*iotago.Ed25519Address)}, - }, - }) - txBuilder.AddTaggedDataPayload(&iotago.TaggedData{Tag: []byte("Faucet funds"), Data: []byte("to addr" + receiveAddr.String())}) - txBuilder.SetCreationSlot(currentSlot) - - return txBuilder, remainderIndex, nil -} diff --git a/tools/evil-spammer/accountwallet/options.go b/tools/evil-spammer/accountwallet/options.go deleted file mode 100644 index 51b28fcd7..000000000 --- a/tools/evil-spammer/accountwallet/options.go +++ /dev/null @@ -1,24 +0,0 @@ -package accountwallet - -import ( - "github.com/iotaledger/hive.go/runtime/options" -) - -// WithClientURL sets the client bind address. -func WithClientURL(url string) options.Option[AccountWallet] { - return func(w *AccountWallet) { - w.optsClientBindAddress = url - } -} - -func WithAccountStatesFile(fileName string) options.Option[AccountWallet] { - return func(w *AccountWallet) { - w.optsAccountStatesFile = fileName - } -} - -func WithFaucetAccountParams(params *faucetParams) options.Option[AccountWallet] { - return func(w *AccountWallet) { - w.optsFaucetParams = params - } -} diff --git a/tools/evil-spammer/accountwallet/wallet.go b/tools/evil-spammer/accountwallet/wallet.go deleted file mode 100644 index af8903e3a..000000000 --- a/tools/evil-spammer/accountwallet/wallet.go +++ /dev/null @@ -1,343 +0,0 @@ -package accountwallet - -import ( - "crypto/ed25519" - "os" - "time" - - "github.com/mr-tron/base58" - - "github.com/iotaledger/hive.go/ierrors" - "github.com/iotaledger/hive.go/runtime/options" - "github.com/iotaledger/hive.go/runtime/timeutil" - "github.com/iotaledger/iota-core/pkg/blockhandler" - "github.com/iotaledger/iota-core/pkg/testsuite/mock" - "github.com/iotaledger/iota-core/tools/evil-spammer/logger" - "github.com/iotaledger/iota-core/tools/evil-spammer/models" - iotago "github.com/iotaledger/iota.go/v4" - "github.com/iotaledger/iota.go/v4/builder" - "github.com/iotaledger/iota.go/v4/tpkg" -) - -var log = logger.New("AccountWallet") - -func Run(config *Configuration) (*AccountWallet, error) { - var opts []options.Option[AccountWallet] - if config.BindAddress != "" { - opts = append(opts, WithClientURL(config.BindAddress)) - } - if config.AccountStatesFile != "" { - opts = append(opts, WithAccountStatesFile(config.AccountStatesFile)) - } - - opts = append(opts, WithFaucetAccountParams(&faucetParams{ - genesisSeed: config.GenesisSeed, - faucetPrivateKey: config.BlockIssuerPrivateKey, - faucetAccountID: config.AccountID, - })) - - wallet, err := NewAccountWallet(opts...) - if err != nil { - return nil, ierrors.Wrap(err, "failed to create wallet") - } - - // load wallet - err = wallet.fromAccountStateFile() - if err != nil { - return nil, ierrors.Wrap(err, "failed to load wallet from file") - } - - return wallet, nil -} - -func SaveState(w *AccountWallet) error { - return w.toAccountStateFile() -} - -type AccountWallet struct { - faucet *faucet - seed [32]byte - - accountsAliases map[string]*models.AccountData - - //accountsStatus map[string]models.AccountMetadata - latestUsedIndex uint64 - - client *models.WebClient - - optsClientBindAddress string - optsAccountStatesFile string - optsFaucetParams *faucetParams - optsRequestTimeout time.Duration - optsRequestTicker time.Duration -} - -func NewAccountWallet(opts ...options.Option[AccountWallet]) (*AccountWallet, error) { - var initErr error - return options.Apply(&AccountWallet{ - accountsAliases: make(map[string]*models.AccountData), - seed: tpkg.RandEd25519Seed(), - optsRequestTimeout: time.Second * 120, - optsRequestTicker: time.Second * 5, - }, opts, func(w *AccountWallet) { - w.client, initErr = models.NewWebClient(w.optsClientBindAddress) - if initErr != nil { - return - } - - var f *faucet - f, initErr = newFaucet(w.client, w.optsFaucetParams) - if initErr != nil { - return - } - - w.faucet = f - w.accountsAliases[FaucetAccountAlias] = &models.AccountData{ - Alias: FaucetAccountAlias, - Status: models.AccountReady, - OutputID: iotago.EmptyOutputID, - Index: 0, - Account: w.faucet.account, - } - }), initErr -} - -// toAccountStateFile write account states to file. -func (a *AccountWallet) toAccountStateFile() error { - accounts := make([]*models.AccountState, 0) - - for _, acc := range a.accountsAliases { - accounts = append(accounts, models.AccountStateFromAccountData(acc)) - } - - stateBytes, err := a.client.LatestAPI().Encode(&StateData{ - Seed: base58.Encode(a.seed[:]), - LastUsedIndex: a.latestUsedIndex, - AccountsData: accounts, - }) - if err != nil { - return ierrors.Wrap(err, "failed to encode state") - } - - //nolint:gosec // users should be able to read the file - if err = os.WriteFile(a.optsAccountStatesFile, stateBytes, 0o644); err != nil { - return ierrors.Wrap(err, "failed to write account states to file") - } - - return nil -} - -func (a *AccountWallet) fromAccountStateFile() error { - walletStateBytes, err := os.ReadFile(a.optsAccountStatesFile) - if err != nil { - if !os.IsNotExist(err) { - return ierrors.Wrap(err, "failed to read file") - } - return nil - } - - var data StateData - _, err = a.client.LatestAPI().Decode(walletStateBytes, &data) - if err != nil { - return ierrors.Wrap(err, "failed to decode from file") - } - - // copy seeds - decodedSeeds, err := base58.Decode(data.Seed) - if err != nil { - return ierrors.Wrap(err, "failed to decode seed") - } - copy(a.seed[:], decodedSeeds) - - // set latest used index - a.latestUsedIndex = data.LastUsedIndex - - // account data - for _, acc := range data.AccountsData { - a.accountsAliases[acc.Alias] = acc.ToAccountData() - if acc.Alias == FaucetAccountAlias { - a.accountsAliases[acc.Alias].Status = models.AccountReady - } - } - - return nil -} - -func (a *AccountWallet) registerAccount(alias string, outputID iotago.OutputID, index uint64, privKey ed25519.PrivateKey) iotago.AccountID { - accountID := iotago.AccountIDFromOutputID(outputID) - account := blockhandler.NewEd25519Account(accountID, privKey) - - a.accountsAliases[alias] = &models.AccountData{ - Alias: alias, - Account: account, - Status: models.AccountPending, - OutputID: outputID, - Index: index, - } - - return accountID -} - -func (a *AccountWallet) updateAccountStatus(alias string, status models.AccountStatus) (*models.AccountData, bool) { - accData, exists := a.accountsAliases[alias] - if !exists { - return nil, false - } - - if accData.Status == status { - return accData, false - } - - accData.Status = status - a.accountsAliases[alias] = accData - - return accData, true -} - -func (a *AccountWallet) GetReadyAccount(alias string) (*models.AccountData, error) { - accData, exists := a.accountsAliases[alias] - if !exists { - return nil, ierrors.Errorf("account with alias %s does not exist", alias) - } - - // check if account is ready (to be included in a commitment) - ready := a.isAccountReady(accData) - if !ready { - return nil, ierrors.Errorf("account with alias %s is not ready", alias) - } - - accData, _ = a.updateAccountStatus(alias, models.AccountReady) - - return accData, nil -} - -func (a *AccountWallet) GetAccount(alias string) (*models.AccountData, error) { - accData, exists := a.accountsAliases[alias] - if !exists { - return nil, ierrors.Errorf("account with alias %s does not exist", alias) - } - - return accData, nil -} - -func (a *AccountWallet) isAccountReady(accData *models.AccountData) bool { - if accData.Status == models.AccountReady { - return true - } - - creationSlot := accData.OutputID.CreationSlot() - - // wait for the account to be committed - log.Infof("Waiting for account %s to be committed within slot %d...", accData.Alias, creationSlot) - err := a.retry(func() (bool, error) { - resp, err := a.client.GetBlockIssuance() - if err != nil { - return false, err - } - - if resp.Commitment.Slot >= creationSlot { - log.Infof("Slot %d commited, account %s is ready to use", creationSlot, accData.Alias) - return true, nil - } - - return false, nil - }) - - if err != nil { - log.Errorf("failed to get commitment details while waiting %s: %s", accData.Alias, err) - return false - } - - return true -} - -func (a *AccountWallet) getFunds(amount uint64, addressType iotago.AddressType) (*models.Output, ed25519.PrivateKey, error) { - hdWallet := mock.NewHDWallet("", a.seed[:], a.latestUsedIndex+1) - privKey, _ := hdWallet.KeyPair() - receiverAddr := hdWallet.Address(addressType) - createdOutput, err := a.RequestFaucetFunds(a.client, receiverAddr, iotago.BaseToken(amount)) - if err != nil { - return nil, nil, ierrors.Wrap(err, "failed to request funds from Faucet") - } - - a.latestUsedIndex++ - createdOutput.Index = a.latestUsedIndex - - return createdOutput, privKey, nil -} - -func (a *AccountWallet) destroyAccount(alias string) error { - accData, err := a.GetAccount(alias) - if err != nil { - return err - } - hdWallet := mock.NewHDWallet("", a.seed[:], accData.Index) - - issuingTime := time.Now() - issuingSlot := a.client.LatestAPI().TimeProvider().SlotFromTime(issuingTime) - apiForSlot := a.client.APIForSlot(issuingSlot) - - // get output from node - // From TIP42: Indexers and node plugins shall map the account address of the output derived with Account ID to the regular address -> output mapping table, so that given an Account Address, its most recent unspent account output can be retrieved. - // TODO: use correct outputID - accountOutput := a.client.GetOutput(accData.OutputID) - - txBuilder := builder.NewTransactionBuilder(apiForSlot) - txBuilder.AddInput(&builder.TxInput{ - UnlockTarget: a.accountsAliases[alias].Account.ID().ToAddress(), - InputID: accData.OutputID, - Input: accountOutput, - }) - - // send all tokens to faucet - txBuilder.AddOutput(&iotago.BasicOutput{ - Amount: accountOutput.BaseTokenAmount(), - Conditions: iotago.BasicOutputUnlockConditions{ - &iotago.AddressUnlockCondition{Address: a.faucet.genesisHdWallet.Address(iotago.AddressEd25519).(*iotago.Ed25519Address)}, - }, - }) - - tx, err := txBuilder.Build(hdWallet.AddressSigner()) - if err != nil { - return ierrors.Wrapf(err, "failed to build transaction for account alias destruction %s", alias) - } - - congestionResp, issuerResp, version, err := a.RequestBlockBuiltData(a.client.Client(), a.faucet.account.ID()) - if err != nil { - return ierrors.Wrap(err, "failed to request block built data for the faucet account") - } - - blockID, err := a.PostWithBlock(a.client, tx, a.faucet.account, congestionResp, issuerResp, version) - if err != nil { - return ierrors.Wrapf(err, "failed to post block with ID %s", blockID) - } - - // remove account from wallet - delete(a.accountsAliases, alias) - - log.Infof("Account %s has been destroyed", alias) - return nil -} - -func (a *AccountWallet) retry(requestFunc func() (bool, error)) error { - timeout := time.NewTimer(a.optsRequestTimeout) - interval := time.NewTicker(a.optsRequestTicker) - defer timeutil.CleanupTimer(timeout) - defer timeutil.CleanupTicker(interval) - - for { - done, err := requestFunc() - if err != nil { - return err - } - if done { - return nil - } - select { - case <-interval.C: - continue - case <-timeout.C: - return ierrors.New("timeout while trying to request") - } - } -} diff --git a/tools/evil-spammer/config.go b/tools/evil-spammer/config.go deleted file mode 100644 index a62e16dca..000000000 --- a/tools/evil-spammer/config.go +++ /dev/null @@ -1,60 +0,0 @@ -package main - -import ( - "time" - - "github.com/iotaledger/iota-core/tools/evil-spammer/accountwallet" - "github.com/iotaledger/iota-core/tools/evil-spammer/evilwallet" - "github.com/iotaledger/iota-core/tools/evil-spammer/programs" - "github.com/iotaledger/iota-core/tools/evil-spammer/spammer" -) - -// Nodes used during the test, use at least two nodes to be able to doublespend. -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{} -) - -var ( - Script = "basic" - Subcommand = "" - - customSpamParams = programs.CustomSpamParams{ - ClientURLs: urls, - SpamTypes: []string{spammer.TypeBlock}, - Rates: []int{1}, - Durations: []time.Duration{time.Second * 20}, - BlkToBeSent: []int{0}, - TimeUnit: time.Second, - DelayBetweenConflicts: 0, - NSpend: 2, - Scenario: evilwallet.Scenario1(), - DeepSpam: false, - EnableRateSetter: false, - AccountAlias: accountwallet.FaucetAccountAlias, - } - - quickTestParams = programs.QuickTestParams{ - ClientURLs: urls, - Rate: 100, - Duration: time.Second * 30, - TimeUnit: time.Second, - DelayBetweenConflicts: 0, - EnableRateSetter: false, - } - - accountsSubcommandsFlags []accountwallet.AccountSubcommands - - //nolint:godot - // commitmentsSpamParams = CommitmentsSpamParams{ - // Rate: 1, - // Duration: time.Second * 20, - // TimeUnit: time.Second, - // NetworkAlias: "docker", - // SpammerAlias: "validator-1", - // ValidAlias: accountwallet.FaucetAccountAlias, - // CommitmentType: "latest", - // ForkAfter: 10, - // } -) diff --git a/tools/evil-spammer/evilwallet/aliasmanager.go b/tools/evil-spammer/evilwallet/aliasmanager.go deleted file mode 100644 index 284e89f6d..000000000 --- a/tools/evil-spammer/evilwallet/aliasmanager.go +++ /dev/null @@ -1,110 +0,0 @@ -package evilwallet - -import ( - "go.uber.org/atomic" - - "github.com/iotaledger/hive.go/ierrors" - "github.com/iotaledger/hive.go/runtime/syncutils" - "github.com/iotaledger/iota-core/tools/evil-spammer/models" -) - -// region AliasManager ///////////////////////////////////////////////////////////////////////////////////////////////// - -// AliasManager is the manager for output aliases. -type AliasManager struct { - outputMap map[string]*models.Output - inputMap map[string]*models.Output - - outputAliasCount *atomic.Uint64 - mu syncutils.RWMutex -} - -// NewAliasManager creates and returns a new AliasManager. -func NewAliasManager() *AliasManager { - return &AliasManager{ - outputMap: make(map[string]*models.Output), - inputMap: make(map[string]*models.Output), - outputAliasCount: atomic.NewUint64(0), - } -} - -// AddOutputAlias maps the given outputAliasName to output, if there's duplicate outputAliasName, it will be overwritten. -func (a *AliasManager) AddOutputAlias(output *models.Output, aliasName string) { - a.mu.Lock() - defer a.mu.Unlock() - - a.outputMap[aliasName] = output -} - -// AddInputAlias adds an input alias. -func (a *AliasManager) AddInputAlias(input *models.Output, aliasName string) { - a.mu.Lock() - defer a.mu.Unlock() - - a.inputMap[aliasName] = input -} - -// GetInput returns the input for the alias specified. -func (a *AliasManager) GetInput(aliasName string) (*models.Output, bool) { - a.mu.RLock() - defer a.mu.RUnlock() - in, ok := a.inputMap[aliasName] - - return in, ok -} - -// GetOutput returns the output for the alias specified. -func (a *AliasManager) GetOutput(aliasName string) *models.Output { - a.mu.RLock() - defer a.mu.RUnlock() - - return a.outputMap[aliasName] -} - -// ClearAllAliases clears all aliases. -func (a *AliasManager) ClearAllAliases() { - a.mu.Lock() - defer a.mu.Unlock() - - a.inputMap = make(map[string]*models.Output) - a.outputMap = make(map[string]*models.Output) -} - -// ClearAliases clears provided aliases. -func (a *AliasManager) ClearAliases(aliases ScenarioAlias) { - a.mu.Lock() - defer a.mu.Unlock() - - for _, in := range aliases.Inputs { - delete(a.inputMap, in) - } - for _, out := range aliases.Outputs { - delete(a.outputMap, out) - } -} - -// AddOutputAliases batch adds the outputs their respective aliases. -func (a *AliasManager) AddOutputAliases(outputs []*models.Output, aliases []string) error { - if len(outputs) != len(aliases) { - return ierrors.New("mismatch outputs and aliases length") - } - for i, out := range outputs { - a.AddOutputAlias(out, aliases[i]) - } - - return nil -} - -// AddInputAliases batch adds the inputs their respective aliases. -func (a *AliasManager) AddInputAliases(inputs []*models.Output, aliases []string) error { - if len(inputs) != len(aliases) { - return ierrors.New("mismatch outputs and aliases length") - } - for i, out := range inputs { - a.AddInputAlias(out, aliases[i]) - } - - return nil -} - -// endregion ///////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/tools/evil-spammer/evilwallet/customscenarios.go b/tools/evil-spammer/evilwallet/customscenarios.go deleted file mode 100644 index e26142746..000000000 --- a/tools/evil-spammer/evilwallet/customscenarios.go +++ /dev/null @@ -1,238 +0,0 @@ -package evilwallet - -import ( - "strconv" -) - -var scenariosMap map[string]EvilBatch - -func init() { - scenariosMap = make(map[string]EvilBatch) - scenariosMap["tx"] = SingleTransactionBatch() - scenariosMap["ds"] = NSpendBatch(2) - scenariosMap["conflict-circle"] = ConflictSetCircle(4) - scenariosMap["guava"] = Scenario1() - scenariosMap["orange"] = Scenario2() - scenariosMap["mango"] = Scenario3() - scenariosMap["pear"] = Scenario4() - scenariosMap["lemon"] = Scenario5() - scenariosMap["banana"] = Scenario6() - scenariosMap["kiwi"] = Scenario7() - scenariosMap["peace"] = NoConflictsScenario1() -} - -// GetScenario returns an evil batch based i=on its name. -func GetScenario(scenarioName string) (batch EvilBatch, ok bool) { - batch, ok = scenariosMap[scenarioName] - return -} - -// SingleTransactionBatch returns an EvilBatch that is a single transaction. -func SingleTransactionBatch() EvilBatch { - return EvilBatch{{ScenarioAlias{Inputs: []string{"1"}, Outputs: []string{"2"}}}} -} - -// ConflictSetCircle creates a circular conflict set for a given size, e.g. for size=3, conflict set: A-B-C-A. -func ConflictSetCircle(size int) EvilBatch { - scenarioAlias := make([]ScenarioAlias, 0) - inputStartNum := size - - for i := 0; i < inputStartNum; i++ { - in := i - in2 := (in + 1) % inputStartNum - scenarioAlias = append(scenarioAlias, - ScenarioAlias{ - Inputs: []string{strconv.Itoa(in), strconv.Itoa(in2)}, - Outputs: []string{strconv.Itoa(inputStartNum + i)}, - }) - } - - return EvilBatch{scenarioAlias} -} - -func NSpendBatch(nSpent int) EvilBatch { - conflictSlice := make(EvilBatch, 0) - scenarioAlias := make([]ScenarioAlias, 0) - inputStartNum := nSpent + 1 - - for i := 1; i <= nSpent; i++ { - scenarioAlias = append(scenarioAlias, - ScenarioAlias{ - Inputs: []string{strconv.Itoa(inputStartNum)}, - Outputs: []string{strconv.Itoa(i)}, - }, - ) - } - conflictSlice = append(conflictSlice, scenarioAlias) - - return conflictSlice -} - -// Scenario1 describes two double spends and aggregates them. -func Scenario1() EvilBatch { - return EvilBatch{ - []ScenarioAlias{ - {Inputs: []string{"1"}, Outputs: []string{"2", "3"}}, - }, - []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"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"6", "5"}, Outputs: []string{"8"}}, - }, - } -} - -// Scenario2 is a reflection of UTXO unit test scenario example B - packages/ledgerstate/utxo_dag_test_exampleB.png. -func Scenario2() EvilBatch { - return EvilBatch{ - []ScenarioAlias{ - {Inputs: []string{"A"}, Outputs: []string{"C"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"B"}, Outputs: []string{"D"}}, - {Inputs: []string{"B"}, Outputs: []string{"G"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"C", "D"}, Outputs: []string{"E"}}, - {Inputs: []string{"D"}, Outputs: []string{"F"}}, - }, - } -} - -// Scenario3 is a reflection of UTXO unit test scenario example C - packages/ledgerstate/utxo_dag_test_exampleC.png. -func Scenario3() EvilBatch { - return EvilBatch{ - []ScenarioAlias{ - {Inputs: []string{"A"}, Outputs: []string{"C"}}, - {Inputs: []string{"A"}, Outputs: []string{"D"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"B"}, Outputs: []string{"E"}}, - {Inputs: []string{"B"}, Outputs: []string{"F"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"D", "E"}, Outputs: []string{"G"}}, - }, - } -} - -// Scenario4 is a reflection of ledgerstate unit test for conflict confirmation - packages/ledgerstate/ledgerstate_test_SetConflictConfirmed.png. -func Scenario4() EvilBatch { - return EvilBatch{ - []ScenarioAlias{ - {Inputs: []string{"1"}, Outputs: []string{"A"}}, - {Inputs: []string{"1"}, Outputs: []string{"B"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"2"}, Outputs: []string{"C"}}, - {Inputs: []string{"2"}, Outputs: []string{"D"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"3"}, Outputs: []string{"E"}}, - {Inputs: []string{"3"}, Outputs: []string{"F"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"D", "E"}, Outputs: []string{"G"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"G"}, Outputs: []string{"H"}}, - {Inputs: []string{"G"}, Outputs: []string{"I"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"A", "I"}, Outputs: []string{"J"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"J"}, Outputs: []string{"K"}}, - }, - } -} - -// Scenario5 uses ConflictSetCircle with size 4 and aggregate its outputs. -func Scenario5() EvilBatch { - circularConflict := ConflictSetCircle(4) - circularConflict = append(circularConflict, []ScenarioAlias{{ - Inputs: []string{"4", "6"}, - Outputs: []string{"8"}, - }}) - circularConflict = append(circularConflict, []ScenarioAlias{{ - Inputs: []string{"5", "7"}, - Outputs: []string{"9"}, - }}) - - return circularConflict -} - -// Scenario6 returns 5 levels deep scenario. -func Scenario6() EvilBatch { - return EvilBatch{ - []ScenarioAlias{ - {Inputs: []string{"1"}, Outputs: []string{"A", "B"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"A"}, Outputs: []string{"C", "D"}}, - {Inputs: []string{"B"}, Outputs: []string{"E"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"C"}, Outputs: []string{"F", "G"}}, - {Inputs: []string{"C", "D"}, Outputs: []string{"H"}}, - {Inputs: []string{"D"}, Outputs: []string{"I"}}, - {Inputs: []string{"F", "D"}, Outputs: []string{"J"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"G"}, Outputs: []string{"K"}}, - {Inputs: []string{"I"}, Outputs: []string{"L"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"K", "I"}, Outputs: []string{"M"}}, - {Inputs: []string{"L"}, Outputs: []string{"N"}}, - }, - } -} - -// Scenario7 three level deep scenario, with two separate conflict sets aggregated. -func Scenario7() EvilBatch { - return EvilBatch{ - []ScenarioAlias{ - {Inputs: []string{"A"}, Outputs: []string{"E"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"A", "B"}, Outputs: []string{"F"}}, - {Inputs: []string{"B"}, Outputs: []string{"G"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"C"}, Outputs: []string{"H"}}, - {Inputs: []string{"D"}, Outputs: []string{"I"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"E", "G"}, Outputs: []string{"J"}}, - {Inputs: []string{"H"}, Outputs: []string{"K"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"J", "K"}, Outputs: []string{"L"}}, - {Inputs: []string{"I", "K"}, Outputs: []string{"M"}}, - }, - } -} - -// NoConflictsScenario1 returns batch with no conflicts that is 3 levels deep. -func NoConflictsScenario1() EvilBatch { - return EvilBatch{ - []ScenarioAlias{ - {Inputs: []string{"1"}, Outputs: []string{"3", "4"}}, - {Inputs: []string{"2"}, Outputs: []string{"5", "6"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"6", "4"}, Outputs: []string{"7"}}, - {Inputs: []string{"3", "5"}, Outputs: []string{"8"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"8", "7"}, Outputs: []string{"9"}}, - }, - } -} diff --git a/tools/evil-spammer/evilwallet/evilscenario.go b/tools/evil-spammer/evilwallet/evilscenario.go deleted file mode 100644 index 00f15d968..000000000 --- a/tools/evil-spammer/evilwallet/evilscenario.go +++ /dev/null @@ -1,143 +0,0 @@ -package evilwallet - -import ( - "fmt" - "strconv" - - "github.com/mr-tron/base58" - "go.uber.org/atomic" - - "github.com/iotaledger/hive.go/ds/types" - iotago "github.com/iotaledger/iota.go/v4" -) - -// The custom conflict in spammer can be provided like this: -// EvilBatch{ -// { -// ScenarioAlias{inputs: []{"1"}, outputs: []{"2","3"}} -// }, -// { -// ScenarioAlias{inputs: []{"2"}, outputs: []{"4"}}, -// ScenarioAlias{inputs: []{"2"}, outputs: []{"5"}} -// } -// } - -type ScenarioAlias struct { - Inputs []string - Outputs []string -} - -func NewScenarioAlias() ScenarioAlias { - return ScenarioAlias{ - Inputs: make([]string, 0), - Outputs: make([]string, 0), - } -} - -type EvilBatch [][]ScenarioAlias - -type EvilScenario struct { - ID string - // provides a user-friendly way of listing input and output aliases - ConflictBatch EvilBatch - // determines whether outputs of the batch should be reused during the spam to create deep UTXO tree structure. - Reuse bool - // specifies the output type of the spam, if not provided, defaults to BasicOutput - OutputType iotago.OutputType - // if provided, the outputs from the spam will be saved into this wallet, accepted types of wallet: Reuse, RestrictedReuse. - // if type == Reuse, then wallet is available for reuse spamming scenarios that did not provide RestrictedWallet. - OutputWallet *Wallet - // if provided and reuse set to true, outputs from this wallet will be used for deep spamming, allows for controllable building of UTXO deep structures. - // if not provided evil wallet will use Reuse wallet if any is available. Accepts only RestrictedReuse wallet type. - RestrictedInputWallet *Wallet - // used together with scenario ID to create a prefix for distinct batch alias creation - BatchesCreated *atomic.Uint64 - // used to determine how many clients are needed to run this scenario, some double spends need more than one client to pass the filter - NumOfClientsNeeded int -} - -func NewEvilScenario(options ...ScenarioOption) *EvilScenario { - scenario := &EvilScenario{ - ConflictBatch: SingleTransactionBatch(), - Reuse: false, - OutputType: iotago.OutputBasic, - OutputWallet: NewWallet(), - BatchesCreated: atomic.NewUint64(0), - } - - for _, option := range options { - option(scenario) - } - scenario.ID = base58.Encode([]byte(fmt.Sprintf("%v%v%v", scenario.ConflictBatch, scenario.Reuse, scenario.OutputWallet.ID)))[:11] - scenario.NumOfClientsNeeded = calculateNumofClientsNeeded(scenario) - - return scenario -} - -func calculateNumofClientsNeeded(scenario *EvilScenario) (counter int) { - for _, conflictMap := range scenario.ConflictBatch { - if len(conflictMap) > counter { - counter = len(conflictMap) - } - } - - return -} - -// readCustomConflictsPattern determines outputs of the batch, needed for saving batch outputs to the outputWallet. -func (e *EvilScenario) readCustomConflictsPattern(batch EvilBatch) (batchOutputs map[string]types.Empty) { - outputs := make(map[string]types.Empty) - inputs := make(map[string]types.Empty) - - for _, conflictMap := range batch { - for _, conflicts := range conflictMap { - // add output to outputsAliases - for _, input := range conflicts.Inputs { - inputs[input] = types.Void - } - for _, output := range conflicts.Outputs { - outputs[output] = types.Void - } - } - } - // remove outputs that were never used as input in this EvilBatch to determine batch outputs - for output := range outputs { - if _, ok := inputs[output]; ok { - delete(outputs, output) - } - } - batchOutputs = outputs - - return -} - -// NextBatchPrefix creates a new batch prefix by increasing the number of created batches for this scenario. -func (e *EvilScenario) nextBatchPrefix() string { - return e.ID + strconv.Itoa(int(e.BatchesCreated.Add(1))) -} - -// ConflictBatchWithPrefix generates a new conflict batch with scenario prefix created from scenario ID and batch count. -// BatchOutputs are outputs of the batch that can be reused in deep spamming by collecting them in Reuse wallet. -func (e *EvilScenario) ConflictBatchWithPrefix() (prefixedBatch EvilBatch, allAliases ScenarioAlias, batchOutputs map[string]types.Empty) { - allAliases = NewScenarioAlias() - prefix := e.nextBatchPrefix() - for _, conflictMap := range e.ConflictBatch { - scenarioAlias := make([]ScenarioAlias, 0) - for _, aliases := range conflictMap { - sa := NewScenarioAlias() - for _, in := range aliases.Inputs { - sa.Inputs = append(sa.Inputs, prefix+in) - allAliases.Inputs = append(allAliases.Inputs, prefix+in) - } - for _, out := range aliases.Outputs { - sa.Outputs = append(sa.Outputs, prefix+out) - allAliases.Outputs = append(allAliases.Outputs, prefix+out) - } - scenarioAlias = append(scenarioAlias, sa) - } - prefixedBatch = append(prefixedBatch, scenarioAlias) - } - batchOutputs = e.readCustomConflictsPattern(prefixedBatch) - - return -} diff --git a/tools/evil-spammer/evilwallet/evilwallet.go b/tools/evil-spammer/evilwallet/evilwallet.go deleted file mode 100644 index 007059cce..000000000 --- a/tools/evil-spammer/evilwallet/evilwallet.go +++ /dev/null @@ -1,846 +0,0 @@ -package evilwallet - -import ( - "sync" - "time" - - "github.com/ethereum/go-ethereum/log" - - "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/options" - "github.com/iotaledger/iota-core/pkg/blockhandler" - "github.com/iotaledger/iota-core/tools/evil-spammer/accountwallet" - evillogger "github.com/iotaledger/iota-core/tools/evil-spammer/logger" - "github.com/iotaledger/iota-core/tools/evil-spammer/models" - iotago "github.com/iotaledger/iota.go/v4" - "github.com/iotaledger/iota.go/v4/builder" - "github.com/iotaledger/iota.go/v4/nodeclient/apimodels" - "github.com/iotaledger/iota.go/v4/tpkg" -) - -const ( - // FaucetRequestSplitNumber defines the number of outputs to split from a faucet request. - FaucetRequestSplitNumber = 100 - faucetTokensPerRequest iotago.BaseToken = 1_000_000 - - waitForConfirmation = 15 * time.Second - waitForSolidification = 10 * time.Second - - awaitConfirmationSleep = 2 * time.Second - awaitSolidificationSleep = time.Millisecond * 500 -) - -var ( - defaultClientsURLs = []string{"http://localhost:8080", "http://localhost:8090"} -) - -// region EvilWallet /////////////////////////////////////////////////////////////////////////////////////////////////////// - -// EvilWallet provides a user-friendly way to do complicated double spend scenarios. -type EvilWallet struct { - // faucet is the wallet of faucet - faucet *Wallet - wallets *Wallets - accWallet *accountwallet.AccountWallet - connector models.Connector - outputManager *OutputManager - aliasManager *AliasManager - - optsClientURLs []string - log *logger.Logger -} - -// NewEvilWallet creates an EvilWallet instance. -func NewEvilWallet(opts ...options.Option[EvilWallet]) *EvilWallet { - return options.Apply(&EvilWallet{ - wallets: NewWallets(), - aliasManager: NewAliasManager(), - optsClientURLs: defaultClientsURLs, - log: evillogger.New("EvilWallet"), - }, opts, func(w *EvilWallet) { - connector := models.NewWebClients(w.optsClientURLs) - w.connector = connector - w.outputManager = NewOutputManager(connector, w.wallets, w.log) - - }) -} - -func (e *EvilWallet) LastFaucetUnspentOutput() iotago.OutputID { - faucetAddr := e.faucet.AddressOnIndex(0) - unspentFaucet := e.faucet.UnspentOutput(faucetAddr.String()) - - return unspentFaucet.OutputID -} - -// NewWallet creates a new wallet of the given wallet type. -func (e *EvilWallet) NewWallet(wType ...WalletType) *Wallet { - walletType := Other - if len(wType) != 0 { - walletType = wType[0] - } - - return e.wallets.NewWallet(walletType) -} - -// GetClients returns the given number of clients. -func (e *EvilWallet) GetClients(num int) []models.Client { - return e.connector.GetClients(num) -} - -// Connector give access to the EvilWallet connector. -func (e *EvilWallet) Connector() models.Connector { - return e.connector -} - -func (e *EvilWallet) UnspentOutputsLeft(walletType WalletType) int { - return e.wallets.UnspentOutputsLeft(walletType) -} - -func (e *EvilWallet) NumOfClient() int { - clts := e.connector.Clients() - return len(clts) -} - -func (e *EvilWallet) AddClient(clientURL string) { - e.connector.AddClient(clientURL) -} - -func (e *EvilWallet) RemoveClient(clientURL string) { - e.connector.RemoveClient(clientURL) -} - -func (e *EvilWallet) GetAccount(alias string) (blockhandler.Account, error) { - account, err := e.accWallet.GetAccount(alias) - if err != nil { - return nil, err - } - - return account.Account, nil -} - -func (e *EvilWallet) PrepareAndPostBlock(clt models.Client, payload iotago.Payload, congestionResp *apimodels.CongestionResponse, issuer blockhandler.Account) (iotago.BlockID, error) { - congestionResp, issuerResp, version, err := e.accWallet.RequestBlockBuiltData(clt.Client(), issuer.ID()) - if err != nil { - return iotago.EmptyBlockID, ierrors.Wrapf(err, "failed to get block built data for issuer %s", issuer.ID().ToHex()) - } - blockID, err := e.accWallet.PostWithBlock(clt, payload, issuer, congestionResp, issuerResp, version) - if err != nil { - return iotago.EmptyBlockID, err - } - - return blockID, nil -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// region EvilWallet Faucet Requests /////////////////////////////////////////////////////////////////////////////////// - -// RequestFundsFromFaucet requests funds from the faucet, then track the confirmed status of unspent output, -// also register the alias name for the unspent output if provided. -func (e *EvilWallet) RequestFundsFromFaucet(options ...FaucetRequestOption) (initWallet *Wallet, err error) { - initWallet = e.NewWallet(Fresh) - buildOptions := NewFaucetRequestOptions(options...) - - output, err := e.requestFaucetFunds(initWallet) - if err != nil { - return - } - - if buildOptions.outputAliasName != "" { - e.aliasManager.AddInputAlias(output, buildOptions.outputAliasName) - } - - log.Debug("Funds requested succesfully") - - return -} - -// RequestFreshBigFaucetWallets creates n new wallets, each wallet is created from one faucet request and contains 1000 outputs. -func (e *EvilWallet) RequestFreshBigFaucetWallets(numberOfWallets int) { - // channel to block the number of concurrent goroutines - semaphore := make(chan bool, 1) - wg := sync.WaitGroup{} - - for reqNum := 0; reqNum < numberOfWallets; reqNum++ { - wg.Add(1) - // block if full - semaphore <- true - go func() { - defer wg.Done() - defer func() { - // release - <-semaphore - }() - - err := e.RequestFreshBigFaucetWallet() - if err != nil { - return - } - }() - } - wg.Wait() -} - -// RequestFreshBigFaucetWallet creates a new wallet and fills the wallet with 1000 outputs created from funds -// requested from the Faucet. -func (e *EvilWallet) RequestFreshBigFaucetWallet() error { - initWallet := NewWallet() - receiveWallet := e.NewWallet(Fresh) - - txIDs := make(iotago.TransactionIDs, 0) - for i := 0; i < 1; i++ { - txID, err := e.requestAndSplitFaucetFunds(initWallet, receiveWallet) - if err != nil { - return ierrors.Wrap(err, "failed to request big funds from faucet") - } - - txIDs = append(txIDs, txID) - } - - e.outputManager.AwaitTransactionsConfirmation(txIDs...) - - e.wallets.SetWalletReady(receiveWallet) - - return nil -} - -// RequestFreshFaucetWallet creates a new wallet and fills the wallet with 100 outputs created from funds -// requested from the Faucet. -func (e *EvilWallet) RequestFreshFaucetWallet() error { - initWallet := NewWallet() - receiveWallet := e.NewWallet(Fresh) - txID, err := e.requestAndSplitFaucetFunds(initWallet, receiveWallet) - if err != nil { - return ierrors.Wrap(err, "failed to request funds from faucet") - } - - e.outputManager.AwaitTransactionsConfirmation(txID) - - e.wallets.SetWalletReady(receiveWallet) - - return err -} - -func (e *EvilWallet) requestAndSplitFaucetFunds(initWallet, receiveWallet *Wallet) (txID iotago.TransactionID, err error) { - splitOutput, err := e.requestFaucetFunds(initWallet) - if err != nil { - return iotago.EmptyTransactionID, err - } - - e.log.Debugf("Faucet funds received, continue spliting output: %s", splitOutput.OutputID.ToHex()) - // first split 1 to FaucetRequestSplitNumber outputs - return e.splitOutputs(splitOutput, initWallet, receiveWallet) -} - -func (e *EvilWallet) requestFaucetFunds(wallet *Wallet) (outputID *models.Output, err error) { - receiveAddr := wallet.AddressOnIndex(0) - clt := e.connector.GetClient() - - output, err := e.accWallet.RequestFaucetFunds(clt, receiveAddr, faucetTokensPerRequest) - if err != nil { - return nil, ierrors.Wrap(err, "failed to request funds from faucet") - } - - // update wallet with newly created output - e.outputManager.createOutputFromAddress(wallet, receiveAddr, faucetTokensPerRequest, output.OutputID, output.OutputStruct) - - return output, nil -} - -// splitOutputs splits faucet input to 100 outputs. -func (e *EvilWallet) splitOutputs(splitOutput *models.Output, inputWallet, outputWallet *Wallet) (iotago.TransactionID, error) { - if inputWallet.IsEmpty() { - return iotago.EmptyTransactionID, ierrors.New("inputWallet is empty") - } - - input, outputs := e.handleInputOutputDuringSplitOutputs(splitOutput, FaucetRequestSplitNumber, outputWallet) - - faucetAccount, err := e.accWallet.GetAccount(accountwallet.FaucetAccountAlias) - if err != nil { - return iotago.EmptyTransactionID, err - } - txData, err := e.CreateTransaction( - WithInputs(input), - WithOutputs(outputs), - WithInputWallet(inputWallet), - WithOutputWallet(outputWallet), - WithIssuanceStrategy(models.AllotmentStrategyAll, faucetAccount.Account.ID()), - ) - - if err != nil { - return iotago.EmptyTransactionID, err - } - - _, err = e.PrepareAndPostBlock(e.connector.GetClient(), txData.Payload, txData.CongestionResponse, faucetAccount.Account) - if err != nil { - return iotago.TransactionID{}, err - } - - if txData.Payload.PayloadType() != iotago.PayloadSignedTransaction { - return iotago.EmptyTransactionID, ierrors.New("payload type is not signed transaction") - } - - txID := lo.PanicOnErr(txData.Payload.(*iotago.SignedTransaction).Transaction.ID()) - - e.log.Debugf("Splitting output %s finished with tx: %s", splitOutput.OutputID.ToHex(), txID.ToHex()) - - return txID, nil -} - -func (e *EvilWallet) handleInputOutputDuringSplitOutputs(splitOutput *models.Output, splitNumber int, receiveWallet *Wallet) (input *models.Output, outputs []*OutputOption) { - input = splitOutput - - balances := SplitBalanceEqually(splitNumber, input.Balance) - for _, bal := range balances { - outputs = append(outputs, &OutputOption{amount: bal, address: receiveWallet.Address(), outputType: iotago.OutputBasic}) - } - - return -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// region EvilWallet functionality /////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// ClearAliases remove only provided aliases from AliasManager. -func (e *EvilWallet) ClearAliases(aliases ScenarioAlias) { - e.aliasManager.ClearAliases(aliases) -} - -// ClearAllAliases remove all registered alias names. -func (e *EvilWallet) ClearAllAliases() { - e.aliasManager.ClearAllAliases() -} - -func (e *EvilWallet) PrepareCustomConflicts(conflictsMaps []ConflictSlice) (conflictBatch [][]*models.PayloadIssuanceData, err error) { - for _, conflictMap := range conflictsMaps { - var txsData []*models.PayloadIssuanceData - for _, conflictOptions := range conflictMap { - txData, err2 := e.CreateTransaction(conflictOptions...) - if err2 != nil { - return nil, err2 - } - txsData = append(txsData, txData) - } - conflictBatch = append(conflictBatch, txsData) - } - - return conflictBatch, nil -} - -// CreateTransaction creates a transaction based on provided options. If no input wallet is provided, the next non-empty faucet wallet is used. -// Inputs of the transaction are determined in three ways: -// 1 - inputs are provided directly without associated alias, 2- alias is provided, and input is already stored in an alias manager, -// 3 - alias is provided, and there are no inputs assigned in Alias manager, so aliases are assigned to next ready inputs from input wallet. -func (e *EvilWallet) CreateTransaction(options ...Option) (*models.PayloadIssuanceData, error) { - buildOptions, err := NewOptions(options...) - if err != nil { - return nil, err - } - // wallet used only for outputs in the middle of the batch, that will never be reused outside custom conflict batch creation. - tempWallet := e.NewWallet() - - err = e.updateInputWallet(buildOptions) - if err != nil { - return nil, err - } - - inputs, err := e.prepareInputs(buildOptions) - if err != nil { - return nil, err - } - - outputs, addrAliasMap, tempAddresses, err := e.prepareOutputs(buildOptions, tempWallet) - if err != nil { - return nil, err - } - - alias, remainder, remainderAddr, hasRemainder := e.prepareRemainderOutput(buildOptions, outputs) - if hasRemainder { - outputs = append(outputs, remainder) - if alias != "" && addrAliasMap != nil { - addrAliasMap[remainderAddr.String()] = alias - } - } - - var congestionResp *apimodels.CongestionResponse - // request congestion endpoint if allotment strategy configured - if buildOptions.allotmentStrategy == models.AllotmentStrategyMinCost { - congestionResp, err = e.connector.GetClient().GetCongestion(buildOptions.issuerAccountID) - if err != nil { - return nil, err - } - } - - signedTx, err := e.makeTransaction(inputs, outputs, buildOptions.inputWallet, congestionResp, buildOptions.allotmentStrategy, buildOptions.issuerAccountID) - if err != nil { - return nil, err - } - txData := &models.PayloadIssuanceData{ - Payload: signedTx, - CongestionResponse: congestionResp, - } - - e.addOutputsToOutputManager(signedTx, buildOptions.outputWallet, tempWallet, tempAddresses) - e.registerOutputAliases(signedTx, addrAliasMap) - - return txData, nil -} - -// addOutputsToOutputManager adds output to the OutputManager if. -func (e *EvilWallet) addOutputsToOutputManager(signedTx *iotago.SignedTransaction, outWallet, tmpWallet *Wallet, tempAddresses map[string]types.Empty) { - for idx, o := range signedTx.Transaction.Outputs { - if o.UnlockConditionSet().Address() == nil { - continue - } - - // register UnlockConditionAddress only (skip account outputs) - addr := o.UnlockConditionSet().Address().Address - out := &models.Output{ - OutputID: iotago.OutputIDFromTransactionIDAndIndex(lo.PanicOnErr(signedTx.Transaction.ID()), uint16(idx)), - Address: addr, - Balance: o.BaseTokenAmount(), - OutputStruct: o, - } - - if _, ok := tempAddresses[addr.String()]; ok { - e.outputManager.AddOutput(tmpWallet, out) - } else { - out.Index = outWallet.AddrIndexMap(addr.String()) - e.outputManager.AddOutput(outWallet, out) - } - } -} - -// updateInputWallet if input wallet is not specified, or aliases were provided without inputs (batch inputs) use Fresh faucet wallet. -func (e *EvilWallet) updateInputWallet(buildOptions *Options) error { - for alias := range buildOptions.aliasInputs { - // inputs provided for aliases (middle inputs in a batch) - _, ok := e.aliasManager.GetInput(alias) - if ok { - // leave nil, wallet will be selected based on OutputIDWalletMap - buildOptions.inputWallet = nil - return nil - } - - break - } - wallet, err := e.useFreshIfInputWalletNotProvided(buildOptions) - if err != nil { - return err - } - buildOptions.inputWallet = wallet - - return nil -} - -func (e *EvilWallet) registerOutputAliases(signedTx *iotago.SignedTransaction, addrAliasMap map[string]string) { - if len(addrAliasMap) == 0 { - return - } - - for idx := range signedTx.Transaction.Outputs { - id := iotago.OutputIDFromTransactionIDAndIndex(lo.PanicOnErr(signedTx.Transaction.ID()), uint16(idx)) - out := e.outputManager.GetOutput(id) - if out == nil { - continue - } - - // register output alias - e.aliasManager.AddOutputAlias(out, addrAliasMap[out.Address.String()]) - - // register output as unspent output(input) - e.aliasManager.AddInputAlias(out, addrAliasMap[out.Address.String()]) - } -} - -func (e *EvilWallet) prepareInputs(buildOptions *Options) (inputs []*models.Output, err error) { - if buildOptions.areInputsProvidedWithoutAliases() { - inputs = append(inputs, buildOptions.inputs...) - - return - } - // append inputs with alias - aliasInputs, err := e.matchInputsWithAliases(buildOptions) - if err != nil { - return nil, err - } - inputs = append(inputs, aliasInputs...) - - return inputs, nil -} - -// prepareOutputs creates outputs for different scenarios, if no aliases were provided, new empty outputs are created from buildOptions.outputs balances. -func (e *EvilWallet) prepareOutputs(buildOptions *Options, tempWallet *Wallet) (outputs []iotago.Output, - addrAliasMap map[string]string, tempAddresses map[string]types.Empty, err error, -) { - if buildOptions.areOutputsProvidedWithoutAliases() { - outputs = append(outputs, buildOptions.outputs...) - } else { - // if outputs were provided with aliases - outputs, addrAliasMap, tempAddresses, err = e.matchOutputsWithAliases(buildOptions, tempWallet) - } - - return -} - -// matchInputsWithAliases gets input from the alias manager. if input was not assigned to an alias before, -// it assigns a new Fresh faucet output. -func (e *EvilWallet) matchInputsWithAliases(buildOptions *Options) (inputs []*models.Output, err error) { - // get inputs by alias - for inputAlias := range buildOptions.aliasInputs { - in, ok := e.aliasManager.GetInput(inputAlias) - if !ok { - wallet, err2 := e.useFreshIfInputWalletNotProvided(buildOptions) - if err2 != nil { - err = err2 - return - } - // No output found for given alias, use internal Fresh output if wallets are non-empty. - in = e.wallets.GetUnspentOutput(wallet) - if in == nil { - return nil, ierrors.New("could not get unspent output") - } - e.aliasManager.AddInputAlias(in, inputAlias) - } - inputs = append(inputs, in) - } - - return inputs, nil -} - -func (e *EvilWallet) useFreshIfInputWalletNotProvided(buildOptions *Options) (*Wallet, error) { - // if input wallet is not specified, use Fresh faucet wallet - if buildOptions.inputWallet == nil { - // deep spam enabled and no input reuse wallet provided, use evil wallet reuse wallet if enough outputs are available - if buildOptions.reuse { - outputsNeeded := len(buildOptions.inputs) - if wallet := e.wallets.reuseWallet(outputsNeeded); wallet != nil { - return wallet, nil - } - } - - wallet, err := e.wallets.freshWallet() - if err != nil { - return nil, ierrors.Wrap(err, "no Fresh wallet is available") - } - - return wallet, nil - } - - return buildOptions.inputWallet, nil -} - -// matchOutputsWithAliases creates outputs based on balances provided via options. -// Outputs are not yet added to the Alias Manager, as they have no ID before the transaction is created. -// 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(buildOptions *Options, tempWallet *Wallet) (outputs []iotago.Output, - addrAliasMap map[string]string, tempAddresses map[string]types.Empty, err error, -) { - err = e.updateOutputBalances(buildOptions) - if err != nil { - return nil, nil, nil, err - } - - tempAddresses = make(map[string]types.Empty) - addrAliasMap = make(map[string]string) - 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 - } - - switch output.Type() { - case iotago.OutputBasic: - outputBuilder := builder.NewBasicOutputBuilder(addr, output.BaseTokenAmount()) - outputs = append(outputs, outputBuilder.MustBuild()) - case iotago.OutputAccount: - outputBuilder := builder.NewAccountOutputBuilder(addr, addr, output.BaseTokenAmount()) - outputs = append(outputs, outputBuilder.MustBuild()) - } - - addrAliasMap[addr.String()] = alias - } - - return -} - -func (e *EvilWallet) prepareRemainderOutput(buildOptions *Options, outputs []iotago.Output) (alias string, remainderOutput iotago.Output, remainderAddress iotago.Address, added bool) { - inputBalance := iotago.BaseToken(0) - - for inputAlias := range buildOptions.aliasInputs { - in, _ := e.aliasManager.GetInput(inputAlias) - inputBalance += in.Balance - - if alias == "" { - remainderAddress = in.Address - alias = inputAlias - } - } - - for _, input := range buildOptions.inputs { - // get balance from output manager - in := e.outputManager.GetOutput(input.OutputID) - inputBalance += in.Balance - - if remainderAddress == nil { - remainderAddress = in.Address - } - } - - outputBalance := iotago.BaseToken(0) - for _, o := range outputs { - outputBalance += o.BaseTokenAmount() - } - - // remainder balances is sent to one of the address in inputs - if outputBalance < inputBalance { - remainderOutput = &iotago.BasicOutput{ - Amount: inputBalance - outputBalance, - Conditions: iotago.BasicOutputUnlockConditions{ - &iotago.AddressUnlockCondition{Address: remainderAddress}, - }, - } - - added = true - } - - return -} - -func (e *EvilWallet) updateOutputBalances(buildOptions *Options) (err error) { - // when aliases are not used for outputs, the balance had to be provided in options, nothing to do - if buildOptions.areOutputsProvidedWithoutAliases() { - return - } - totalBalance := iotago.BaseToken(0) - if !buildOptions.isBalanceProvided() { - if buildOptions.areInputsProvidedWithoutAliases() { - for _, input := range buildOptions.inputs { - // get balance from output manager - inputDetails := e.outputManager.GetOutput(input.OutputID) - totalBalance += inputDetails.Balance - } - } else { - for inputAlias := range buildOptions.aliasInputs { - in, ok := e.aliasManager.GetInput(inputAlias) - if !ok { - err = ierrors.New("could not get input by input alias") - return - } - totalBalance += in.Balance - } - } - balances := SplitBalanceEqually(len(buildOptions.outputs)+len(buildOptions.aliasOutputs), totalBalance) - i := 0 - for out, output := range buildOptions.aliasOutputs { - switch output.Type() { - case iotago.OutputBasic: - buildOptions.aliasOutputs[out] = &iotago.BasicOutput{ - Amount: balances[i], - } - case iotago.OutputAccount: - buildOptions.aliasOutputs[out] = &iotago.AccountOutput{ - Amount: balances[i], - } - } - i++ - } - } - - return -} - -func (e *EvilWallet) makeTransaction(inputs []*models.Output, outputs iotago.Outputs[iotago.Output], w *Wallet, congestionResponse *apimodels.CongestionResponse, allotmentStrategy models.AllotmentStrategy, issuerAccountID iotago.AccountID) (tx *iotago.SignedTransaction, err error) { - clt := e.Connector().GetClient() - currentTime := time.Now() - targetSlot := clt.LatestAPI().TimeProvider().SlotFromTime(currentTime) - targetAPI := clt.APIForSlot(targetSlot) - - txBuilder := builder.NewTransactionBuilder(targetAPI) - - for _, input := range inputs { - txBuilder.AddInput(&builder.TxInput{UnlockTarget: input.Address, InputID: input.OutputID, Input: input.OutputStruct}) - } - - for _, output := range outputs { - txBuilder.AddOutput(output) - } - - randomPayload := tpkg.Rand12ByteArray() - txBuilder.AddTaggedDataPayload(&iotago.TaggedData{Tag: randomPayload[:], Data: randomPayload[:]}) - - walletKeys := make([]iotago.AddressKeys, len(inputs)) - for i, input := range inputs { - addr := input.Address - var wallet *Wallet - if w == nil { // aliases provided with inputs, use wallet saved in outputManager - wallet = e.outputManager.OutputIDWalletMap(input.OutputID.ToHex()) - } else { - wallet = w - } - index := wallet.AddrIndexMap(addr.String()) - inputPrivateKey, _ := wallet.KeyPair(index) - walletKeys[i] = iotago.AddressKeys{Address: addr, Keys: inputPrivateKey} - } - - txBuilder.SetCreationSlot(targetSlot) - // no allotment strategy - if congestionResponse == nil { - return txBuilder.Build(iotago.NewInMemoryAddressSigner(walletKeys...)) - } - switch allotmentStrategy { - case models.AllotmentStrategyAll: - txBuilder.AllotAllMana(targetSlot, issuerAccountID) - case models.AllotmentStrategyMinCost: - txBuilder.AllotRequiredManaAndStoreRemainingManaInOutput(targetSlot, congestionResponse.ReferenceManaCost, issuerAccountID, 0) - } - - return txBuilder.Build(iotago.NewInMemoryAddressSigner(walletKeys...)) -} - -func (e *EvilWallet) PrepareCustomConflictsSpam(scenario *EvilScenario, strategy *models.IssuancePaymentStrategy) (txsData [][]*models.PayloadIssuanceData, allAliases ScenarioAlias, err error) { - conflicts, allAliases := e.prepareConflictSliceForScenario(scenario, strategy) - txsData, err = e.PrepareCustomConflicts(conflicts) - - return txsData, allAliases, err -} - -func (e *EvilWallet) PrepareAccountSpam(scenario *EvilScenario, strategy *models.IssuancePaymentStrategy) (*models.PayloadIssuanceData, ScenarioAlias, error) { - accountSpamOptions, allAliases := e.prepareFlatOptionsForAccountScenario(scenario, strategy) - - txData, err := e.CreateTransaction(accountSpamOptions...) - - return txData, allAliases, err -} - -func (e *EvilWallet) evaluateIssuanceStrategy(strategy *models.IssuancePaymentStrategy) (models.AllotmentStrategy, iotago.AccountID) { - var issuerAccountID iotago.AccountID - if strategy.AllotmentStrategy != models.AllotmentStrategyNone { - // get issuer accountID - accData, err := e.accWallet.GetAccount(strategy.IssuerAlias) - if err != nil { - panic("could not get issuer accountID while preparing conflicts") - } - issuerAccountID = accData.Account.ID() - } - return strategy.AllotmentStrategy, issuerAccountID -} - -func (e *EvilWallet) prepareConflictSliceForScenario(scenario *EvilScenario, strategy *models.IssuancePaymentStrategy) (conflictSlice []ConflictSlice, allAliases ScenarioAlias) { - genOutputOptions := func(aliases []string) []*OutputOption { - outputOptions := make([]*OutputOption, 0) - for _, o := range aliases { - outputOptions = append(outputOptions, &OutputOption{aliasName: o, outputType: iotago.OutputBasic}) - } - - return outputOptions - } - - // make conflictSlice - prefixedBatch, allAliases, batchOutputs := scenario.ConflictBatchWithPrefix() - conflictSlice = make([]ConflictSlice, 0) - for _, conflictMap := range prefixedBatch { - conflicts := make([][]Option, 0) - for _, aliases := range conflictMap { - outs := genOutputOptions(aliases.Outputs) - option := []Option{WithInputs(aliases.Inputs), WithOutputs(outs), WithOutputBatchAliases(batchOutputs)} - if scenario.OutputWallet != nil { - option = append(option, WithOutputWallet(scenario.OutputWallet)) - } - if scenario.RestrictedInputWallet != nil { - option = append(option, WithInputWallet(scenario.RestrictedInputWallet)) - } - if scenario.Reuse { - option = append(option, WithReuseOutputs()) - } - option = append(option, WithIssuanceStrategy(e.evaluateIssuanceStrategy(strategy))) - conflicts = append(conflicts, option) - } - conflictSlice = append(conflictSlice, conflicts) - } - - return -} - -func (e *EvilWallet) prepareFlatOptionsForAccountScenario(scenario *EvilScenario, strategy *models.IssuancePaymentStrategy) ([]Option, ScenarioAlias) { - // we do not care about batchedOutputs, because we do not support saving account spam result in evil wallet for now - prefixedBatch, allAliases, _ := scenario.ConflictBatchWithPrefix() - if len(prefixedBatch) != 1 { - panic("invalid scenario, cannot prepare flat option structure with deep scenario, EvilBatch should have only one element") - } - evilBatch := prefixedBatch[0] - if len(evilBatch) != 1 { - panic("invalid scenario, cannot prepare flat option structure with deep scenario, EvilBatch should have only one element") - } - - genOutputOptions := func(aliases []string) []*OutputOption { - outputOptions := make([]*OutputOption, 0) - for _, o := range aliases { - outputOptions = append(outputOptions, &OutputOption{ - aliasName: o, - outputType: iotago.OutputAccount, - }) - } - - return outputOptions - } - scenarioAlias := evilBatch[0] - outs := genOutputOptions(scenarioAlias.Outputs) - - return []Option{ - WithInputs(scenarioAlias.Inputs), - WithOutputs(outs), - WithIssuanceStrategy(e.evaluateIssuanceStrategy(strategy)), - }, allAliases -} - -// AwaitInputsSolidity waits for all inputs to be solid for client clt. -// func (e *EvilWallet) AwaitInputsSolidity(inputs devnetvm.Inputs, clt Client) (allSolid bool) { -// awaitSolid := make([]string, 0) -// for _, in := range inputs { -// awaitSolid = append(awaitSolid, in.Base58()) -// } -// allSolid = e.outputManager.AwaitOutputsToBeSolid(awaitSolid, clt, maxGoroutines) -// return -// } - -// SetTxOutputsSolid marks all outputs as solid in OutputManager for clientID. -func (e *EvilWallet) SetTxOutputsSolid(outputs iotago.OutputIDs, clientID string) { - for _, out := range outputs { - e.outputManager.SetOutputIDSolidForIssuer(out, clientID) - } -} - -// AddReuseOutputsToThePool adds all addresses corresponding to provided outputs to the reuse pool. -// func (e *EvilWallet) AddReuseOutputsToThePool(outputs devnetvm.Outputs) { -// for _, out := range outputs { -// evilOutput := e.outputManager.GetOutput(out.ID()) -// if evilOutput != nil { -// wallet := e.outputManager.OutputIDWalletMap(out.ID().Base58()) -// wallet.AddReuseAddress(evilOutput.Address.Base58()) -// } -// } -// } - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// - -func WithClients(urls ...string) options.Option[EvilWallet] { - return func(opts *EvilWallet) { - opts.optsClientURLs = urls - } -} - -func WithAccountsWallet(wallet *accountwallet.AccountWallet) options.Option[EvilWallet] { - return func(opts *EvilWallet) { - opts.accWallet = wallet - } -} diff --git a/tools/evil-spammer/evilwallet/options.go b/tools/evil-spammer/evilwallet/options.go deleted file mode 100644 index 7b0b581f8..000000000 --- a/tools/evil-spammer/evilwallet/options.go +++ /dev/null @@ -1,295 +0,0 @@ -package evilwallet - -import ( - "time" - - "github.com/iotaledger/hive.go/ds/types" - "github.com/iotaledger/hive.go/ierrors" - "github.com/iotaledger/iota-core/tools/evil-spammer/models" - iotago "github.com/iotaledger/iota.go/v4" - "github.com/iotaledger/iota.go/v4/builder" -) - -// region Options /////////////////////////////////////////////////////////////////////////// - -// Options is a struct that represents a collection of options that can be set when creating a block. -type Options struct { - aliasInputs map[string]types.Empty - inputs []*models.Output - aliasOutputs map[string]iotago.Output - outputs []iotago.Output - inputWallet *Wallet - outputWallet *Wallet - outputBatchAliases map[string]types.Empty - reuse bool - issuingTime time.Time - allotmentStrategy models.AllotmentStrategy - issuerAccountID iotago.AccountID - // maps input alias to desired output type, used to create account output types - specialOutputTypes map[string]iotago.OutputType -} - -type OutputOption struct { - aliasName string - amount iotago.BaseToken - address *iotago.Ed25519Address - outputType iotago.OutputType -} - -// NewOptions is the constructor for the tx creation. -func NewOptions(options ...Option) (option *Options, err error) { - option = &Options{ - aliasInputs: make(map[string]types.Empty), - inputs: make([]*models.Output, 0), - aliasOutputs: make(map[string]iotago.Output), - outputs: make([]iotago.Output, 0), - specialOutputTypes: make(map[string]iotago.OutputType), - } - - for _, opt := range options { - opt(option) - } - - // check if alias and non-alias are mixed in use. - if err := option.checkInputsAndOutputs(); err != nil { - return nil, err - } - - // input and output wallets must be provided if inputs/outputs are not aliases. - if err := option.isWalletProvidedForInputsOutputs(); err != nil { - return nil, err - } - - if option.outputWallet == nil { - option.outputWallet = NewWallet() - } - - return -} - -// Option is the type that is used for options that can be passed into the CreateBlock method to configure its -// behavior. -type Option func(*Options) - -func (o *Options) isBalanceProvided() bool { - provided := false - - for _, output := range o.aliasOutputs { - if output.BaseTokenAmount() > 0 { - provided = true - } - } - - return provided -} - -func (o *Options) isWalletProvidedForInputsOutputs() error { - if o.areInputsProvidedWithoutAliases() { - if o.inputWallet == nil { - return ierrors.New("no input wallet provided for inputs without aliases") - } - } - if o.areOutputsProvidedWithoutAliases() { - if o.outputWallet == nil { - return ierrors.New("no output wallet provided for outputs without aliases") - } - } - - return nil -} - -func (o *Options) areInputsProvidedWithoutAliases() bool { - return len(o.inputs) > 0 -} - -func (o *Options) areOutputsProvidedWithoutAliases() bool { - return len(o.outputs) > 0 -} - -// checkInputsAndOutputs checks if either all provided inputs/outputs are with aliases or all are without, -// we do not allow for mixing those two possibilities. -func (o *Options) checkInputsAndOutputs() error { - inLength, outLength, aliasInLength, aliasOutLength := len(o.inputs), len(o.outputs), len(o.aliasInputs), len(o.aliasOutputs) - - if (inLength == 0 && aliasInLength == 0) || (outLength == 0 && aliasOutLength == 0) { - return ierrors.New("no inputs or outputs provided") - } - - inputsOk := (inLength > 0 && aliasInLength == 0) || (aliasInLength > 0 && inLength == 0) - outputsOk := (outLength > 0 && aliasOutLength == 0) || (aliasOutLength > 0 && outLength == 0) - if !inputsOk || !outputsOk { - return ierrors.New("mixing providing inputs/outputs with and without aliases is not allowed") - } - - return nil -} - -// WithInputs returns an Option that is used to provide the Inputs of the Transaction. -func WithInputs(inputs interface{}) Option { - return func(options *Options) { - switch in := inputs.(type) { - case string: - options.aliasInputs[in] = types.Void - case []string: - for _, input := range in { - options.aliasInputs[input] = types.Void - } - case *models.Output: - options.inputs = append(options.inputs, in) - case []*models.Output: - options.inputs = append(options.inputs, in...) - } - } -} - -// WithOutputs returns an Option that is used to define a non-colored Outputs for the Transaction in the Block. -func WithOutputs(outputsOptions []*OutputOption) Option { - return func(options *Options) { - for _, outputOptions := range outputsOptions { - var output iotago.Output - switch outputOptions.outputType { - case iotago.OutputBasic: - outputBuilder := builder.NewBasicOutputBuilder(outputOptions.address, outputOptions.amount) - output = outputBuilder.MustBuild() - case iotago.OutputAccount: - outputBuilder := builder.NewAccountOutputBuilder(outputOptions.address, outputOptions.address, outputOptions.amount) - output = outputBuilder.MustBuild() - } - - if outputOptions.aliasName != "" { - options.aliasOutputs[outputOptions.aliasName] = output - } else { - options.outputs = append(options.outputs, output) - } - } - } -} - -func WithIssuanceStrategy(strategy models.AllotmentStrategy, issuerID iotago.AccountID) Option { - return func(options *Options) { - options.allotmentStrategy = strategy - options.issuerAccountID = issuerID - } -} - -// WithInputWallet returns a BlockOption that is used to define the inputWallet of the Block. -func WithInputWallet(issuer *Wallet) Option { - return func(options *Options) { - options.inputWallet = issuer - } -} - -// WithOutputWallet returns a BlockOption that is used to define the inputWallet of the Block. -func WithOutputWallet(wallet *Wallet) Option { - return func(options *Options) { - options.outputWallet = wallet - } -} - -// WithOutputBatchAliases returns a BlockOption that is used to determine which outputs should be added to the outWallet. -func WithOutputBatchAliases(outputAliases map[string]types.Empty) Option { - return func(options *Options) { - options.outputBatchAliases = outputAliases - } -} - -// WithReuseOutputs returns a BlockOption that is used to enable deep spamming with Reuse wallet outputs. -func WithReuseOutputs() Option { - return func(options *Options) { - options.reuse = true - } -} - -// WithIssuingTime returns a BlockOption that is used to set issuing time of the Block. -func WithIssuingTime(issuingTime time.Time) Option { - return func(options *Options) { - options.issuingTime = issuingTime - } -} - -// ConflictSlice represents a set of conflict transactions. -type ConflictSlice [][]Option - -// endregion ////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// region FaucetRequestOptions ///////////////////////////////////////////////////////////////////////////////////////// - -// FaucetRequestOptions is options for faucet request. -type FaucetRequestOptions struct { - outputAliasName string -} - -// NewFaucetRequestOptions creates options for a faucet request. -func NewFaucetRequestOptions(options ...FaucetRequestOption) *FaucetRequestOptions { - reqOptions := &FaucetRequestOptions{ - outputAliasName: "", - } - - for _, option := range options { - option(reqOptions) - } - - return reqOptions -} - -// FaucetRequestOption is an option for faucet request. -type FaucetRequestOption func(*FaucetRequestOptions) - -// WithOutputAlias returns an Option that is used to provide the Output of the Transaction. -func WithOutputAlias(aliasName string) FaucetRequestOption { - return func(options *FaucetRequestOptions) { - options.outputAliasName = aliasName - } -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// region EvilScenario Options ///////////////////////////////////////////////////////////////////////////////////////// - -type ScenarioOption func(scenario *EvilScenario) - -// WithScenarioCustomConflicts specifies the EvilBatch that describes the UTXO structure that should be used for the spam. -func WithScenarioCustomConflicts(batch EvilBatch) ScenarioOption { - return func(options *EvilScenario) { - if batch != nil { - options.ConflictBatch = batch - } - } -} - -// WithScenarioDeepSpamEnabled enables deep spam, the outputs from available Reuse wallets or RestrictedReuse wallet -// if provided with WithReuseInputWalletForDeepSpam option will be used for spam instead fresh faucet outputs. -func WithScenarioDeepSpamEnabled() ScenarioOption { - return func(options *EvilScenario) { - options.Reuse = true - } -} - -// WithScenarioReuseOutputWallet the outputs from the spam will be saved into this wallet, accepted types of wallet: Reuse, RestrictedReuse. -func WithScenarioReuseOutputWallet(wallet *Wallet) ScenarioOption { - return func(options *EvilScenario) { - if wallet != nil { - if wallet.walletType == Reuse || wallet.walletType == RestrictedReuse { - options.OutputWallet = wallet - } - } - } -} - -// WithScenarioInputWalletForDeepSpam reuse set to true, outputs from this wallet will be used for deep spamming, -// allows for controllable building of UTXO deep structures. Accepts only RestrictedReuse wallet type. -func WithScenarioInputWalletForDeepSpam(wallet *Wallet) ScenarioOption { - return func(options *EvilScenario) { - if wallet.walletType == RestrictedReuse { - options.RestrictedInputWallet = wallet - } - } -} - -func WithCreateAccounts() ScenarioOption { - return func(options *EvilScenario) { - options.OutputType = iotago.OutputAccount - } -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/tools/evil-spammer/evilwallet/output_manager.go b/tools/evil-spammer/evilwallet/output_manager.go deleted file mode 100644 index e86ac7448..000000000 --- a/tools/evil-spammer/evilwallet/output_manager.go +++ /dev/null @@ -1,367 +0,0 @@ -package evilwallet - -import ( - "sync" - "sync/atomic" - "time" - - "github.com/iotaledger/hive.go/ds/types" - "github.com/iotaledger/hive.go/ierrors" - "github.com/iotaledger/hive.go/logger" - "github.com/iotaledger/hive.go/runtime/syncutils" - "github.com/iotaledger/iota-core/tools/evil-spammer/models" - iotago "github.com/iotaledger/iota.go/v4" -) - -const ( - awaitOutputToBeConfirmed = 10 * time.Second -) - -// OutputManager keeps track of the output statuses. -type OutputManager struct { - connector models.Connector - - wallets *Wallets - outputIDWalletMap map[string]*Wallet - outputIDAddrMap map[string]string - // stores solid outputs per node - issuerSolidOutIDMap map[string]map[iotago.OutputID]types.Empty - - log *logger.Logger - - syncutils.RWMutex -} - -// NewOutputManager creates an OutputManager instance. -func NewOutputManager(connector models.Connector, wallets *Wallets, log *logger.Logger) *OutputManager { - return &OutputManager{ - connector: connector, - wallets: wallets, - outputIDWalletMap: make(map[string]*Wallet), - outputIDAddrMap: make(map[string]string), - issuerSolidOutIDMap: make(map[string]map[iotago.OutputID]types.Empty), - log: log, - } -} - -// setOutputIDWalletMap sets wallet for the provided outputID. -func (o *OutputManager) setOutputIDWalletMap(outputID string, wallet *Wallet) { - o.Lock() - defer o.Unlock() - - o.outputIDWalletMap[outputID] = wallet -} - -// setOutputIDAddrMap sets address for the provided outputID. -func (o *OutputManager) setOutputIDAddrMap(outputID string, addr string) { - o.Lock() - defer o.Unlock() - - o.outputIDAddrMap[outputID] = addr -} - -// OutputIDWalletMap returns wallet corresponding to the outputID stored in OutputManager. -func (o *OutputManager) OutputIDWalletMap(outputID string) *Wallet { - o.RLock() - defer o.RUnlock() - - return o.outputIDWalletMap[outputID] -} - -// OutputIDAddrMap returns address corresponding to the outputID stored in OutputManager. -func (o *OutputManager) OutputIDAddrMap(outputID string) (addr string) { - o.RLock() - defer o.RUnlock() - - addr = o.outputIDAddrMap[outputID] - - return -} - -// SetOutputIDSolidForIssuer sets solid flag for the provided outputID and issuer. -func (o *OutputManager) SetOutputIDSolidForIssuer(outputID iotago.OutputID, issuer string) { - o.Lock() - defer o.Unlock() - - if _, ok := o.issuerSolidOutIDMap[issuer]; !ok { - o.issuerSolidOutIDMap[issuer] = make(map[iotago.OutputID]types.Empty) - } - o.issuerSolidOutIDMap[issuer][outputID] = types.Void -} - -// IssuerSolidOutIDMap checks whether output was marked as solid for a given node. -func (o *OutputManager) IssuerSolidOutIDMap(issuer string, outputID iotago.OutputID) (isSolid bool) { - o.RLock() - defer o.RUnlock() - - if solidOutputs, ok := o.issuerSolidOutIDMap[issuer]; ok { - if _, isSolid = solidOutputs[outputID]; isSolid { - return - } - } - - return -} - -// Track the confirmed statuses of the given outputIDs, it returns true if all of them are confirmed. -func (o *OutputManager) Track(outputIDs ...iotago.OutputID) (allConfirmed bool) { - var ( - wg sync.WaitGroup - unconfirmedOutputFound atomic.Bool - ) - - for _, ID := range outputIDs { - wg.Add(1) - - go func(id iotago.OutputID) { - defer wg.Done() - - if !o.AwaitOutputToBeAccepted(id, awaitOutputToBeConfirmed) { - unconfirmedOutputFound.Store(true) - } - }(ID) - } - wg.Wait() - - return !unconfirmedOutputFound.Load() -} - -// createOutputFromAddress creates output, retrieves outputID, and adds it to the wallet. -// Provided address should be generated from provided wallet. Considers only first output found on address. -func (o *OutputManager) createOutputFromAddress(w *Wallet, addr *iotago.Ed25519Address, balance iotago.BaseToken, outputID iotago.OutputID, outputStruct iotago.Output) *models.Output { - index := w.AddrIndexMap(addr.String()) - out := &models.Output{ - Address: addr, - Index: index, - OutputID: outputID, - Balance: balance, - OutputStruct: outputStruct, - } - w.AddUnspentOutput(out) - o.setOutputIDWalletMap(outputID.ToHex(), w) - o.setOutputIDAddrMap(outputID.ToHex(), addr.String()) - - return out -} - -// AddOutput adds existing output from wallet w to the OutputManager. -func (o *OutputManager) AddOutput(w *Wallet, output *models.Output) *models.Output { - idx := w.AddrIndexMap(output.Address.String()) - out := &models.Output{ - Address: output.Address, - Index: idx, - OutputID: output.OutputID, - Balance: output.Balance, - OutputStruct: output.OutputStruct, - } - w.AddUnspentOutput(out) - o.setOutputIDWalletMap(out.OutputID.ToHex(), w) - o.setOutputIDAddrMap(out.OutputID.ToHex(), output.Address.String()) - - return out -} - -// GetOutput returns the Output of the given outputID. -// Firstly checks if output can be retrieved by outputManager from wallet, if not does an API call. -func (o *OutputManager) GetOutput(outputID iotago.OutputID) (output *models.Output) { - output = o.getOutputFromWallet(outputID) - - // get output info via web api - if output == nil { - clt := o.connector.GetClient() - out := clt.GetOutput(outputID) - if out == nil { - return nil - } - - basicOutput, isBasic := out.(*iotago.BasicOutput) - if !isBasic { - return nil - } - - output = &models.Output{ - OutputID: outputID, - Address: basicOutput.UnlockConditionSet().Address().Address, - Balance: basicOutput.BaseTokenAmount(), - OutputStruct: basicOutput, - } - } - - return output -} - -func (o *OutputManager) getOutputFromWallet(outputID iotago.OutputID) (output *models.Output) { - o.RLock() - defer o.RUnlock() - w, ok := o.outputIDWalletMap[outputID.ToHex()] - if ok { - addr := o.outputIDAddrMap[outputID.ToHex()] - output = w.UnspentOutput(addr) - } - - return -} - -// RequestOutputsByAddress finds the unspent outputs of a given address and updates the provided output status map. -// func (o *OutputManager) RequestOutputsByAddress(address string) (outputIDs []iotago.OutputID) { -// s := time.Now() -// clt := o.connector.GetClient() -// for ; time.Since(s) < awaitOutputsByAddress; time.Sleep(1 * time.Second) { -// outputIDs, err := clt.GetAddressUnspentOutputs(address) -// if err == nil && len(outputIDs) > 0 { -// return outputIDs -// } -// } - -// 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{} - for _, output := range wallet.UnspentOutputs() { - wg.Add(1) - if output == nil { - continue - } - - var outs iotago.OutputIDs - outs = append(outs, output.OutputID) - - go func(outs iotago.OutputIDs) { - defer wg.Done() - - o.Track(outs...) - }(outs) - } - 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(awaitConfirmationSleep) { - confirmationState := clt.GetOutputConfirmationState(outputID) - if confirmationState == "confirmed" { - accepted = true - break - } - } - - return accepted -} - -// AwaitTransactionsConfirmation awaits for transaction confirmation and updates wallet with outputIDs. -func (o *OutputManager) AwaitTransactionsConfirmation(txIDs ...iotago.TransactionID) { - wg := sync.WaitGroup{} - semaphore := make(chan bool, 1) - - o.log.Debugf("Awaiting confirmation of %d transactions", len(txIDs)) - - for _, txID := range txIDs { - wg.Add(1) - go func(txID iotago.TransactionID) { - defer wg.Done() - semaphore <- true - defer func() { - <-semaphore - }() - err := o.AwaitTransactionToBeAccepted(txID, waitForConfirmation) - if err != nil { - return - } - }(txID) - } - wg.Wait() -} - -// AwaitTransactionToBeAccepted awaits for acceptance of a single transaction. -func (o *OutputManager) AwaitTransactionToBeAccepted(txID iotago.TransactionID, waitFor time.Duration) error { - s := time.Now() - clt := o.connector.GetClient() - var accepted bool - for ; time.Since(s) < waitFor; time.Sleep(awaitConfirmationSleep) { - confirmationState := clt.GetTransactionConfirmationState(txID) - o.log.Debugf("Tx %s confirmationState: %s", txID.ToHex(), confirmationState) - if 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 -} - -// AwaitOutputToBeSolid awaits for solidification of a single output by provided clt. -func (o *OutputManager) AwaitOutputToBeSolid(outID iotago.OutputID, clt models.Client, waitFor time.Duration) error { - s := time.Now() - var solid bool - - for ; time.Since(s) < waitFor; time.Sleep(awaitSolidificationSleep) { - solid = o.IssuerSolidOutIDMap(clt.URL(), outID) - if solid { - break - } - if output := clt.GetOutput(outID); output != nil { - o.SetOutputIDSolidForIssuer(outID, clt.URL()) - solid = true - - break - } - } - if !solid { - return ierrors.Errorf("output %s not solidified in time", outID) - } - - return nil -} - -// AwaitOutputsToBeSolid awaits for all provided outputs are solid for a provided client. -func (o *OutputManager) AwaitOutputsToBeSolid(outputs iotago.OutputIDs, clt models.Client, maxGoroutines int) (allSolid bool) { - wg := sync.WaitGroup{} - semaphore := make(chan bool, maxGoroutines) - allSolid = true - - for _, outID := range outputs { - wg.Add(1) - go func(outID iotago.OutputID) { - defer wg.Done() - semaphore <- true - defer func() { - <-semaphore - }() - err := o.AwaitOutputToBeSolid(outID, clt, waitForSolidification) - if err != nil { - allSolid = false - return - } - }(outID) - } - wg.Wait() - - return -} diff --git a/tools/evil-spammer/evilwallet/utils.go b/tools/evil-spammer/evilwallet/utils.go deleted file mode 100644 index 733d86987..000000000 --- a/tools/evil-spammer/evilwallet/utils.go +++ /dev/null @@ -1,25 +0,0 @@ -package evilwallet - -import iotago "github.com/iotaledger/iota.go/v4" - -// region utxo/tx realted functions //////////////////////////////////////////////////////////////////////////////////////////// - -// SplitBalanceEqually splits the balance equally between `splitNumber` outputs. -func SplitBalanceEqually(splitNumber int, balance iotago.BaseToken) []iotago.BaseToken { - outputBalances := make([]iotago.BaseToken, 0) - - // make sure the output balances are equal input - var totalBalance iotago.BaseToken - - // input is divided equally among outputs - for i := 0; i < splitNumber-1; i++ { - outputBalances = append(outputBalances, balance/iotago.BaseToken(splitNumber)) - totalBalance += outputBalances[i] - } - lastBalance := balance - totalBalance - outputBalances = append(outputBalances, lastBalance) - - return outputBalances -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/tools/evil-spammer/evilwallet/wallet.go b/tools/evil-spammer/evilwallet/wallet.go deleted file mode 100644 index 5b97d86f8..000000000 --- a/tools/evil-spammer/evilwallet/wallet.go +++ /dev/null @@ -1,259 +0,0 @@ -package evilwallet - -import ( - "crypto/ed25519" - - "go.uber.org/atomic" - - "github.com/iotaledger/hive.go/ds/types" - "github.com/iotaledger/hive.go/runtime/syncutils" - "github.com/iotaledger/iota-core/pkg/testsuite/mock" - "github.com/iotaledger/iota-core/tools/evil-spammer/models" - iotago "github.com/iotaledger/iota.go/v4" - "github.com/iotaledger/iota.go/v4/tpkg" -) - -// region Wallet /////////////////////////////////////////////////////////////////////////////////////////////////////// - -// Wallet is the definition of a wallet. -type Wallet struct { - ID walletID - walletType WalletType - unspentOutputs map[string]*models.Output // maps addr to its unspentOutput - indexAddrMap map[uint64]string - addrIndexMap map[string]uint64 - inputTransactions map[string]types.Empty - reuseAddressPool map[string]types.Empty - seed [32]byte - - lastAddrIdxUsed atomic.Int64 // used during filling in wallet with new outputs - lastAddrSpent atomic.Int64 // used during spamming with outputs one by one - - *syncutils.RWMutex -} - -// NewWallet creates a wallet of a given type. -func NewWallet(wType ...WalletType) *Wallet { - walletType := Other - if len(wType) > 0 { - walletType = wType[0] - } - idxSpent := atomic.NewInt64(-1) - addrUsed := atomic.NewInt64(-1) - - wallet := &Wallet{ - walletType: walletType, - ID: -1, - seed: tpkg.RandEd25519Seed(), - unspentOutputs: make(map[string]*models.Output), - indexAddrMap: make(map[uint64]string), - addrIndexMap: make(map[string]uint64), - inputTransactions: make(map[string]types.Empty), - lastAddrSpent: *idxSpent, - lastAddrIdxUsed: *addrUsed, - RWMutex: &syncutils.RWMutex{}, - } - - if walletType == Reuse { - wallet.reuseAddressPool = make(map[string]types.Empty) - } - - return wallet -} - -// Type returns the wallet type. -func (w *Wallet) Type() WalletType { - return w.walletType -} - -// Address returns a new and unused address of a given wallet. -func (w *Wallet) Address() *iotago.Ed25519Address { - w.Lock() - defer w.Unlock() - - index := uint64(w.lastAddrIdxUsed.Add(1)) - hdWallet := mock.NewHDWallet("", w.seed[:], index) - //nolint:forcetypeassert - addr := hdWallet.Address(iotago.AddressEd25519).(*iotago.Ed25519Address) - w.indexAddrMap[index] = addr.String() - w.addrIndexMap[addr.String()] = index - - return addr -} - -// AddressOnIndex returns a new and unused address of a given wallet. -func (w *Wallet) AddressOnIndex(index uint64) *iotago.Ed25519Address { - w.Lock() - defer w.Unlock() - - hdWallet := mock.NewHDWallet("", w.seed[:], index) - //nolint:forcetypeassert - addr := hdWallet.Address(iotago.AddressEd25519).(*iotago.Ed25519Address) - - return addr -} - -// UnspentOutput returns the unspent output on the address. -func (w *Wallet) UnspentOutput(addr string) *models.Output { - w.RLock() - defer w.RUnlock() - - return w.unspentOutputs[addr] -} - -// UnspentOutputs returns all unspent outputs on the wallet. -func (w *Wallet) UnspentOutputs() (outputs map[string]*models.Output) { - w.RLock() - defer w.RUnlock() - outputs = make(map[string]*models.Output) - for addr, outs := range w.unspentOutputs { - outputs[addr] = outs - } - - return outputs -} - -// IndexAddrMap returns the address for the index specified. -func (w *Wallet) IndexAddrMap(outIndex uint64) string { - w.RLock() - defer w.RUnlock() - - return w.indexAddrMap[outIndex] -} - -// AddrIndexMap returns the index for the address specified. -func (w *Wallet) AddrIndexMap(address string) uint64 { - w.RLock() - defer w.RUnlock() - - return w.addrIndexMap[address] -} - -// AddUnspentOutput adds an unspentOutput of a given wallet. -func (w *Wallet) AddUnspentOutput(output *models.Output) { - w.Lock() - defer w.Unlock() - w.unspentOutputs[output.Address.String()] = output -} - -// UnspentOutputBalance returns the balance on the unspent output sitting on the address specified. -func (w *Wallet) UnspentOutputBalance(addr string) iotago.BaseToken { - w.RLock() - defer w.RUnlock() - - total := iotago.BaseToken(0) - if out, ok := w.unspentOutputs[addr]; ok { - total += out.Balance - } - - return total -} - -// IsEmpty returns true if the wallet is empty. -func (w *Wallet) IsEmpty() (empty bool) { - switch w.walletType { - case Reuse: - empty = len(w.reuseAddressPool) == 0 - default: - empty = w.UnspentOutputsLength() == 0 - } - - return -} - -// UnspentOutputsLeft returns how many unused outputs are available in wallet. -func (w *Wallet) UnspentOutputsLeft() (left int) { - switch w.walletType { - case Reuse: - left = len(w.reuseAddressPool) - default: - left = int(w.lastAddrIdxUsed.Load() - w.lastAddrSpent.Load()) - } - - return -} - -// AddReuseAddress adds address to the reuse ready outputs' addresses pool for a Reuse wallet. -func (w *Wallet) AddReuseAddress(addr string) { - w.Lock() - defer w.Unlock() - - if w.walletType == Reuse { - w.reuseAddressPool[addr] = types.Void - } -} - -// GetReuseAddress get random address from reuse addresses reuseOutputsAddresses pool. Address is removed from the pool after selecting. -func (w *Wallet) GetReuseAddress() string { - w.Lock() - defer w.Unlock() - - if w.walletType == Reuse { - if len(w.reuseAddressPool) > 0 { - for addr := range w.reuseAddressPool { - delete(w.reuseAddressPool, addr) - return addr - } - } - } - - return "" -} - -// GetUnspentOutput returns an unspent output on the oldest address ordered by index. -func (w *Wallet) GetUnspentOutput() *models.Output { - switch w.walletType { - case Reuse: - addr := w.GetReuseAddress() - return w.UnspentOutput(addr) - default: - if w.lastAddrSpent.Load() < w.lastAddrIdxUsed.Load() { - idx := w.lastAddrSpent.Add(1) - addr := w.IndexAddrMap(uint64(idx)) - outs := w.UnspentOutput(addr) - - return outs - } - } - - return nil -} - -// Sign signs the tx essence. -func (w *Wallet) AddressSigner(addr *iotago.Ed25519Address) iotago.AddressSigner { - w.RLock() - defer w.RUnlock() - index := w.AddrIndexMap(addr.String()) - hdWallet := mock.NewHDWallet("", w.seed[:], index) - - return hdWallet.AddressSigner() -} - -func (w *Wallet) KeyPair(index uint64) (ed25519.PrivateKey, ed25519.PublicKey) { - w.RLock() - defer w.RUnlock() - hdWallet := mock.NewHDWallet("", w.seed[:], index) - - return hdWallet.KeyPair() -} - -// UpdateUnspentOutputID updates the unspent output on the address specified. -// func (w *Wallet) UpdateUnspentOutputID(addr string, outputID utxo.OutputID) error { -// w.RLock() -// walletOutput, ok := w.unspentOutputs[addr] -// w.RUnlock() -// if !ok { -// return errors.Errorf("could not find unspent output under provided address in the wallet, outID:%s, addr: %s", outputID.Base58(), addr) -// } -// w.Lock() -// walletOutput.OutputID = outputID -// w.Unlock() -// return nil -// } - -// UnspentOutputsLength returns the number of unspent outputs on the wallet. -func (w *Wallet) UnspentOutputsLength() int { - return len(w.unspentOutputs) -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/tools/evil-spammer/evilwallet/wallets.go b/tools/evil-spammer/evilwallet/wallets.go deleted file mode 100644 index 31e5c0366..000000000 --- a/tools/evil-spammer/evilwallet/wallets.go +++ /dev/null @@ -1,226 +0,0 @@ -package evilwallet - -import ( - "go.uber.org/atomic" - - "github.com/iotaledger/hive.go/ierrors" - "github.com/iotaledger/hive.go/runtime/syncutils" - "github.com/iotaledger/iota-core/tools/evil-spammer/models" -) - -type walletID int - -type ( - // WalletType is the type of the wallet. - //nolint:revive - WalletType int8 - //nolint:revive - WalletStatus int8 -) - -const ( - Other WalletType = iota - // Fresh is used for automatic Faucet Requests, outputs are returned one by one. - Fresh - // Reuse stores resulting outputs of double spends or transactions issued by the evilWallet, - // outputs from this wallet are reused in spamming scenario with flag reuse set to true and no RestrictedReuse wallet provided. - // Reusing spammed outputs allows for creation of deeper UTXO DAG structure. - Reuse - // RestrictedReuse it is a reuse wallet, that will be available only to spamming scenarios that - // will provide RestrictedWallet for the reuse spamming. - RestrictedReuse -) - -// region Wallets /////////////////////////////////////////////////////////////////////////////////////////////////////// - -// Wallets is a container of wallets. -type Wallets struct { - wallets map[walletID]*Wallet - // we store here non-empty wallets ids of wallets with Fresh faucet outputs. - faucetWallets []walletID - // reuse wallets are stored without an order, so they are picked up randomly. - // Boolean flag indicates if wallet is ready - no new addresses will be generated, so empty wallets can be deleted. - reuseWallets map[walletID]bool - mu syncutils.RWMutex - - lastWalletID atomic.Int64 -} - -// NewWallets creates and returns a new Wallets container. -func NewWallets() *Wallets { - return &Wallets{ - wallets: make(map[walletID]*Wallet), - faucetWallets: make([]walletID, 0), - reuseWallets: make(map[walletID]bool), - lastWalletID: *atomic.NewInt64(-1), - } -} - -// NewWallet adds a new wallet to Wallets and returns the created wallet. -func (w *Wallets) NewWallet(walletType WalletType) *Wallet { - wallet := NewWallet(walletType) - wallet.ID = walletID(w.lastWalletID.Add(1)) - - w.addWallet(wallet) - if walletType == Reuse { - w.addReuseWallet(wallet) - } - - return wallet -} - -// GetWallet returns the wallet with the specified ID. -func (w *Wallets) GetWallet(walletID walletID) *Wallet { - return w.wallets[walletID] -} - -// GetNextWallet get next non-empty wallet based on provided type. -func (w *Wallets) GetNextWallet(walletType WalletType, minOutputsLeft int) (*Wallet, error) { - w.mu.Lock() - defer w.mu.Unlock() - - switch walletType { - case Fresh: - if !w.IsFaucetWalletAvailable() { - return nil, ierrors.New("no faucet wallets available, need to request more funds") - } - - wallet := w.wallets[w.faucetWallets[0]] - if wallet.IsEmpty() { - return nil, ierrors.New("wallet is empty, need to request more funds") - } - - return wallet, nil - case Reuse: - for id, ready := range w.reuseWallets { - wal := w.wallets[id] - if wal.UnspentOutputsLeft() > minOutputsLeft { - // if are solid - - return wal, nil - } - // no outputs will be ever added to this wallet, so it can be deleted - if wal.IsEmpty() && ready { - w.removeReuseWallet(id) - } - } - - return nil, ierrors.New("no reuse wallets available") - } - - return nil, ierrors.New("wallet type not supported for ordered usage, use GetWallet by ID instead") -} - -func (w *Wallets) UnspentOutputsLeft(walletType WalletType) int { - w.mu.RLock() - defer w.mu.RUnlock() - outputsLeft := 0 - - switch walletType { - case Fresh: - for _, wID := range w.faucetWallets { - outputsLeft += w.wallets[wID].UnspentOutputsLeft() - } - case Reuse: - for wID := range w.reuseWallets { - outputsLeft += w.wallets[wID].UnspentOutputsLeft() - } - } - - return outputsLeft -} - -// addWallet stores newly created wallet. -func (w *Wallets) addWallet(wallet *Wallet) { - w.mu.Lock() - defer w.mu.Unlock() - - w.wallets[wallet.ID] = wallet -} - -// addReuseWallet stores newly created wallet. -func (w *Wallets) addReuseWallet(wallet *Wallet) { - w.mu.Lock() - defer w.mu.Unlock() - - w.reuseWallets[wallet.ID] = false -} - -// GetUnspentOutput gets first found unspent output for a given walletType. -func (w *Wallets) GetUnspentOutput(wallet *Wallet) *models.Output { - if wallet == nil { - return nil - } - - return wallet.GetUnspentOutput() -} - -// IsFaucetWalletAvailable checks if there is any faucet wallet left. -func (w *Wallets) IsFaucetWalletAvailable() bool { - return len(w.faucetWallets) > 0 -} - -// freshWallet returns the first non-empty wallet from the faucetWallets queue. If current wallet is empty, -// it is removed and the next enqueued one is returned. -func (w *Wallets) freshWallet() (*Wallet, error) { - wallet, err := w.GetNextWallet(Fresh, 1) - if err != nil { - w.removeFreshWallet() - wallet, err = w.GetNextWallet(Fresh, 1) - if err != nil { - return nil, err - } - } - - return wallet, nil -} - -// reuseWallet returns the first non-empty wallet from the reuseWallets queue. If current wallet is empty, -// it is removed and the next enqueued one is returned. -func (w *Wallets) reuseWallet(outputsNeeded int) *Wallet { - wallet, err := w.GetNextWallet(Reuse, outputsNeeded) - if err != nil { - return nil - } - - return wallet -} - -// removeWallet removes wallet, for Fresh wallet it will be the first wallet in a queue. -func (w *Wallets) removeFreshWallet() { - w.mu.Lock() - defer w.mu.Unlock() - - if w.IsFaucetWalletAvailable() { - removedID := w.faucetWallets[0] - w.faucetWallets = w.faucetWallets[1:] - delete(w.wallets, removedID) - } -} - -// removeWallet removes wallet, for Fresh wallet it will be the first wallet in a queue. -func (w *Wallets) removeReuseWallet(walletID walletID) { - if _, ok := w.reuseWallets[walletID]; ok { - delete(w.reuseWallets, walletID) - delete(w.wallets, walletID) - } -} - -// SetWalletReady makes wallet ready to use, Fresh wallet is added to freshWallets queue. -func (w *Wallets) SetWalletReady(wallet *Wallet) { - w.mu.Lock() - defer w.mu.Unlock() - - if wallet.IsEmpty() { - return - } - wType := wallet.walletType - switch wType { - case Fresh: - w.faucetWallets = append(w.faucetWallets, wallet.ID) - case Reuse: - w.reuseWallets[wallet.ID] = true - } -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/tools/evil-spammer/go.mod b/tools/evil-spammer/go.mod deleted file mode 100644 index 3a4476d1f..000000000 --- a/tools/evil-spammer/go.mod +++ /dev/null @@ -1,95 +0,0 @@ -module github.com/iotaledger/iota-core/tools/evil-spammer - -go 1.21 - -replace github.com/iotaledger/iota-core => ../../ - -replace github.com/iotaledger/iota-core/tools/genesis-snapshot => ../genesis-snapshot/ - -require ( - github.com/AlecAivazis/survey/v2 v2.3.7 - github.com/ethereum/go-ethereum v1.13.4 - 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-20231020115340-13da292c580b - github.com/iotaledger/hive.go/lo v0.0.0-20231020115340-13da292c580b - github.com/iotaledger/hive.go/logger v0.0.0-20231020115340-13da292c580b - github.com/iotaledger/hive.go/runtime v0.0.0-20231020115340-13da292c580b - github.com/iotaledger/iota-core v0.0.0-00010101000000-000000000000 - github.com/iotaledger/iota-core/tools/genesis-snapshot v0.0.0-00010101000000-000000000000 - github.com/iotaledger/iota.go/v4 v4.0.0-20231025071930-7cc5b35d50b1 - github.com/mr-tron/base58 v1.2.0 - go.uber.org/atomic v1.11.0 -) - -require ( - filippo.io/edwards25519 v1.0.0 // indirect - github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect - github.com/eclipse/paho.mqtt.golang v1.4.3 // indirect - github.com/fatih/structs v1.1.0 // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/go-stack/stack v1.8.1 // indirect - github.com/google/uuid v1.3.1 // indirect - github.com/gorilla/websocket v1.5.0 // indirect - github.com/holiman/uint256 v1.2.3 // indirect - github.com/iancoleman/orderedmap v0.3.0 // indirect - 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/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 - github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231020115340-13da292c580b // indirect - github.com/iotaledger/hive.go/stringify v0.0.0-20231020115340-13da292c580b // indirect - github.com/ipfs/go-cid v0.4.1 // indirect - github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect - github.com/klauspost/cpuid/v2 v2.2.5 // indirect - github.com/knadh/koanf v1.5.0 // indirect - github.com/kr/text v0.2.0 // indirect - github.com/libp2p/go-buffer-pool v0.1.0 // indirect - github.com/libp2p/go-libp2p v0.31.0 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect - github.com/minio/sha256-simd v1.0.1 // indirect - github.com/mitchellh/copystructure v1.2.0 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/multiformats/go-base32 v0.1.0 // indirect - github.com/multiformats/go-base36 v0.2.0 // indirect - github.com/multiformats/go-multiaddr v0.12.0 // indirect - github.com/multiformats/go-multibase v0.2.0 // indirect - github.com/multiformats/go-multicodec v0.9.0 // indirect - github.com/multiformats/go-multihash v0.2.3 // indirect - github.com/multiformats/go-varint v0.0.7 // indirect - github.com/otiai10/copy v1.14.0 // indirect - github.com/pasztorpisti/qs v0.0.0-20171216220353-8d6c33ee906c // indirect - github.com/pelletier/go-toml/v2 v2.1.0 // indirect - github.com/petermattis/goid v0.0.0-20230904192822-1876fd5063bc // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/pokt-network/smt v0.6.1 // indirect - github.com/sasha-s/go-deadlock v0.3.1 // indirect - github.com/spaolacci/murmur3 v1.1.0 // indirect - github.com/spf13/cast v1.5.1 // indirect - github.com/spf13/pflag v1.0.5 // indirect - github.com/stretchr/testify v1.8.4 // indirect - github.com/wollac/iota-crypto-demo v0.0.0-20221117162917-b10619eccb98 // indirect - github.com/zyedidia/generic v1.2.1 // indirect - go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.26.0 // indirect - golang.org/x/crypto v0.14.0 // indirect - golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/sync v0.4.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/term v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect - google.golang.org/protobuf v1.31.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect - lukechampine.com/blake3 v1.2.1 // indirect -) diff --git a/tools/evil-spammer/go.sum b/tools/evil-spammer/go.sum deleted file mode 100644 index 4a5dec9c2..000000000 --- a/tools/evil-spammer/go.sum +++ /dev/null @@ -1,581 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= -filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= -github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= -github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= -github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/aws/aws-sdk-go-v2 v1.9.2/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= -github.com/aws/aws-sdk-go-v2/config v1.8.3/go.mod h1:4AEiLtAb8kLs7vgw2ZV3p2VZ1+hBavOc84hqxVNpCyw= -github.com/aws/aws-sdk-go-v2/credentials v1.4.3/go.mod h1:FNNC6nQZQUuyhq5aE5c7ata8o9e4ECGmS4lAXC7o1mQ= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.6.0/go.mod h1:gqlclDEZp4aqJOancXK6TN24aKhT0W0Ae9MHk3wzTMM= -github.com/aws/aws-sdk-go-v2/internal/ini v1.2.4/go.mod h1:ZcBrrI3zBKlhGFNYWvju0I3TR93I7YIgAfy82Fh4lcQ= -github.com/aws/aws-sdk-go-v2/service/appconfig v1.4.2/go.mod h1:FZ3HkCe+b10uFZZkFdvf98LHW21k49W8o8J366lqVKY= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.2/go.mod h1:72HRZDLMtmVQiLG2tLfQcaWLCssELvGl+Zf2WVxMmR8= -github.com/aws/aws-sdk-go-v2/service/sso v1.4.2/go.mod h1:NBvT9R1MEF+Ud6ApJKM0G+IkPchKS7p7c2YPKwHmBOk= -github.com/aws/aws-sdk-go-v2/service/sts v1.7.2/go.mod h1:8EzeIqfWt2wWT4rJVu3f21TfrhJ8AEMzVybRNSb/b4g= -github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= -github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= -github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI= -github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= -github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/eclipse/paho.mqtt.golang v1.4.3 h1:2kwcUGn8seMUfWndX0hGbvH8r7crgcJguQNCyp70xik= -github.com/eclipse/paho.mqtt.golang v1.4.3/go.mod h1:CSYvoAlsMkhYOXh/oKyxa8EcBci6dVkLCbo5tTC1RIE= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/ethereum/go-ethereum v1.13.4 h1:25HJnaWVg3q1O7Z62LaaI6S9wVq8QCw3K88g8wEzrcM= -github.com/ethereum/go-ethereum v1.13.4/go.mod h1:I0U5VewuuTzvBtVzKo7b3hJzDhXOUtn9mJW7SsIPB0Q= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= -github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= -github.com/fjl/memsize v0.0.2 h1:27txuSD9or+NZlnOWdKUxeBzTAUkWCVh+4Gf2dWFOzA= -github.com/fjl/memsize v0.0.2/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= -github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= -github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= -github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= -github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= -github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= -github.com/hashicorp/consul/api v1.13.0/go.mod h1:ZlVrynguJKcYr54zGaDbaL3fOvKC9m72FhPvA8T35KQ= -github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= -github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= -github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= -github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= -github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= -github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q= -github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= -github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= -github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= -github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= -github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= -github.com/hjson/hjson-go/v4 v4.0.0 h1:wlm6IYYqHjOdXH1gHev4VoXCaW20HdQAGCxdOEEg2cs= -github.com/hjson/hjson-go/v4 v4.0.0/go.mod h1:KaYt3bTw3zhBjYqnXkYywcYctk0A2nxeEFTse3rH13E= -github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o= -github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= -github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc= -github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE= -github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7 h1:dTrD7X2PTNgli6EbS4tV9qu3QAm/kBU3XaYZV2xdzys= -github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7/go.mod h1:ZRdPu684P0fQ1z8sXz4dj9H5LWHhz4a9oCtvjunkSrw= -github.com/iotaledger/hive.go/ads v0.0.0-20231020115340-13da292c580b h1:D68khiAFb9DwTvjZc2nc4R0E6wUdKyYCUXkmdaMzuoQ= -github.com/iotaledger/hive.go/ads v0.0.0-20231020115340-13da292c580b/go.mod h1:IFh0gDfeMgZtfCo+5afK59IDR4xXh+cTR9YtLnZPcbY= -github.com/iotaledger/hive.go/app v0.0.0-20231020115340-13da292c580b h1:mX3NXaTMLEwZnEs4IlxEvXY0YZo8qbb8M1xM39FS6qY= -github.com/iotaledger/hive.go/app v0.0.0-20231020115340-13da292c580b/go.mod h1:8ZbIKR84oQd/3iQ5eeT7xpudO9/ytzXP7veIYnk7Orc= -github.com/iotaledger/hive.go/constraints v0.0.0-20231020115340-13da292c580b h1:HF4e0wz0JMIT4m3saqdQ//T9nWHV9d5sLMtEwNDuykM= -github.com/iotaledger/hive.go/constraints v0.0.0-20231020115340-13da292c580b/go.mod h1:dOBOM2s4se3HcWefPe8sQLUalGXJ8yVXw58oK8jke3s= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231020115340-13da292c580b h1:ZERXxhQBUBV1AqTE6cUI4vTxSx4JrnsMuLZFgj32xLM= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231020115340-13da292c580b/go.mod h1:Mc+ACqBGPxrPMIPUBOm6/HL0J6m0iVMwjtIEKW3uow8= -github.com/iotaledger/hive.go/crypto v0.0.0-20231020115340-13da292c580b h1:ZUUqRRO6XnQmVcXlXyx07vqySn28+bln6jp9KagYCjY= -github.com/iotaledger/hive.go/crypto v0.0.0-20231020115340-13da292c580b/go.mod h1:h3o6okvMSEK3KOX6pOp3yq1h9ohTkTfo6X8MzEadeb0= -github.com/iotaledger/hive.go/ds v0.0.0-20231020115340-13da292c580b h1:8b2sH+2Vf0y5BDYTMwKa09iQr3JF9JrzTI64DkXb+9U= -github.com/iotaledger/hive.go/ds v0.0.0-20231020115340-13da292c580b/go.mod h1:3XkUSKfHaVxGbT0XAvjNlVYqPzhfLTGhDtdNA5UBPco= -github.com/iotaledger/hive.go/ierrors v0.0.0-20231020115340-13da292c580b h1:JJPnr231djUTgTnE4oGz847WE9VA7Py6E6fgZwT5TQo= -github.com/iotaledger/hive.go/ierrors v0.0.0-20231020115340-13da292c580b/go.mod h1:HcE8B5lP96enc/OALTb2/rIIi+yOLouRoHOKRclKmC8= -github.com/iotaledger/hive.go/kvstore v0.0.0-20231020115340-13da292c580b h1:LusmtjpfG/q8lc15Fp9W3kABbN3tArKx/zw2ibdY1DU= -github.com/iotaledger/hive.go/kvstore v0.0.0-20231020115340-13da292c580b/go.mod h1:O/U3jtiUDeqqM0MZQFu2UPqS9fUm0C5hNISxlmg/thE= -github.com/iotaledger/hive.go/lo v0.0.0-20231020115340-13da292c580b h1:UvFWI8wQJS/XQOeWHpPsaFVeS2nxJ7nIGFr+IFjrnVw= -github.com/iotaledger/hive.go/lo v0.0.0-20231020115340-13da292c580b/go.mod h1:s4kzx9QY1MVWHJralj+3q5kI0eARtrJhphYD/iBbPfo= -github.com/iotaledger/hive.go/log v0.0.0-20231020115340-13da292c580b h1:IwhoeOeRu25mBdrimuOOvbbhHYX0QipibV69ubn8nX0= -github.com/iotaledger/hive.go/log v0.0.0-20231020115340-13da292c580b/go.mod h1:JvokzmpmFZPDskMlUqqjgHtD8usVJU4nAY/TNMGge8M= -github.com/iotaledger/hive.go/logger v0.0.0-20231020115340-13da292c580b h1:EhVgAU/f2J3VYZwP60dRdyfAeDU3c/gBzX9blKtQGKA= -github.com/iotaledger/hive.go/logger v0.0.0-20231020115340-13da292c580b/go.mod h1:aBfAfIB2GO/IblhYt5ipCbyeL9bXSNeAwtYVA3hZaHg= -github.com/iotaledger/hive.go/runtime v0.0.0-20231020115340-13da292c580b h1:O68POYIqBLnoHN+HIszc58QwAI2qocYq0WKGfVrXmMg= -github.com/iotaledger/hive.go/runtime v0.0.0-20231020115340-13da292c580b/go.mod h1:jRw8yFipiPaqmTPHh7hTcxAP9u6pjRGpByS3REJKkbY= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231020115340-13da292c580b h1:zaXZn9yV/95SRDkgCZQeBbSbmcJTKSZbCB7oBd71Qwg= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231020115340-13da292c580b/go.mod h1:SdK26z8/VhWtxaqCuQrufm80SELgowQPmu9T/8eUQ8g= -github.com/iotaledger/hive.go/stringify v0.0.0-20231020115340-13da292c580b h1:MDZhTZTVDiydXcW5j4TA7HixVCyAdToIMPhHfJee7cE= -github.com/iotaledger/hive.go/stringify v0.0.0-20231020115340-13da292c580b/go.mod h1:FTo/UWzNYgnQ082GI9QVM9HFDERqf9rw9RivNpqrnTs= -github.com/iotaledger/iota.go/v4 v4.0.0-20231025071930-7cc5b35d50b1 h1:7ChiFHg4KsdS4mKjYpFUUNe9t9nsQzNtJ/XnUV2w5ZM= -github.com/iotaledger/iota.go/v4 v4.0.0-20231025071930-7cc5b35d50b1/go.mod h1:jqbLYq4a/FwuiPBqFfkAwwxU8vs3+kReRq2/tyX5qRA= -github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= -github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= -github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= -github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/knadh/koanf v1.5.0 h1:q2TSd/3Pyc/5yP9ldIrSdIz26MCcyNQzW0pEAugLPNs= -github.com/knadh/koanf v1.5.0/go.mod h1:Hgyjp4y8v44hpZtPzs7JZfRAW5AhN7KfZcwv1RYggDs= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= -github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= -github.com/libp2p/go-libp2p v0.31.0 h1:LFShhP8F6xthWiBBq3euxbKjZsoRajVEyBS9snfHxYg= -github.com/libp2p/go-libp2p v0.31.0/go.mod h1:W/FEK1c/t04PbRH3fA9i5oucu5YcgrG0JVoBWT1B7Eg= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= -github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= -github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= -github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= -github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= -github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= -github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= -github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= -github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= -github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= -github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= -github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= -github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= -github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= -github.com/multiformats/go-multiaddr v0.12.0 h1:1QlibTFkoXJuDjjYsMHhE73TnzJQl8FSWatk/0gxGzE= -github.com/multiformats/go-multiaddr v0.12.0/go.mod h1:WmZXgObOQOYp9r3cslLlppkrz1FYSHmE834dfz/lWu8= -github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g= -github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk= -github.com/multiformats/go-multicodec v0.9.0 h1:pb/dlPnzee/Sxv/j4PmkDRxCOi3hXTz3IbPKOXWJkmg= -github.com/multiformats/go-multicodec v0.9.0/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k= -github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U= -github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= -github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= -github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= -github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/orcaman/writerseeker v0.0.0-20200621085525-1d3f536ff85e h1:s2RNOM/IGdY0Y6qfTeUKhDawdHDpK9RGBdx80qN4Ttw= -github.com/orcaman/writerseeker v0.0.0-20200621085525-1d3f536ff85e/go.mod h1:nBdnFKj15wFbf94Rwfq4m30eAcyY9V/IyKAGQFtqkW0= -github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= -github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= -github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks= -github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pasztorpisti/qs v0.0.0-20171216220353-8d6c33ee906c h1:Gcce/r5tSQeprxswXXOwQ/RBU1bjQWVd9dB7QKoPXBE= -github.com/pasztorpisti/qs v0.0.0-20171216220353-8d6c33ee906c/go.mod h1:1iCZ0433JJMecYqCa+TdWA9Pax8MGl4ByuNDZ7eSnQY= -github.com/pelletier/go-toml v1.7.0 h1:7utD74fnzVc/cpcyy8sjrlFr5vYpypUixARcHIMIGuI= -github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= -github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= -github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= -github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o= -github.com/petermattis/goid v0.0.0-20230904192822-1876fd5063bc h1:8bQZVK1X6BJR/6nYUPxQEP+ReTsceJTKizeuwjWOPUA= -github.com/petermattis/goid v0.0.0-20230904192822-1876fd5063bc/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/pokt-network/smt v0.6.1 h1:u5yTGNNND6edXv3vMQrAcjku1Ig4osehdu+EMYSXHUU= -github.com/pokt-network/smt v0.6.1/go.mod h1:CWgC9UzDxXJNkL7TEADnJXutZVMYzK/+dmBb37RWkeQ= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/rhnvrm/simples3 v0.6.1/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= -github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= -github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= -github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0= -github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= -github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= -github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/wollac/iota-crypto-demo v0.0.0-20221117162917-b10619eccb98 h1:i7k63xHOX2ntuHrhHewfKro67c834jug2DIk599fqAA= -github.com/wollac/iota-crypto-demo v0.0.0-20221117162917-b10619eccb98/go.mod h1:Knu2XMRWe8SkwTlHc/+ghP+O9DEaZRQQEyTjvLJ5Cck= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/zyedidia/generic v1.2.1 h1:Zv5KS/N2m0XZZiuLS82qheRG4X1o5gsWreGb0hR7XDc= -github.com/zyedidia/generic v1.2.1/go.mod h1:ly2RBz4mnz1yeuVbQA/VFwGjK3mnHGRj1JuoG336Bis= -go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= -go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= -go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= -go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= -go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= -go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY= -gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0= -gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI= -lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/tools/evil-spammer/identity/config.go b/tools/evil-spammer/identity/config.go deleted file mode 100644 index 4529b5055..000000000 --- a/tools/evil-spammer/identity/config.go +++ /dev/null @@ -1,72 +0,0 @@ -package identity - -import ( - "encoding/json" - "fmt" - "os" -) - -type Params map[string]Identities -type Identities map[string]privKeyURLPair -type privKeyURLPair [2]string - -var Config = Params{} - -var identityConfigJSON = `{ - "docker": { - "validator-1": [ - "8q491c3YWjbPwLmF2WD95YmCgh61j2kenCKHfGfByoWi", - "http://localhost:8080" - ], - "validator-2": [ - "4ata8GcTRMJ5sSv2jQJWmSYYTHct748p3tXmCFYm7wjA", - "http://localhost:8070" - ], - "validator-3": [ - "3YX6e7AL28hHihZewKdq6CMkEYVsTJBLgRiprUNiNq5E", - "http://localhost:8090" - ] - } -}` - -func LoadIdentity(networkName, alias string) (privKey, url string) { - fmt.Println("Loading identity", alias, "for network", networkName) - if networkIdentities, exists := Config[networkName]; exists { - if keyURLPair, ok := networkIdentities[alias]; ok { - privateKey := keyURLPair[0] - urlAPI := keyURLPair[1] - fmt.Println("Loaded identity", alias, "API url:", url, "for network", networkName, "...DONE") - - return privateKey, urlAPI - } - - return "", "" - } - - return "", "" -} - -func LoadConfig() Params { - // open config file - fname := "keys-config.json" - file, err := os.Open(fname) - if err != nil { - if !os.IsNotExist(err) { - panic(err) - } - if err = os.WriteFile(fname, []byte(identityConfigJSON), 0o600); err != nil { - panic(err) - } - } - defer file.Close() - // decode config file - fbytes, err := os.ReadFile(fname) - if err != nil { - panic(err) - } - if err = json.Unmarshal(fbytes, &Config); err != nil { - panic(err) - } - - return Config -} diff --git a/tools/evil-spammer/interactive/interactive.go b/tools/evil-spammer/interactive/interactive.go deleted file mode 100644 index 620f1c9b7..000000000 --- a/tools/evil-spammer/interactive/interactive.go +++ /dev/null @@ -1,899 +0,0 @@ -package interactive - -import ( - "encoding/json" - "fmt" - "io" - "os" - "strconv" - "strings" - "text/tabwriter" - "time" - - "github.com/AlecAivazis/survey/v2" - "go.uber.org/atomic" - - "github.com/iotaledger/hive.go/ds/types" - "github.com/iotaledger/hive.go/runtime/syncutils" - "github.com/iotaledger/iota-core/tools/evil-spammer/evilwallet" - "github.com/iotaledger/iota-core/tools/evil-spammer/programs" - "github.com/iotaledger/iota-core/tools/evil-spammer/spammer" - "github.com/iotaledger/iota.go/v4/nodeclient" -) - -const ( - faucetFundsCheck = time.Minute / 12 - maxConcurrentSpams = 5 - lastSpamsShowed = 15 - timeFormat = "2006/01/02 15:04:05" - configFilename = "interactive_config.json" -) - -const ( - AnswerEnable = "enable" - AnswerDisable = "disable" -) - -var ( - faucetTicker *time.Ticker - printer *Printer - minSpamOutputs int -) - -type InteractiveConfig struct { - //nolint:tagliatelle - WebAPI []string `json:"webAPI"` - Rate int `json:"rate"` - DurationStr string `json:"duration"` - TimeUnitStr string `json:"timeUnit"` - Deep bool `json:"deepEnabled"` - Reuse bool `json:"reuseEnabled"` - Scenario string `json:"scenario"` - AutoRequesting bool `json:"autoRequestingEnabled"` - AutoRequestingAmount string `json:"autoRequestingAmount"` - UseRateSetter bool `json:"useRateSetter"` - - duration time.Duration - timeUnit time.Duration - clientURLs map[string]types.Empty -} - -var configJSON = fmt.Sprintf(`{ - "webAPI": ["http://localhost:8080","http://localhost:8090"], - "rate": 2, - "duration": "20s", - "timeUnit": "1s", - "deepEnabled": false, - "reuseEnabled": true, - "scenario": "%s", - "autoRequestingEnabled": false, - "autoRequestingAmount": "100", - "useRateSetter": true -}`, spammer.TypeTx) - -var defaultConfig = InteractiveConfig{ - clientURLs: map[string]types.Empty{ - "http://localhost:8080": types.Void, - "http://localhost:8090": types.Void, - }, - Rate: 2, - duration: 20 * time.Second, - timeUnit: time.Second, - Deep: false, - Reuse: true, - Scenario: spammer.TypeTx, - AutoRequesting: false, - AutoRequestingAmount: "100", - UseRateSetter: true, -} - -const ( - requestAmount100 = "100" - requestAmount10k = "10000" -) - -// region survey selections /////////////////////////////////////////////////////////////////////////////////////////////////////// - -type action int - -const ( - actionWalletDetails action = iota - actionPrepareFunds - actionSpamMenu - actionCurrent - actionHistory - actionSettings - shutdown -) - -var actions = []string{"Evil wallet details", "Prepare faucet funds", "New spam", "Active spammers", "Spam history", "Settings", "Close"} - -const ( - spamScenario = "Change scenario" - spamType = "Update spam options" - spamDetails = "Update spam rate and duration" - startSpam = "Start the spammer" - back = "Go back" -) - -var spamMenuOptions = []string{startSpam, spamScenario, spamDetails, spamType, back} - -const ( - settingPreparation = "Auto funds requesting" - settingAddURLs = "Add client API url" - settingRemoveURLs = "Remove client API urls" - settingUseRateSetter = "Enable/disable rate setter" -) - -var settingsMenuOptions = []string{settingPreparation, settingAddURLs, settingRemoveURLs, settingUseRateSetter, back} - -const ( - currentSpamRemove = "Cancel spam" -) - -var currentSpamOptions = []string{currentSpamRemove, back} - -const ( - mpm string = "Minute, rate is [mpm]" - mps string = "Second, rate is [mps]" -) - -var ( - scenarios = []string{spammer.TypeBlock, spammer.TypeTx, spammer.TypeDs, "conflict-circle", "guava", "orange", "mango", "pear", "lemon", "banana", "kiwi", "peace"} - confirms = []string{AnswerEnable, AnswerDisable} - outputNumbers = []string{"100", "1000", "5000", "cancel"} - timeUnits = []string{mpm, mps} -) - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////// - -// region interactive /////////////////////////////////////////////////////////////////////////////////////////////////////// - -func Run() { - mode := NewInteractiveMode() - - printer = NewPrinter(mode) - - printer.printBanner() - mode.loadConfig() - time.Sleep(time.Millisecond * 100) - configure(mode) - go mode.runBackgroundTasks() - mode.menu() - - for { - select { - case id := <-mode.spamFinished: - mode.summarizeSpam(id) - case <-mode.mainMenu: - mode.menu() - case <-mode.shutdown: - printer.FarewellBlock() - mode.saveConfigsToFile() - os.Exit(0) - - return - } - } -} - -func configure(mode *Mode) { - faucetTicker = time.NewTicker(faucetFundsCheck) - switch mode.Config.AutoRequestingAmount { - case requestAmount100: - minSpamOutputs = 40 - case requestAmount10k: - minSpamOutputs = 2000 - } -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////// - -// region Mode ///////////////////////////////////////////////////////////////////////////////////////////////////////// - -type Mode struct { - evilWallet *evilwallet.EvilWallet - shutdown chan types.Empty - mainMenu chan types.Empty - spamFinished chan int - action chan action - - nextAction string - - preparingFunds bool - - Config InteractiveConfig - blkSent *atomic.Uint64 - txSent *atomic.Uint64 - scenariosSent *atomic.Uint64 - - activeSpammers map[int]*spammer.Spammer - spammerLog *SpammerLog - spamMutex syncutils.Mutex - - stdOutMutex syncutils.Mutex -} - -func NewInteractiveMode() *Mode { - return &Mode{ - evilWallet: evilwallet.NewEvilWallet(), - action: make(chan action), - shutdown: make(chan types.Empty), - mainMenu: make(chan types.Empty), - spamFinished: make(chan int), - - Config: defaultConfig, - blkSent: atomic.NewUint64(0), - txSent: atomic.NewUint64(0), - scenariosSent: atomic.NewUint64(0), - - spammerLog: NewSpammerLog(), - activeSpammers: make(map[int]*spammer.Spammer), - } -} - -func (m *Mode) runBackgroundTasks() { - for { - select { - case <-faucetTicker.C: - m.prepareFundsIfNeeded() - case act := <-m.action: - switch act { - case actionSpamMenu: - go m.spamMenu() - case actionWalletDetails: - m.walletDetails() - m.mainMenu <- types.Void - case actionPrepareFunds: - m.prepareFunds() - m.mainMenu <- types.Void - case actionHistory: - m.history() - m.mainMenu <- types.Void - case actionCurrent: - go m.currentSpams() - case actionSettings: - go m.settingsMenu() - case shutdown: - m.shutdown <- types.Void - } - } - } -} - -func (m *Mode) walletDetails() { - m.stdOutMutex.Lock() - defer m.stdOutMutex.Unlock() - - printer.EvilWalletStatus() -} - -func (m *Mode) menu() { - m.stdOutMutex.Lock() - defer m.stdOutMutex.Unlock() - err := survey.AskOne(actionQuestion, &m.nextAction) - if err != nil { - fmt.Println(err.Error()) - return - } - m.onMenuAction() -} - -func (m *Mode) onMenuAction() { - switch m.nextAction { - case actions[actionWalletDetails]: - m.action <- actionWalletDetails - case actions[actionPrepareFunds]: - m.action <- actionPrepareFunds - case actions[actionSpamMenu]: - m.action <- actionSpamMenu - case actions[actionSettings]: - m.action <- actionSettings - case actions[actionCurrent]: - m.action <- actionCurrent - case actions[actionHistory]: - m.action <- actionHistory - case actions[shutdown]: - m.action <- shutdown - } -} - -func (m *Mode) prepareFundsIfNeeded() { - if m.evilWallet.UnspentOutputsLeft(evilwallet.Fresh) < minSpamOutputs { - if !m.preparingFunds && m.Config.AutoRequesting { - m.preparingFunds = true - go func() { - switch m.Config.AutoRequestingAmount { - case requestAmount100: - _ = m.evilWallet.RequestFreshFaucetWallet() - case requestAmount10k: - _ = m.evilWallet.RequestFreshBigFaucetWallet() - } - m.preparingFunds = false - }() - } - } -} - -func (m *Mode) prepareFunds() { - m.stdOutMutex.Lock() - defer m.stdOutMutex.Unlock() - printer.DevNetFundsWarning() - - if m.preparingFunds { - printer.FundsCurrentlyPreparedWarning() - return - } - if len(m.Config.clientURLs) == 0 { - printer.NotEnoughClientsWarning(1) - } - numToPrepareStr := "" - err := survey.AskOne(fundsQuestion, &numToPrepareStr) - if err != nil { - fmt.Println(err.Error()) - return - } - switch numToPrepareStr { - case "100": - go func() { - m.preparingFunds = true - err = m.evilWallet.RequestFreshFaucetWallet() - m.preparingFunds = false - }() - case "1000": - go func() { - m.preparingFunds = true - _ = m.evilWallet.RequestFreshBigFaucetWallet() - m.preparingFunds = false - }() - case "cancel": - return - case "5000": - go func() { - m.preparingFunds = true - m.evilWallet.RequestFreshBigFaucetWallets(5) - m.preparingFunds = false - }() - } - - printer.StartedPreparingBlock(numToPrepareStr) -} - -func (m *Mode) spamMenu() { - m.stdOutMutex.Lock() - defer m.stdOutMutex.Unlock() - printer.SpammerSettings() - var submenu string - err := survey.AskOne(spamMenuQuestion, &submenu) - if err != nil { - fmt.Println(err.Error()) - return - } - - m.spamSubMenu(submenu) -} - -func (m *Mode) spamSubMenu(menuType string) { - switch menuType { - case spamDetails: - defaultTimeUnit := timeUnitToString(m.Config.duration) - var spamSurvey spamDetailsSurvey - err := survey.Ask(spamDetailsQuestions(strconv.Itoa(int(m.Config.duration.Seconds())), strconv.Itoa(m.Config.Rate), defaultTimeUnit), &spamSurvey) - if err != nil { - fmt.Println(err.Error()) - m.mainMenu <- types.Void - - return - } - m.parseSpamDetails(spamSurvey) - - case spamType: - var spamSurvey spamTypeSurvey - err := survey.Ask(spamTypeQuestions(boolToEnable(m.Config.Deep), boolToEnable(m.Config.Reuse)), &spamSurvey) - if err != nil { - fmt.Println(err.Error()) - m.mainMenu <- types.Void - - return - } - m.parseSpamType(spamSurvey) - - case spamScenario: - scenario := "" - err := survey.AskOne(spamScenarioQuestion(m.Config.Scenario), &scenario) - if err != nil { - fmt.Println(err.Error()) - m.mainMenu <- types.Void - - return - } - m.parseScenario(scenario) - - case startSpam: - if m.areEnoughFundsAvailable() { - printer.FundsWarning() - m.mainMenu <- types.Void - - return - } - if len(m.activeSpammers) >= maxConcurrentSpams { - printer.MaxSpamWarning() - m.mainMenu <- types.Void - - return - } - m.startSpam() - - case back: - m.mainMenu <- types.Void - return - } - m.action <- actionSpamMenu -} - -func (m *Mode) areEnoughFundsAvailable() bool { - outputsNeeded := m.Config.Rate * int(m.Config.duration.Seconds()) - if m.Config.timeUnit == time.Minute { - outputsNeeded = int(float64(m.Config.Rate) * m.Config.duration.Minutes()) - } - - return m.evilWallet.UnspentOutputsLeft(evilwallet.Fresh) < outputsNeeded && m.Config.Scenario != spammer.TypeBlock -} - -func (m *Mode) startSpam() { - m.spamMutex.Lock() - defer m.spamMutex.Unlock() - - var s *spammer.Spammer - if m.Config.Scenario == spammer.TypeBlock { - s = programs.SpamBlocks(m.evilWallet, m.Config.Rate, time.Second, m.Config.duration, 0, m.Config.UseRateSetter, "") - } else { - scenario, _ := evilwallet.GetScenario(m.Config.Scenario) - s = programs.SpamNestedConflicts(m.evilWallet, m.Config.Rate, time.Second, m.Config.duration, scenario, m.Config.Deep, m.Config.Reuse, m.Config.UseRateSetter, "") - if s == nil { - return - } - } - spamID := m.spammerLog.AddSpam(m.Config) - m.activeSpammers[spamID] = s - go func(id int) { - s.Spam() - m.spamFinished <- id - }(spamID) - printer.SpammerStartedBlock() -} - -func (m *Mode) settingsMenu() { - m.stdOutMutex.Lock() - defer m.stdOutMutex.Unlock() - printer.Settings() - var submenu string - err := survey.AskOne(settingsQuestion, &submenu) - if err != nil { - fmt.Println(err.Error()) - return - } - - m.settingsSubMenu(submenu) -} - -func (m *Mode) settingsSubMenu(menuType string) { - switch menuType { - case settingPreparation: - answer := "" - err := survey.AskOne(autoCreationQuestion, &answer) - if err != nil { - fmt.Println(err.Error()) - m.mainMenu <- types.Void - - return - } - m.onFundsCreation(answer) - - case settingAddURLs: - var url string - err := survey.AskOne(addURLQuestion, &url) - if err != nil { - fmt.Println(err.Error()) - m.mainMenu <- types.Void - - return - } - m.validateAndAddURL(url) - - case settingRemoveURLs: - answer := make([]string, 0) - urlsList := m.urlMapToList() - err := survey.AskOne(removeURLQuestion(urlsList), &answer) - if err != nil { - fmt.Println(err.Error()) - m.mainMenu <- types.Void - - return - } - m.removeUrls(answer) - - case settingUseRateSetter: - answer := "" - err := survey.AskOne(enableRateSetterQuestion, &answer) - if err != nil { - fmt.Println(err.Error()) - m.mainMenu <- types.Void - - return - } - m.onEnableRateSetter(answer) - - case back: - m.mainMenu <- types.Void - return - } - m.action <- actionSettings -} - -func (m *Mode) validateAndAddURL(url string) { - url = "http://" + url - ok := validateURL(url) - if !ok { - printer.URLWarning() - } else { - if _, ok := m.Config.clientURLs[url]; ok { - printer.URLExists() - return - } - m.Config.clientURLs[url] = types.Void - m.evilWallet.AddClient(url) - } -} - -func (m *Mode) onFundsCreation(answer string) { - if answer == AnswerEnable { - m.Config.AutoRequesting = true - printer.AutoRequestingEnabled() - m.prepareFundsIfNeeded() - } else { - m.Config.AutoRequesting = false - } -} - -func (m *Mode) onEnableRateSetter(answer string) { - if answer == AnswerEnable { - m.Config.UseRateSetter = true - printer.RateSetterEnabled() - } else { - m.Config.UseRateSetter = false - } -} - -func (m *Mode) history() { - m.stdOutMutex.Lock() - defer m.stdOutMutex.Unlock() - printer.History() -} - -func (m *Mode) currentSpams() { - m.stdOutMutex.Lock() - defer m.stdOutMutex.Unlock() - - if len(m.activeSpammers) == 0 { - printer.Println(printer.colorString("There are no currently running spammers.", "red"), 1) - fmt.Println("") - m.mainMenu <- types.Void - - return - } - printer.CurrentSpams() - answer := "" - err := survey.AskOne(currentMenuQuestion, &answer) - if err != nil { - fmt.Println(err.Error()) - m.mainMenu <- types.Void - - return - } - - m.currentSpamsSubMenu(answer) -} - -func (m *Mode) currentSpamsSubMenu(menuType string) { - switch menuType { - case currentSpamRemove: - if len(m.activeSpammers) == 0 { - printer.NoActiveSpammer() - } else { - answer := "" - err := survey.AskOne(removeSpammer, &answer) - if err != nil { - fmt.Println(err.Error()) - m.mainMenu <- types.Void - - return - } - m.parseIDToRemove(answer) - } - - m.action <- actionCurrent - - case back: - m.mainMenu <- types.Void - return - } -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// region parsers ///////////////////////////////////////////////////////////////////////////////////////////////////////////// - -func (m *Mode) parseSpamDetails(details spamDetailsSurvey) { - d, _ := strconv.Atoi(details.SpamDuration) - dur := time.Second * time.Duration(d) - rate, err := strconv.Atoi(details.SpamRate) - if err != nil { - return - } - switch details.TimeUnit { - case mpm: - m.Config.timeUnit = time.Minute - case mps: - m.Config.timeUnit = time.Second - } - m.Config.Rate = rate - m.Config.duration = dur - fmt.Println(details) -} - -func (m *Mode) parseSpamType(spamType spamTypeSurvey) { - deep := enableToBool(spamType.DeepSpamEnabled) - reuse := enableToBool(spamType.ReuseLaterEnabled) - m.Config.Deep = deep - m.Config.Reuse = reuse -} - -func (m *Mode) parseScenario(scenario string) { - m.Config.Scenario = scenario -} - -func (m *Mode) removeUrls(urls []string) { - for _, url := range urls { - if _, ok := m.Config.clientURLs[url]; ok { - delete(m.Config.clientURLs, url) - m.evilWallet.RemoveClient(url) - } - } -} - -func (m *Mode) urlMapToList() (list []string) { - for url := range m.Config.clientURLs { - list = append(list, url) - } - - return -} - -func (m *Mode) parseIDToRemove(answer string) { - m.spamMutex.Lock() - defer m.spamMutex.Unlock() - - id, err := strconv.Atoi(answer) - if err != nil { - return - } - m.summarizeSpam(id) -} - -func (m *Mode) summarizeSpam(id int) { - if s, ok := m.activeSpammers[id]; ok { - m.updateSentStatistic(s, id) - m.spammerLog.SetSpamEndTime(id) - delete(m.activeSpammers, id) - } else { - printer.ClientNotFoundWarning(id) - } -} - -func (m *Mode) updateSentStatistic(s *spammer.Spammer, id int) { - blkSent := s.BlocksSent() - scenariosCreated := s.BatchesPrepared() - if m.spammerLog.SpamDetails(id).Scenario == spammer.TypeBlock { - m.blkSent.Add(blkSent) - } else { - m.txSent.Add(blkSent) - } - m.scenariosSent.Add(scenariosCreated) -} - -// load the config file. -func (m *Mode) loadConfig() { - // open config file - file, err := os.Open(configFilename) - if err != nil { - if !os.IsNotExist(err) { - panic(err) - } - - //nolint:gosec // users should be able to read the file - if err = os.WriteFile("config.json", []byte(configJSON), 0o644); err != nil { - panic(err) - } - if file, err = os.Open("config.json"); err != nil { - panic(err) - } - } - defer file.Close() - - // decode config file - if err = json.NewDecoder(file).Decode(&m.Config); err != nil { - panic(err) - } - // convert urls array to map - if len(m.Config.WebAPI) > 0 { - // rewrite default value - for url := range m.Config.clientURLs { - m.evilWallet.RemoveClient(url) - } - m.Config.clientURLs = make(map[string]types.Empty) - } - for _, url := range m.Config.WebAPI { - m.Config.clientURLs[url] = types.Void - m.evilWallet.AddClient(url) - } - // parse duration - d, err := time.ParseDuration(m.Config.DurationStr) - if err != nil { - d = time.Minute - } - u, err := time.ParseDuration(m.Config.TimeUnitStr) - if err != nil { - u = time.Second - } - m.Config.duration = d - m.Config.timeUnit = u -} - -func (m *Mode) saveConfigsToFile() { - // open config file - file, err := os.Open("config.json") - if err != nil { - panic(err) - } - defer file.Close() - - // update client urls - m.Config.WebAPI = []string{} - for url := range m.Config.clientURLs { - m.Config.WebAPI = append(m.Config.WebAPI, url) - } - - // update duration - m.Config.DurationStr = m.Config.duration.String() - - // update time unit - m.Config.TimeUnitStr = m.Config.timeUnit.String() - - jsonConfigs, err := json.MarshalIndent(m.Config, "", " ") - if err != nil { - panic(err) - } - //nolint:gosec // users should be able to read the file - if err = os.WriteFile("config.json", jsonConfigs, 0o644); err != nil { - panic(err) - } -} - -func enableToBool(e string) bool { - return e == AnswerEnable -} - -func boolToEnable(b bool) string { - if b { - return AnswerEnable - } - - return AnswerDisable -} - -func validateURL(url string) (ok bool) { - _, err := nodeclient.New(url) - if err != nil { - return - } - - return true -} - -func timeUnitToString(d time.Duration) string { - durStr := d.String() - - if strings.Contains(durStr, "s") { - return mps - } - - return mpm -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// region SpammerLog /////////////////////////////////////////////////////////////////////////////////////////////////////////// - -var ( - historyHeader = "scenario\tstart\tstop\tdeep\treuse\trate\tduration" - historyLineFmt = "%s\t%s\t%s\t%v\t%v\t%d\t%d\n" -) - -type SpammerLog struct { - spamDetails []InteractiveConfig - spamStartTime []time.Time - spamStopTime []time.Time - mu syncutils.Mutex -} - -func NewSpammerLog() *SpammerLog { - return &SpammerLog{ - spamDetails: make([]InteractiveConfig, 0), - spamStartTime: make([]time.Time, 0), - spamStopTime: make([]time.Time, 0), - } -} - -func (s *SpammerLog) SpamDetails(spamID int) *InteractiveConfig { - return &s.spamDetails[spamID] -} - -func (s *SpammerLog) StartTime(spamID int) time.Time { - return s.spamStartTime[spamID] -} - -func (s *SpammerLog) AddSpam(config InteractiveConfig) (spamID int) { - s.mu.Lock() - defer s.mu.Unlock() - - s.spamDetails = append(s.spamDetails, config) - s.spamStartTime = append(s.spamStartTime, time.Now()) - s.spamStopTime = append(s.spamStopTime, time.Time{}) - - return len(s.spamDetails) - 1 -} - -func (s *SpammerLog) SetSpamEndTime(spamID int) { - s.mu.Lock() - defer s.mu.Unlock() - - s.spamStopTime[spamID] = time.Now() -} - -func newTabWriter(writer io.Writer) *tabwriter.Writer { - return tabwriter.NewWriter(writer, 0, 0, 1, ' ', tabwriter.Debug|tabwriter.TabIndent) -} - -func (s *SpammerLog) LogHistory(lastLines int, writer io.Writer) { - s.mu.Lock() - defer s.mu.Unlock() - - w := newTabWriter(writer) - fmt.Fprintln(w, historyHeader) - idx := len(s.spamDetails) - lastLines + 1 - if idx < 0 { - idx = 0 - } - for i, spam := range s.spamDetails[idx:] { - fmt.Fprintf(w, historyLineFmt, spam.Scenario, s.spamStartTime[i].Format(timeFormat), s.spamStopTime[i].Format(timeFormat), - spam.Deep, spam.Deep, spam.Rate, int(spam.duration.Seconds())) - } - w.Flush() -} - -func (s *SpammerLog) LogSelected(lines []int, writer io.Writer) { - s.mu.Lock() - defer s.mu.Unlock() - - w := newTabWriter(writer) - fmt.Fprintln(w, historyHeader) - for _, idx := range lines { - spam := s.spamDetails[idx] - fmt.Fprintf(w, historyLineFmt, spam.Scenario, s.spamStartTime[idx].Format(timeFormat), s.spamStopTime[idx].Format(timeFormat), - spam.Deep, spam.Deep, spam.Rate, int(spam.duration.Seconds())) - } - w.Flush() -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/tools/evil-spammer/interactive/menu.go b/tools/evil-spammer/interactive/menu.go deleted file mode 100644 index 3f91c8c00..000000000 --- a/tools/evil-spammer/interactive/menu.go +++ /dev/null @@ -1,258 +0,0 @@ -package interactive - -import ( - "fmt" - "os" - "time" - - "github.com/iotaledger/iota-core/tools/evil-spammer/evilwallet" -) - -// region Printer ///////////////////////////////////////////////////////////////////////////////////////////////////////// - -type Printer struct { - mode *Mode -} - -func NewPrinter(mode *Mode) *Printer { - return &Printer{ - mode: mode, - } -} - -func (p *Printer) Println(s string, indent int) { - pre := "█" - for i := 0; i < indent; i++ { - pre += "▓" - } - fmt.Println(pre, s) -} - -func (p *Printer) PrintlnPoint(s string, indent int) { - pre := "" - for i := 0; i < indent; i++ { - pre += " " - } - fmt.Println(pre, "▀▄", s) -} - -func (p *Printer) PrintlnInput(s string) { - fmt.Println("█▓>>", s) -} - -func (p *Printer) PrintThickLine() { - fmt.Println("\n ooo▄▄▓░░▀▀▀▀▄▓▓░░▄▄▄▓▓░░▄▒▄▀█▒▓▄▓▓░░▄▄▒▄▄█▒▓▄▄▀▀▄▓▒▄▄█▒▓▓▀▓▓░░░░█▒▄▄█▒▓░▄▄ooo") - fmt.Println() -} - -func (p *Printer) PrintTopLine() { - fmt.Println("▀▄---------------------------------------------------------------------------▄▀") -} - -func (p *Printer) PrintLine() { - fmt.Println("▄▀___________________________________________________________________________▀▄") -} - -func (p *Printer) printBanner() { - fmt.Println("▓█████ ██▒ █▓ ██▓ ██▓ \n▓█ ▀ ▓██░ █▒▓██▒▓██▒ \n▒███ ▓██ █▒░▒██▒▒██░ \n▒▓█ ▄ ▒██ █░░░██░▒██░ \n░▒████▒ ▒▀█░ ░██░░██████▒ \n░░ ▒░ ░ ░ ▐░ ░▓ ░ ▒░▓ ░ \n ░ ░ ░ ░ ░░ ▒ ░░ ░ ▒ ░ \n ░ ░░ ▒ ░ ░ ░ \n ░ ░ ░ ░ ░ ░ \n ░ \n ██████ ██▓███ ▄▄▄ ███▄ ▄███▓ ███▄ ▄███▓▓█████ ██▀███ \n ▒██ ▒ ▓██░ ██▒▒████▄ ▓██▒▀█▀ ██▒▓██▒▀█▀ ██▒▓█ ▀ ▓██ ▒ ██▒\n ░ ▓██▄ ▓██░ ██▓▒▒██ ▀█▄ ▓██ ▓██░▓██ ▓██░▒███ ▓██ ░▄█ ▒\n ▒ ██▒▒██▄█▓▒ ▒░██▄▄▄▄██ ▒██ ▒██ ▒██ ▒██ ▒▓█ ▄ ▒██▀▀█▄ \n ▒██████▒▒▒██▒ ░ ░ ▓█ ▓██▒▒██▒ ░██▒▒██▒ ░██▒░▒████▒░██▓ ▒██▒\n ▒ ▒▓▒ ▒ ░▒▓▒░ ░ ░ ▒▒ ▓▒█░░ ▒░ ░ ░░ ▒░ ░ ░░░ ▒░ ░░ ▒▓ ░▒▓░\n ░ ░▒ ░ ░░▒ ░ ▒ ▒▒ ░░ ░ ░░ ░ ░ ░ ░ ░ ░▒ ░ ▒░\n ░ ░ ░ ░░ ░ ▒ ░ ░ ░ ░ ░ ░░ ░ \n ░ ░ ░ ░ ░ ░ ░ ░ \n ") - p.PrintThickLine() - p.Println("Interactive mode enabled", level1) - fmt.Println() -} - -func (p *Printer) EvilWalletStatus() { - p.PrintTopLine() - p.Println(p.colorString("Evil Wallet status:", "cyan"), level2) - p.PrintlnPoint(fmt.Sprintf("Available faucet outputs: %d", p.mode.evilWallet.UnspentOutputsLeft(evilwallet.Fresh)), level2) - p.PrintlnPoint(fmt.Sprintf("Available reuse outputs: %d", p.mode.evilWallet.UnspentOutputsLeft(evilwallet.Reuse)), level2) - p.PrintlnPoint(fmt.Sprintf("Spammed blocks: %d", p.mode.blkSent.Load()), level2) - p.PrintlnPoint(fmt.Sprintf("Spammed transactions: %d", p.mode.txSent.Load()), level2) - p.PrintlnPoint(fmt.Sprintf("Spammed scenario batches: %d", p.mode.scenariosSent.Load()), level2) - - p.PrintLine() - fmt.Println() -} - -func (p *Printer) SpammerSettings() { - rateUnit := "[mpm]" - if p.mode.Config.timeUnit == time.Second { - rateUnit = "[mps]" - } - p.PrintTopLine() - p.Println(p.colorString("Current settings:", "cyan"), level1) - p.PrintlnPoint(fmt.Sprintf("Scenario: %s", p.mode.Config.Scenario), level2) - p.PrintlnPoint(fmt.Sprintf("Deep: %v, Reuse: %v", p.mode.Config.Deep, p.mode.Config.Reuse), level2) - p.PrintlnPoint(fmt.Sprintf("Use rate-setter: %v", p.mode.Config.UseRateSetter), level2) - p.PrintlnPoint(fmt.Sprintf("Rate: %d%s, Duration: %d[s]", p.mode.Config.Rate, rateUnit, int(p.mode.Config.duration.Seconds())), level2) - p.PrintLine() - fmt.Println() -} - -func (p *Printer) FarewellBlock() { - p.PrintTopLine() - fmt.Println(" ups... we're forgetting all your private keys ;)") - p.PrintLine() -} - -func (p *Printer) FundsWarning() { - p.Println(p.colorString("Not enough fresh faucet outputs in the wallet to spam!", "red"), level1) - if p.mode.preparingFunds { - p.PrintlnPoint(p.colorString("Funds are currently prepared, wait until outputs will be available.", "yellow"), level2) - } else { - p.PrintlnPoint(p.colorString("Request more outputs manually with 'Prepare faucet funds' option in main menu.", "yellow"), level2) - p.PrintlnPoint(p.colorString("You can also enable auto funds requesting in the settings.", "yellow"), level2) - } - fmt.Println() -} - -func (p *Printer) URLWarning() { - p.Println(p.colorString("Could not connect to provided API endpoint, client not added.", "yellow"), level2) - fmt.Println() -} - -func (p *Printer) URLExists() { - p.Println(p.colorString("The url already exists.", "red"), level2) - fmt.Println() -} - -func (p *Printer) DevNetFundsWarning() { - p.Println(p.colorString("Warning: Preparing 10k outputs and more could take looong time in the DevNet due to high PoW and congestion.", "yellow"), level1) - p.Println(p.colorString("We advice to use 100 option only.", "yellow"), level1) - fmt.Println() -} - -func (p *Printer) NotEnoughClientsWarning(numOfClient int) { - p.Println(p.colorString(fmt.Sprintf("Warning: At least %d clients is recommended if double spends are not allowed from the same node.", numOfClient), "red"), level2) - fmt.Println() -} - -func (p *Printer) clients() { - p.Println(p.colorString("Provided clients:", "cyan"), level1) - for url := range p.mode.Config.clientURLs { - p.PrintlnPoint(url, level2) - } -} - -func (p *Printer) colorString(s string, color string) string { - colorStringReset := "\033[0m" - colorString := "" - switch color { - case "red": - colorString = "\033[31m" - case "cyan": - colorString = "\033[36m" - case "green": - colorString = "\033[32m" - case "yellow": - colorString = "\033[33m" - } - - return colorString + s + colorStringReset -} - -func (p *Printer) Settings() { - p.PrintTopLine() - p.Println(p.colorString("Current settings:", "cyan"), 0) - p.Println(fmt.Sprintf("Auto requesting enabled: %v", p.mode.Config.AutoRequesting), level1) - p.Println(fmt.Sprintf("Use rate-setter: %v", p.mode.Config.UseRateSetter), level1) - p.clients() - p.PrintLine() - fmt.Println() -} - -func (p *Printer) MaxSpamWarning() { - p.Println("", level2) - p.Println(p.colorString("Cannot spam. Maximum number of concurrent spams achieved.", "red"), level1) - p.Println("", level2) - fmt.Println() -} - -func (p *Printer) CurrentSpams() { - p.mode.spamMutex.Lock() - defer p.mode.spamMutex.Unlock() - - lines := make([]string, 0) - for id := range p.mode.activeSpammers { - details := p.mode.spammerLog.SpamDetails(id) - startTime := p.mode.spammerLog.StartTime(id) - endTime := startTime.Add(details.duration) - timeLeft := int(time.Until(endTime).Seconds()) - lines = append(lines, fmt.Sprintf("ID: %d, scenario: %s, time left: %d [s]", id, details.Scenario, timeLeft)) - } - if len(lines) == 0 { - p.Println(p.colorString("There are no currently running spams.", "red"), level1) - return - } - p.Println(p.colorString("Currently active spammers:", "green"), level1) - for _, line := range lines { - p.PrintlnPoint(line, level2) - } - p.PrintLine() - fmt.Println() -} - -func (p *Printer) History() { - p.PrintTopLine() - p.Println(fmt.Sprintf(p.colorString("List of last %d started spams.", "cyan"), lastSpamsShowed), level1) - p.mode.spammerLog.LogHistory(lastSpamsShowed, os.Stdout) - p.PrintLine() - fmt.Println() -} - -func (p *Printer) ClientNotFoundWarning(id int) { - p.Println("", level2) - p.Println(p.colorString(fmt.Sprintf("No spam with id %d found. Nothing removed.", id), "red"), level1) - p.Println("", level2) - - fmt.Println() -} - -func (p *Printer) NoActiveSpammer() { - p.Println("", level2) - p.Println(p.colorString("No active spammers.", "red"), level1) - p.Println("", level2) - - fmt.Println() -} - -func (p *Printer) FundsCurrentlyPreparedWarning() { - p.Println("", level2) - p.Println(p.colorString("Funds are currently prepared. Try again later.", "red"), level1) - p.Println("", level2) - fmt.Println() -} - -func (p *Printer) StartedPreparingBlock(numToPrepareStr string) { - p.Println("", level2) - p.Println(p.colorString("Start preparing "+numToPrepareStr+" faucet outputs.", "green"), level1) - p.Println("", level2) - fmt.Println() -} - -func (p *Printer) SpammerStartedBlock() { - p.Println("", level2) - p.Println(p.colorString("Spammer started", "green"), level1) - p.Println("", level2) - fmt.Println() -} - -func (p *Printer) AutoRequestingEnabled() { - p.Println("", level2) - p.Println(p.colorString(fmt.Sprintf("Automatic funds requesting enabled. %s outputs will be requested whenever output amout will go below %d.", p.mode.Config.AutoRequestingAmount, minSpamOutputs), "green"), level1) - p.Println(p.colorString("The size of the request can be changed in the config file. Possible values: '100', '10000'", "yellow"), level1) - p.Println("", level2) -} - -func (p *Printer) RateSetterEnabled() { - p.Println("", level2) - p.Println(p.colorString("Enable waiting the rate-setter estimate.", "green"), level1) - p.Println(p.colorString(" Enabling this will force the spammer to sleep certain amount of time based on node's rate-setter estimate.", "yellow"), level1) - p.Println("", level2) -} - -const ( - level1 = 1 - level2 = 2 -) - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/tools/evil-spammer/interactive/survey.go b/tools/evil-spammer/interactive/survey.go deleted file mode 100644 index 04d0530b4..000000000 --- a/tools/evil-spammer/interactive/survey.go +++ /dev/null @@ -1,151 +0,0 @@ -package interactive - -import ( - "strconv" - - "github.com/AlecAivazis/survey/v2" - - "github.com/iotaledger/hive.go/ierrors" -) - -// region survey ////////////////////////////////////////////////////////////////////////////////////////////// - -var actionQuestion = &survey.Select{ - Message: "Choose an action", - Options: actions, - Default: "Evil wallet details", -} - -var fundsQuestion = &survey.Select{ - Message: "How many fresh outputs you want to create?", - Options: outputNumbers, - Default: "100", -} - -var settingsQuestion = &survey.Select{ - Message: "Available settings:", - Options: settingsMenuOptions, - Default: settingPreparation, -} - -var autoCreationQuestion = &survey.Select{ - Message: "Enable automatic faucet output creation", - Options: confirms, - Default: "enable", -} - -var enableRateSetterQuestion = &survey.Select{ - Message: "Enable using rate-setter estimate", - Options: confirms, - Default: "enable", -} - -var addURLQuestion = &survey.Input{ - Message: "http://", - Default: "enable", -} - -var removeURLQuestion = func(urls []string) *survey.MultiSelect { - return &survey.MultiSelect{ - Message: "Select urls that should be removed.", - Options: urls, - } -} - -type spamTypeSurvey struct { - DeepSpamEnabled string - ReuseLaterEnabled string -} - -var spamTypeQuestions = func(defaultDeep, defaultReuse string) []*survey.Question { - return []*survey.Question{ - { - Name: "deepSpamEnabled", - Prompt: &survey.Select{ - Message: "Deep spam", - Options: confirms, - Default: defaultDeep, - Help: "Uses outputs generated during the spam, to create deep UTXO and conflict structures.", - }, - }, - { - Name: "reuseLaterEnabled", - Prompt: &survey.Select{ - Message: "Reuse outputs", - Options: confirms, - Default: defaultReuse, - Help: "Remember created outputs (add them to reuse outputs and use in future deep spams).", - }, - }, - } -} - -type spamDetailsSurvey struct { - SpamDuration string - SpamRate string - TimeUnit string -} - -var spamDetailsQuestions = func(defaultDuration, defaultRate, defaultTimeUnit string) []*survey.Question { - return []*survey.Question{ - { - Name: "spamDuration", - Prompt: &survey.Input{ - Message: "Spam duration in [s].", - Default: defaultDuration, - }, - }, - { - Name: "timeUnit", - Prompt: &survey.Select{ - Message: "Choose time unit for the spam", - Options: timeUnits, - Default: defaultTimeUnit, - }, - }, - { - Name: "spamRate", - Prompt: &survey.Input{ - Message: "Spam rate", - Default: defaultRate, - }, - Validate: func(val interface{}) error { - if str, ok := val.(string); ok { - _, err := strconv.Atoi(str) - if err == nil { - return nil - } - - return ierrors.New("Incorrect spam rate") - } - - return nil - }, - }, - } -} - -var spamScenarioQuestion = func(defaultScenario string) *survey.Select { - return &survey.Select{ - Message: "Choose a spam scenario", - Options: scenarios, - Default: defaultScenario, - } -} - -var spamMenuQuestion = &survey.Select{ - Message: "Spam settings", - Options: spamMenuOptions, - Default: startSpam, -} - -var currentMenuQuestion = &survey.Select{ - Options: currentSpamOptions, - Default: back, -} - -var removeSpammer = &survey.Input{ - Message: "Type in id of the spammer you wish to stop.", -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/tools/evil-spammer/logger/logger.go b/tools/evil-spammer/logger/logger.go deleted file mode 100644 index 20ce465a3..000000000 --- a/tools/evil-spammer/logger/logger.go +++ /dev/null @@ -1,24 +0,0 @@ -package logger - -import ( - "fmt" - - "github.com/iotaledger/hive.go/app/configuration" - appLogger "github.com/iotaledger/hive.go/app/logger" - "github.com/iotaledger/hive.go/logger" -) - -var New = logger.NewLogger - -func init() { - config := configuration.New() - err := config.Set(logger.ConfigurationKeyOutputPaths, []string{"evil-spammer.log", "stdout"}) - if err != nil { - fmt.Println(err) - return - } - if err = appLogger.InitGlobalLogger(config); err != nil { - panic(err) - } - logger.SetLevel(logger.LevelDebug) -} diff --git a/tools/evil-spammer/main.go b/tools/evil-spammer/main.go deleted file mode 100644 index b94687059..000000000 --- a/tools/evil-spammer/main.go +++ /dev/null @@ -1,116 +0,0 @@ -package main - -import ( - "flag" - "fmt" - - "github.com/iotaledger/iota-core/tools/evil-spammer/accountwallet" - "github.com/iotaledger/iota-core/tools/evil-spammer/interactive" - "github.com/iotaledger/iota-core/tools/evil-spammer/logger" - "github.com/iotaledger/iota-core/tools/evil-spammer/programs" -) - -var ( - log = logger.New("main") - optionFlagSet = flag.NewFlagSet("script flag set", flag.ExitOnError) -) - -func main() { - help := parseFlags() - - if help { - fmt.Println("Usage of the Evil Spammer tool, provide the first argument for the selected mode:\n" + - "'interactive' - enters the interactive mode.\n" + - "'basic' - can be parametrized with additional flags to run one time spammer. Run 'evil-wallet basic -h' for the list of possible flags.\n" + - "'accounts' - tool for account creation and transition. Run 'evil-wallet accounts -h' for the list of possible flags.\n" + - "'quick' - runs simple stress test: tx spam -> blk spam -> ds spam. Run 'evil-wallet quick -h' for the list of possible flags.") - - return - } - // init account wallet - var accWallet *accountwallet.AccountWallet - var err error - if Script == "basic" || Script == "accounts" { - // read config here - config := accountwallet.LoadConfiguration() - // load wallet - accWallet, err = accountwallet.Run(config) - if err != nil { - log.Error(err) - log.Errorf("Failed to init account wallet, exitting...") - - return - } - - // save wallet and latest faucet output - defer func() { - err = accountwallet.SaveState(accWallet) - if err != nil { - log.Errorf("Error while saving wallet state: %v", err) - } - accountwallet.SaveConfiguration(config) - - }() - } - // run selected test scenario - switch Script { - case "interactive": - interactive.Run() - case "basic": - programs.CustomSpam(&customSpamParams, accWallet) - case "accounts": - accountsSubcommands(accWallet, accountsSubcommandsFlags) - case "quick": - programs.QuickTest(&quickTestParams) - // case SpammerTypeCommitments: - // CommitmentsSpam(&commitmentsSpamParams) - default: - log.Warnf("Unknown parameter for script, possible values: interactive, basic, accounts, quick") - } -} - -func accountsSubcommands(wallet *accountwallet.AccountWallet, subcommands []accountwallet.AccountSubcommands) { - for _, sub := range subcommands { - accountsSubcommand(wallet, sub) - } -} - -func accountsSubcommand(wallet *accountwallet.AccountWallet, sub accountwallet.AccountSubcommands) { - switch sub.Type() { - case accountwallet.OperationCreateAccount: - log.Infof("Run subcommand: %s, with parametetr set: %v", accountwallet.OperationCreateAccount.String(), sub) - params := sub.(*accountwallet.CreateAccountParams) - accountID, err := wallet.CreateAccount(params) - if err != nil { - log.Errorf("Error creating account: %v", err) - - return - } - log.Infof("Created account %s with %d tokens", accountID, params.Amount) - case accountwallet.OperationDestroyAccound: - log.Infof("Run subcommand: %s, with parametetr set: %v", accountwallet.OperationDestroyAccound, sub) - params := sub.(*accountwallet.DestroyAccountParams) - err := wallet.DestroyAccount(params) - if err != nil { - log.Errorf("Error destroying account: %v", err) - - return - } - case accountwallet.OperationListAccounts: - err := wallet.ListAccount() - if err != nil { - log.Errorf("Error listing accounts: %v", err) - - return - } - case accountwallet.OperationAllotAccount: - log.Infof("Run subcommand: %s, with parametetr set: %v", accountwallet.OperationAllotAccount, sub) - params := sub.(*accountwallet.AllotAccountParams) - err := wallet.AllotToAccount(params) - if err != nil { - log.Errorf("Error allotting account: %v", err) - - return - } - } -} diff --git a/tools/evil-spammer/models/connector.go b/tools/evil-spammer/models/connector.go deleted file mode 100644 index 3fd408cf3..000000000 --- a/tools/evil-spammer/models/connector.go +++ /dev/null @@ -1,328 +0,0 @@ -package models - -import ( - "context" - "time" - - "github.com/google/martian/log" - - "github.com/iotaledger/hive.go/runtime/options" - "github.com/iotaledger/hive.go/runtime/syncutils" - "github.com/iotaledger/iota-core/pkg/model" - iotago "github.com/iotaledger/iota.go/v4" - "github.com/iotaledger/iota.go/v4/builder" - "github.com/iotaledger/iota.go/v4/nodeclient" - "github.com/iotaledger/iota.go/v4/nodeclient/apimodels" -) - -type ServerInfo struct { - Healthy bool - Version string -} - -type ServerInfos []*ServerInfo - -type Connector interface { - // ServersStatuses retrieves the connected server status for each client. - ServersStatuses() ServerInfos - // ServerStatus retrieves the connected server status. - ServerStatus(cltIdx int) (status *ServerInfo, err error) - // Clients returns list of all clients. - Clients(...bool) []Client - // GetClients returns the numOfClt client instances that were used the longest time ago. - GetClients(numOfClt int) []Client - // AddClient adds a client to WebClients based on provided GoShimmerAPI url. - AddClient(url string, setters ...options.Option[WebClient]) - // RemoveClient removes a client with the provided url from the WebClients. - RemoveClient(url string) - // GetClient returns the client instance that was used the longest time ago. - GetClient() Client -} - -// WebClients is responsible for handling connections via GoShimmerAPI. -type WebClients struct { - clients []*WebClient - urls []string - - // helper variable indicating which clt was recently used, useful for double, triple,... spends - lastUsed int - - mu syncutils.Mutex -} - -// NewWebClients creates Connector from provided GoShimmerAPI urls. -func NewWebClients(urls []string, setters ...options.Option[WebClient]) *WebClients { - clients := make([]*WebClient, len(urls)) - var err error - for i, url := range urls { - clients[i], err = NewWebClient(url, setters...) - if err != nil { - log.Errorf("failed to create client for url %s: %s", url, err) - - return nil - } - } - - return &WebClients{ - clients: clients, - urls: urls, - lastUsed: -1, - } -} - -// ServersStatuses retrieves the connected server status for each client. -func (c *WebClients) ServersStatuses() ServerInfos { - status := make(ServerInfos, len(c.clients)) - - for i := range c.clients { - status[i], _ = c.ServerStatus(i) - } - - return status -} - -// ServerStatus retrieves the connected server status. -func (c *WebClients) ServerStatus(cltIdx int) (status *ServerInfo, err error) { - response, err := c.clients[cltIdx].client.Info(context.Background()) - if err != nil { - return nil, err - } - - return &ServerInfo{ - Healthy: response.Status.IsHealthy, - Version: response.Version, - }, nil -} - -// Clients returns list of all clients. -func (c *WebClients) Clients(...bool) []Client { - clients := make([]Client, len(c.clients)) - for i, c := range c.clients { - clients[i] = c - } - - return clients -} - -// GetClients returns the numOfClt client instances that were used the longest time ago. -func (c *WebClients) GetClients(numOfClt int) []Client { - c.mu.Lock() - defer c.mu.Unlock() - - clts := make([]Client, numOfClt) - - for i := range clts { - clts[i] = c.getClient() - } - - return clts -} - -// getClient returns the client instance that was used the longest time ago, not protected by mutex. -func (c *WebClients) getClient() Client { - if c.lastUsed >= len(c.clients)-1 { - c.lastUsed = 0 - } else { - c.lastUsed++ - } - - return c.clients[c.lastUsed] -} - -// GetClient returns the client instance that was used the longest time ago. -func (c *WebClients) GetClient() Client { - c.mu.Lock() - defer c.mu.Unlock() - - return c.getClient() -} - -// AddClient adds client to WebClients based on provided GoShimmerAPI url. -func (c *WebClients) AddClient(url string, setters ...options.Option[WebClient]) { - c.mu.Lock() - defer c.mu.Unlock() - - clt, err := NewWebClient(url, setters...) - if err != nil { - log.Errorf("failed to create client for url %s: %s", url, err) - - return - } - c.clients = append(c.clients, clt) - c.urls = append(c.urls, url) -} - -// RemoveClient removes client with the provided url from the WebClients. -func (c *WebClients) RemoveClient(url string) { - c.mu.Lock() - defer c.mu.Unlock() - - indexToRemove := -1 - for i, u := range c.urls { - if u == url { - indexToRemove = i - break - } - } - if indexToRemove == -1 { - return - } - c.clients = append(c.clients[:indexToRemove], c.clients[indexToRemove+1:]...) - c.urls = append(c.urls[:indexToRemove], c.urls[indexToRemove+1:]...) -} - -type Client interface { - Client() *nodeclient.Client - Indexer() (nodeclient.IndexerClient, error) - // URL returns a client API url. - URL() (cltID string) - // PostBlock sends a block to the Tangle via a given client. - PostBlock(block *iotago.ProtocolBlock) (iotago.BlockID, error) - // PostData sends the given data (payload) by creating a block in the backend. - PostData(data []byte) (blkID string, err error) - // GetTransactionConfirmationState returns the AcceptanceState of a given transaction ID. - GetTransactionConfirmationState(txID iotago.TransactionID) string - // GetOutput gets the output of a given outputID. - GetOutput(outputID iotago.OutputID) iotago.Output - // GetOutputConfirmationState gets the first unspent outputs of a given address. - GetOutputConfirmationState(outputID iotago.OutputID) string - // GetTransaction gets the transaction. - GetTransaction(txID iotago.TransactionID) (resp *iotago.SignedTransaction, err error) - // GetBlockIssuance returns the latest commitment and data needed to create a new block. - GetBlockIssuance(...iotago.SlotIndex) (resp *apimodels.IssuanceBlockHeaderResponse, err error) - // GetCongestion returns congestion data such as rmc or issuing readiness. - GetCongestion(id iotago.AccountID) (resp *apimodels.CongestionResponse, err error) - - iotago.APIProvider -} - -// WebClient contains a GoShimmer web API to interact with a node. -type WebClient struct { - client *nodeclient.Client - url string -} - -func (c *WebClient) Client() *nodeclient.Client { - return c.client -} - -func (c *WebClient) Indexer() (nodeclient.IndexerClient, error) { - return c.client.Indexer(context.Background()) -} - -func (c *WebClient) APIForVersion(version iotago.Version) (iotago.API, error) { - return c.client.APIForVersion(version) -} - -func (c *WebClient) APIForTime(t time.Time) iotago.API { - return c.client.APIForTime(t) -} - -func (c *WebClient) APIForSlot(index iotago.SlotIndex) iotago.API { - return c.client.APIForSlot(index) -} - -func (c *WebClient) APIForEpoch(index iotago.EpochIndex) iotago.API { - return c.client.APIForEpoch(index) -} - -func (c *WebClient) CommittedAPI() iotago.API { - return c.client.CommittedAPI() -} - -func (c *WebClient) LatestAPI() iotago.API { - return c.client.LatestAPI() -} - -// URL returns a client API Url. -func (c *WebClient) URL() string { - return c.url -} - -// NewWebClient creates Connector from provided iota-core API urls. -func NewWebClient(url string, opts ...options.Option[WebClient]) (*WebClient, error) { - var initErr error - return options.Apply(&WebClient{ - url: url, - }, opts, func(w *WebClient) { - w.client, initErr = nodeclient.New(w.url) - }), initErr -} - -func (c *WebClient) PostBlock(block *iotago.ProtocolBlock) (blockID iotago.BlockID, err error) { - return c.client.SubmitBlock(context.Background(), block) -} - -// PostData sends the given data (payload) by creating a block in the backend. -func (c *WebClient) PostData(data []byte) (blkID string, err error) { - blockBuilder := builder.NewBasicBlockBuilder(c.client.CommittedAPI()) - blockBuilder.IssuingTime(time.Time{}) - - blockBuilder.Payload(&iotago.TaggedData{ - Tag: data, - }) - - blk, err := blockBuilder.Build() - if err != nil { - return iotago.EmptyBlockID.ToHex(), err - } - - id, err := c.client.SubmitBlock(context.Background(), blk) - if err != nil { - return - } - - return id.ToHex(), nil -} - -// GetOutputConfirmationState gets the first unspent outputs of a given address. -func (c *WebClient) GetOutputConfirmationState(outputID iotago.OutputID) string { - txID := outputID.TransactionID() - - return c.GetTransactionConfirmationState(txID) -} - -// GetOutput gets the output of a given outputID. -func (c *WebClient) GetOutput(outputID iotago.OutputID) iotago.Output { - res, err := c.client.OutputByID(context.Background(), outputID) - if err != nil { - return nil - } - - return res -} - -// GetTransactionConfirmationState returns the AcceptanceState of a given transaction ID. -func (c *WebClient) GetTransactionConfirmationState(txID iotago.TransactionID) string { - resp, err := c.client.TransactionIncludedBlockMetadata(context.Background(), txID) - if err != nil { - return "" - } - - return resp.TransactionState -} - -// GetTransaction gets the transaction. -func (c *WebClient) GetTransaction(txID iotago.TransactionID) (tx *iotago.SignedTransaction, err error) { - resp, err := c.client.TransactionIncludedBlock(context.Background(), txID) - if err != nil { - return - } - - modelBlk, err := model.BlockFromBlock(resp) - if err != nil { - return - } - - tx, _ = modelBlk.SignedTransaction() - - return tx, nil -} - -func (c *WebClient) GetBlockIssuance(slotIndex ...iotago.SlotIndex) (resp *apimodels.IssuanceBlockHeaderResponse, err error) { - return c.client.BlockIssuance(context.Background(), slotIndex...) -} - -func (c *WebClient) GetCongestion(accountID iotago.AccountID) (resp *apimodels.CongestionResponse, err error) { - return c.client.Congestion(context.Background(), accountID) -} diff --git a/tools/evil-spammer/models/output.go b/tools/evil-spammer/models/output.go deleted file mode 100644 index 539da3a19..000000000 --- a/tools/evil-spammer/models/output.go +++ /dev/null @@ -1,88 +0,0 @@ -package models - -import ( - "crypto/ed25519" - - "github.com/iotaledger/iota-core/pkg/blockhandler" - iotago "github.com/iotaledger/iota.go/v4" - "github.com/iotaledger/iota.go/v4/nodeclient/apimodels" -) - -// Input contains details of an input. -type Input struct { - OutputID iotago.OutputID - Address iotago.Address -} - -// Output contains details of an output ID. -type Output struct { - OutputID iotago.OutputID - Address iotago.Address - Index uint64 - Balance iotago.BaseToken - - OutputStruct iotago.Output -} - -// Outputs is a list of Output. -type Outputs []*Output - -type AccountStatus uint8 - -const ( - AccountPending AccountStatus = iota - AccountReady -) - -type AccountData struct { - Alias string - Status AccountStatus - Account blockhandler.Account - OutputID iotago.OutputID - Index uint64 -} - -type AccountState struct { - Alias string `serix:"0,lengthPrefixType=uint8"` - AccountID iotago.AccountID `serix:"2"` - PrivateKey ed25519.PrivateKey `serix:"3,lengthPrefixType=uint8"` - OutputID iotago.OutputID `serix:"4"` - Index uint64 `serix:"5"` -} - -func AccountStateFromAccountData(acc *AccountData) *AccountState { - return &AccountState{ - Alias: acc.Alias, - AccountID: acc.Account.ID(), - PrivateKey: acc.Account.PrivateKey(), - OutputID: acc.OutputID, - Index: acc.Index, - } -} - -func (a *AccountState) ToAccountData() *AccountData { - return &AccountData{ - Alias: a.Alias, - Account: blockhandler.NewEd25519Account(a.AccountID, a.PrivateKey), - OutputID: a.OutputID, - Index: a.Index, - } -} - -type PayloadIssuanceData struct { - Payload iotago.Payload - CongestionResponse *apimodels.CongestionResponse -} - -type AllotmentStrategy uint8 - -const ( - AllotmentStrategyNone AllotmentStrategy = iota - AllotmentStrategyMinCost - AllotmentStrategyAll -) - -type IssuancePaymentStrategy struct { - AllotmentStrategy AllotmentStrategy - IssuerAlias string -} diff --git a/tools/evil-spammer/parse.go b/tools/evil-spammer/parse.go deleted file mode 100644 index b62b85292..000000000 --- a/tools/evil-spammer/parse.go +++ /dev/null @@ -1,494 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "os" - "strconv" - "strings" - "time" - - "github.com/iotaledger/hive.go/ierrors" - "github.com/iotaledger/iota-core/tools/evil-spammer/accountwallet" - "github.com/iotaledger/iota-core/tools/evil-spammer/evilwallet" -) - -func parseFlags() (help bool) { - if len(os.Args) <= 1 { - return true - } - script := os.Args[1] - - Script = script - log.Infof("script %s", Script) - - switch Script { - case "basic": - parseBasicSpamFlags() - case "accounts": - // pass subcommands - subcommands := make([]string, 0) - if len(os.Args) > 2 { - subcommands = os.Args[2:] - } - splitedCmds := readSubcommandsAndFlagSets(subcommands) - accountsSubcommandsFlags = parseAccountTestFlags(splitedCmds) - - case "quick": - parseQuickTestFlags() - // case SpammerTypeCommitments: - // parseCommitmentsSpamFlags() - } - if Script == "help" || Script == "-h" || Script == "--help" { - return true - } - - return -} - -func parseOptionFlagSet(flagSet *flag.FlagSet, args ...[]string) { - commands := os.Args[2:] - if len(args) > 0 { - commands = args[0] - } - err := flagSet.Parse(commands) - if err != nil { - log.Errorf("Cannot parse first `script` parameter") - return - } -} - -func parseBasicSpamFlags() { - urls := optionFlagSet.String("urls", "", "API urls for clients used in test separated with commas") - spamTypes := optionFlagSet.String("spammer", "", "Spammers used during test. Format: strings separated with comma, available options: 'blk' - block,"+ - " 'tx' - transaction, 'ds' - double spends spammers, 'nds' - n-spends spammer, 'custom' - spams with provided scenario") - rate := optionFlagSet.String("rate", "", "Spamming rate for provided 'spammer'. Format: numbers separated with comma, e.g. 10,100,1 if three spammers were provided for 'spammer' parameter.") - duration := optionFlagSet.String("duration", "", "Spam duration. Cannot be combined with flag 'blkNum'. Format: separated by commas list of decimal numbers, each with optional fraction and a unit suffix, such as '300ms', '-1.5h' or '2h45m'.\n Valid time units are 'ns', 'us', 'ms', 's', 'm', 'h'.") - blkNum := optionFlagSet.String("blkNum", "", "Spam duration in seconds. Cannot be combined with flag 'duration'. Format: numbers separated with comma, e.g. 10,100,1 if three spammers were provided for 'spammer' parameter.") - timeunit := optionFlagSet.Duration("tu", customSpamParams.TimeUnit, "Time unit for the spamming rate. Format: decimal numbers, each with optional fraction and a unit suffix, such as '300ms', '-1.5h' or '2h45m'.\n Valid time units are 'ns', 'us', 'ms', 's', 'm', 'h'.") - delayBetweenConflicts := optionFlagSet.Duration("dbc", customSpamParams.DelayBetweenConflicts, "delayBetweenConflicts - Time delay between conflicts in double spend spamming") - scenario := optionFlagSet.String("scenario", "", "Name of the EvilBatch that should be used for the spam. By default uses Scenario1. Possible scenarios can be found in evilwallet/customscenarion.go.") - deepSpam := optionFlagSet.Bool("deep", customSpamParams.DeepSpam, "Enable the deep spam, by reusing outputs created during the spam.") - nSpend := optionFlagSet.Int("nSpend", customSpamParams.NSpend, "Number of outputs to be spent in n-spends spammer for the spammer type needs to be set to 'ds'. Default value is 2 for double-spend.") - account := optionFlagSet.String("account", "", "Account alias to be used for the spam. Account should be created first with accounts tool.") - - parseOptionFlagSet(optionFlagSet) - - if *urls != "" { - parsedUrls := parseCommaSepString(*urls) - quickTestParams.ClientURLs = parsedUrls - customSpamParams.ClientURLs = parsedUrls - } - if *spamTypes != "" { - parsedSpamTypes := parseCommaSepString(*spamTypes) - customSpamParams.SpamTypes = parsedSpamTypes - } - if *rate != "" { - parsedRates := parseCommaSepInt(*rate) - customSpamParams.Rates = parsedRates - } - if *duration != "" { - parsedDurations := parseDurations(*duration) - customSpamParams.Durations = parsedDurations - } - if *blkNum != "" { - parsedBlkNums := parseCommaSepInt(*blkNum) - customSpamParams.BlkToBeSent = parsedBlkNums - } - if *scenario != "" { - conflictBatch, ok := evilwallet.GetScenario(*scenario) - if ok { - customSpamParams.Scenario = conflictBatch - } - } - - customSpamParams.NSpend = *nSpend - customSpamParams.DeepSpam = *deepSpam - customSpamParams.TimeUnit = *timeunit - customSpamParams.DelayBetweenConflicts = *delayBetweenConflicts - if *account != "" { - customSpamParams.AccountAlias = *account - } - - // fill in unused parameter: blkNum or duration with zeros - if *duration == "" && *blkNum != "" { - customSpamParams.Durations = make([]time.Duration, len(customSpamParams.BlkToBeSent)) - } - if *blkNum == "" && *duration != "" { - customSpamParams.BlkToBeSent = make([]int, len(customSpamParams.Durations)) - } -} - -func parseQuickTestFlags() { - urls := optionFlagSet.String("urls", "", "API urls for clients used in test separated with commas") - rate := optionFlagSet.Int("rate", quickTestParams.Rate, "The spamming rate") - duration := optionFlagSet.Duration("duration", quickTestParams.Duration, "Duration of the spam. Format: decimal numbers, each with optional fraction and a unit suffix, such as '300ms', '-1.5h' or '2h45m'.\n Valid time units are 'ns', 'us', 'ms', 's', 'm', 'h'.") - timeunit := optionFlagSet.Duration("tu", quickTestParams.TimeUnit, "Time unit for the spamming rate. Format: decimal numbers, each with optional fraction and a unit suffix, such as '300ms', '-1.5h' or '2h45m'.\n Valid time units are 'ns', 'us', 'ms', 's', 'm', 'h'.") - delayBetweenConflicts := optionFlagSet.Duration("dbc", quickTestParams.DelayBetweenConflicts, "delayBetweenConflicts - Time delay between conflicts in double spend spamming") - verifyLedger := optionFlagSet.Bool("verify", quickTestParams.VerifyLedger, "Set to true if verify ledger script should be run at the end of the test") - - parseOptionFlagSet(optionFlagSet) - - if *urls != "" { - parsedUrls := parseCommaSepString(*urls) - quickTestParams.ClientURLs = parsedUrls - } - quickTestParams.Rate = *rate - quickTestParams.Duration = *duration - quickTestParams.TimeUnit = *timeunit - quickTestParams.DelayBetweenConflicts = *delayBetweenConflicts - quickTestParams.VerifyLedger = *verifyLedger -} - -// readSubcommandsAndFlagSets splits the subcommands on multiple flag sets. -func readSubcommandsAndFlagSets(subcommands []string) [][]string { - prevSplitIndex := 0 - subcommandsSplit := make([][]string, 0) - if len(subcommands) == 0 { - return nil - } - - // mainCmd := make([]string, 0) - for index := 0; index < len(subcommands); index++ { - validCommand := accountwallet.AvailableCommands(subcommands[index]) - - if !strings.HasPrefix(subcommands[index], "--") && validCommand { - if index != 0 { - subcommandsSplit = append(subcommandsSplit, subcommands[prevSplitIndex:index]) - } - prevSplitIndex = index - } - } - subcommandsSplit = append(subcommandsSplit, subcommands[prevSplitIndex:]) - - return subcommandsSplit -} - -func parseAccountTestFlags(splitedCmds [][]string) []accountwallet.AccountSubcommands { - parsedCmds := make([]accountwallet.AccountSubcommands, 0) - - for _, cmds := range splitedCmds { - switch cmds[0] { - case "create": - createAccountParams, err := parseCreateAccountFlags(cmds[1:]) - if err != nil { - continue - } - - parsedCmds = append(parsedCmds, createAccountParams) - case "convert": - convertAccountParams, err := parseConvertAccountFlags(cmds[1:]) - if err != nil { - continue - } - - parsedCmds = append(parsedCmds, convertAccountParams) - case "destroy": - destroyAccountParams, err := parseDestroyAccountFlags(cmds[1:]) - if err != nil { - continue - } - - parsedCmds = append(parsedCmds, destroyAccountParams) - case "allot": - allotAccountParams, err := parseAllotAccountFlags(cmds[1:]) - if err != nil { - continue - } - - parsedCmds = append(parsedCmds, allotAccountParams) - case "delegate": - delegatingAccountParams, err := parseDelegateAccountFlags(cmds[1:]) - if err != nil { - continue - } - - parsedCmds = append(parsedCmds, delegatingAccountParams) - case "stake": - stakingAccountParams, err := parseStakeAccountFlags(cmds[1:]) - if err != nil { - continue - } - - parsedCmds = append(parsedCmds, stakingAccountParams) - case "update": - updateAccountParams, err := parseUpdateAccountFlags(cmds[1:]) - if err != nil { - continue - } - - parsedCmds = append(parsedCmds, updateAccountParams) - case "list": - parsedCmds = append(parsedCmds, &accountwallet.NoAccountParams{ - Operation: accountwallet.OperationListAccounts, - }) - default: - accountUsage() - return nil - } - } - - return parsedCmds -} - -func accountUsage() { - fmt.Println("Usage for accounts [COMMAND] [FLAGS], multiple commands can be chained together.") - fmt.Printf("COMMAND: %s\n", accountwallet.CmdNameCreateAccount) - parseCreateAccountFlags(nil) - - fmt.Printf("COMMAND: %s\n", accountwallet.CmdNameConvertAccount) - parseConvertAccountFlags(nil) - - fmt.Printf("COMMAND: %s\n", accountwallet.CmdNameDestroyAccount) - parseDestroyAccountFlags(nil) - - fmt.Printf("COMMAND: %s\n", accountwallet.CmdNameAllotAccount) - parseAllotAccountFlags(nil) - - fmt.Printf("COMMAND: %s\n", accountwallet.CmdNameDelegateAccount) - parseDelegateAccountFlags(nil) - - fmt.Printf("COMMAND: %s\n", accountwallet.CmdNameStakeAccount) - parseStakeAccountFlags(nil) -} - -func parseCreateAccountFlags(subcommands []string) (*accountwallet.CreateAccountParams, error) { - flagSet := flag.NewFlagSet("create", flag.ExitOnError) - alias := flagSet.String("alias", "", "The alias name of new created account") - amount := flagSet.Int64("amount", 1000, "The amount to be transfered to the new account") - noBIF := flagSet.Bool("noBIF", false, "Create account without Block Issuer Feature") - implicit := flagSet.Bool("implicit", false, "Create an implicit account") - - if subcommands == nil { - flagSet.Usage() - - return nil, ierrors.Errorf("no subcommands") - } - - log.Infof("Parsing create account flags, subcommands: %v", subcommands) - err := flagSet.Parse(subcommands) - if err != nil { - log.Errorf("Cannot parse first `script` parameter") - - return nil, ierrors.Wrap(err, "cannot parse first `script` parameter") - } - - return &accountwallet.CreateAccountParams{ - Alias: *alias, - Amount: uint64(*amount), - NoBIF: *noBIF, - Implicit: *implicit, - }, nil -} - -func parseConvertAccountFlags(subcommands []string) (*accountwallet.ConvertAccountParams, error) { - flagSet := flag.NewFlagSet("convert", flag.ExitOnError) - alias := flagSet.String("alias", "", "The implicit account to be converted to full account") - - if subcommands == nil { - flagSet.Usage() - - return nil, ierrors.Errorf("no subcommands") - } - - log.Infof("Parsing convert account flags, subcommands: %v", subcommands) - err := flagSet.Parse(subcommands) - if err != nil { - log.Errorf("Cannot parse first `script` parameter") - - return nil, ierrors.Wrap(err, "cannot parse first `script` parameter") - } - - return &accountwallet.ConvertAccountParams{ - AccountAlias: *alias, - }, nil -} - -func parseDestroyAccountFlags(subcommands []string) (*accountwallet.DestroyAccountParams, error) { - flagSet := flag.NewFlagSet("destroy", flag.ExitOnError) - alias := flagSet.String("alias", "", "The alias name of the account to be destroyed") - expirySlot := flagSet.Int64("expirySlot", 0, "The expiry slot of the account to be destroyed") - - if subcommands == nil { - flagSet.Usage() - - return nil, ierrors.Errorf("no subcommands") - } - - log.Infof("Parsing destroy account flags, subcommands: %v", subcommands) - err := flagSet.Parse(subcommands) - if err != nil { - log.Errorf("Cannot parse first `script` parameter") - - return nil, ierrors.Wrap(err, "cannot parse first `script` parameter") - } - - return &accountwallet.DestroyAccountParams{ - AccountAlias: *alias, - ExpirySlot: uint64(*expirySlot), - }, nil -} - -func parseAllotAccountFlags(subcommands []string) (*accountwallet.AllotAccountParams, error) { - flagSet := flag.NewFlagSet("allot", flag.ExitOnError) - from := flagSet.String("from", "", "The alias name of the account to allot mana from") - to := flagSet.String("to", "", "The alias of the account to allot mana to") - amount := flagSet.Int64("amount", 1000, "The amount of mana to allot") - - if subcommands == nil { - flagSet.Usage() - - return nil, ierrors.Errorf("no subcommands") - } - - log.Infof("Parsing allot account flags, subcommands: %v", subcommands) - err := flagSet.Parse(subcommands) - if err != nil { - log.Errorf("Cannot parse first `script` parameter") - - return nil, ierrors.Wrap(err, "cannot parse first `script` parameter") - } - - return &accountwallet.AllotAccountParams{ - From: *from, - To: *to, - Amount: uint64(*amount), - }, nil -} - -func parseStakeAccountFlags(subcommands []string) (*accountwallet.StakeAccountParams, error) { - flagSet := flag.NewFlagSet("stake", flag.ExitOnError) - alias := flagSet.String("alias", "", "The alias name of the account to stake") - amount := flagSet.Int64("amount", 100, "The amount of tokens to stake") - fixedCost := flagSet.Int64("fixedCost", 0, "The fixed cost of the account to stake") - startEpoch := flagSet.Int64("startEpoch", 0, "The start epoch of the account to stake") - endEpoch := flagSet.Int64("endEpoch", 0, "The end epoch of the account to stake") - - if subcommands == nil { - flagSet.Usage() - - return nil, ierrors.Errorf("no subcommands") - } - - log.Infof("Parsing staking account flags, subcommands: %v", subcommands) - err := flagSet.Parse(subcommands) - if err != nil { - log.Errorf("Cannot parse first `script` parameter") - - return nil, ierrors.Wrap(err, "cannot parse first `script` parameter") - } - - return &accountwallet.StakeAccountParams{ - Alias: *alias, - Amount: uint64(*amount), - FixedCost: uint64(*fixedCost), - StartEpoch: uint64(*startEpoch), - EndEpoch: uint64(*endEpoch), - }, nil -} - -func parseDelegateAccountFlags(subcommands []string) (*accountwallet.DelegateAccountParams, error) { - flagSet := flag.NewFlagSet("delegate", flag.ExitOnError) - from := flagSet.String("from", "", "The alias name of the account to delegate mana from") - to := flagSet.String("to", "", "The alias of the account to delegate mana to") - amount := flagSet.Int64("amount", 100, "The amount of mana to delegate") - - if subcommands == nil { - flagSet.Usage() - - return nil, ierrors.Errorf("no subcommands") - } - - log.Infof("Parsing delegate account flags, subcommands: %v", subcommands) - err := flagSet.Parse(subcommands) - if err != nil { - log.Errorf("Cannot parse first `script` parameter") - - return nil, ierrors.Wrap(err, "cannot parse first `script` parameter") - } - - return &accountwallet.DelegateAccountParams{ - From: *from, - To: *to, - Amount: uint64(*amount), - }, nil -} - -func parseUpdateAccountFlags(subcommands []string) (*accountwallet.UpdateAccountParams, error) { - flagSet := flag.NewFlagSet("update", flag.ExitOnError) - alias := flagSet.String("alias", "", "The alias name of the account to update") - bik := flagSet.String("bik", "", "The block issuer key (in hex) to add") - amount := flagSet.Int64("addamount", 100, "The amount of token to add") - mana := flagSet.Int64("addmana", 100, "The amount of mana to add") - expirySlot := flagSet.Int64("expirySlot", 0, "Update the expiry slot of the account") - - if subcommands == nil { - flagSet.Usage() - - return nil, ierrors.Errorf("no subcommands") - } - - log.Infof("Parsing update account flags, subcommands: %v", subcommands) - err := flagSet.Parse(subcommands) - if err != nil { - log.Errorf("Cannot parse first `script` parameter") - - return nil, ierrors.Wrap(err, "cannot parse first `script` parameter") - } - - return &accountwallet.UpdateAccountParams{ - Alias: *alias, - BlockIssuerKey: *bik, - Amount: uint64(*amount), - Mana: uint64(*mana), - ExpirySlot: uint64(*expirySlot), - }, nil -} - -// func parseCommitmentsSpamFlags() { -// commitmentType := optionFlagSet.String("type", commitmentsSpamParams.CommitmentType, "Type of commitment spam. Possible values: 'latest' - valid commitment spam, 'random' - completely new, invalid cahin, 'fork' - forked chain, combine with 'forkAfter' parameter.") -// rate := optionFlagSet.Int("rate", commitmentsSpamParams.Rate, "Commitment spam rate") -// duration := optionFlagSet.Duration("duration", commitmentsSpamParams.Duration, "Duration of the spam. Format: decimal numbers, each with optional fraction and a unit suffix, such as '300ms', '-1.5h' or '2h45m'.\n Valid time units are 'ns', 'us', 'ms', 's', 'm', 'h'.") -// timeUnit := optionFlagSet.Duration("tu", commitmentsSpamParams.TimeUnit, "Time unit for the spamming rate. Format: decimal numbers, each with optional fraction and a unit suffix, such as '300ms', '-1.5h' or '2h45m'.\n Valid time units are 'ns', 'us', 'ms', 's', 'm', 'h'.") -// networkAlias := optionFlagSet.String("network", commitmentsSpamParams.NetworkAlias, "Network alias for the test. Check your keys-config.json file for possible values.") -// identityAlias := optionFlagSet.String("spammerAlias", commitmentsSpamParams.SpammerAlias, "Identity alias for the node identity and its private keys that will be used to spam. Check your keys-config.json file for possible values.") -// validAlias := optionFlagSet.String("validAlias", commitmentsSpamParams.ValidAlias, "Identity alias for the honest node and its private keys, will be used to request valid commitment and block data. Check your keys-config.json file for possible values.") -// forkAfter := optionFlagSet.Int("forkAfter", commitmentsSpamParams.Rate, "Indicates how many slots after spammer startup should fork be placed in the created commitment chain. Works only for 'fork' commitment spam type.") - -// parseOptionFlagSet(optionFlagSet) - -// commitmentsSpamParams.CommitmentType = *commitmentType -// commitmentsSpamParams.Rate = *rate -// commitmentsSpamParams.Duration = *duration -// commitmentsSpamParams.TimeUnit = *timeUnit -// commitmentsSpamParams.NetworkAlias = *networkAlias -// commitmentsSpamParams.SpammerAlias = *identityAlias -// commitmentsSpamParams.ValidAlias = *validAlias -// commitmentsSpamParams.ForkAfter = *forkAfter -// } - -func parseCommaSepString(urls string) []string { - split := strings.Split(urls, ",") - - return split -} - -func parseCommaSepInt(nums string) []int { - split := strings.Split(nums, ",") - parsed := make([]int, len(split)) - for i, num := range split { - parsed[i], _ = strconv.Atoi(num) - } - - return parsed -} - -func parseDurations(durations string) []time.Duration { - split := strings.Split(durations, ",") - parsed := make([]time.Duration, len(split)) - for i, dur := range split { - parsed[i], _ = time.ParseDuration(dur) - } - - return parsed -} diff --git a/tools/evil-spammer/programs/commitments.go b/tools/evil-spammer/programs/commitments.go deleted file mode 100644 index 5083b8552..000000000 --- a/tools/evil-spammer/programs/commitments.go +++ /dev/null @@ -1,41 +0,0 @@ -package programs - -// import ( -// "time" - -// "github.com/iotaledger/goshimmer/client/evilspammer" -// "github.com/iotaledger/goshimmer/tools/evil-spammer/identity" -// ) - -// type CommitmentsSpamParams struct { -// CommitmentType string -// Rate int -// Duration time.Duration -// TimeUnit time.Duration -// NetworkAlias string -// SpammerAlias string -// ValidAlias string -// ForkAfter int // optional, will be used only with CommitmentType = "fork" -// } - -// func CommitmentsSpam(params *CommitmentsSpamParams) { -// identity.LoadConfig() -// SpamCommitments(*params) -// } - -// func SpamCommitments(params CommitmentsSpamParams) { -// privateKey, urlAPI := identity.LoadIdentity(params.NetworkAlias, params.SpammerAlias) -// _, validAPI := identity.LoadIdentity(params.NetworkAlias, params.ValidAlias) -// options := []evilspammer.Options{ -// evilspammer.WithClientURL(urlAPI), -// evilspammer.WithValidClientURL(validAPI), -// evilspammer.WithSpamRate(params.Rate, params.TimeUnit), -// evilspammer.WithSpamDuration(params.Duration), -// evilspammer.WithSpammingFunc(evilspammer.CommitmentsSpammingFunction), -// evilspammer.WithIdentity(params.SpammerAlias, privateKey), -// evilspammer.WithCommitmentType(params.CommitmentType), -// evilspammer.WithForkAfter(params.ForkAfter), -// } -// spammer := evilspammer.NewSpammer(options...) -// spammer.Spam() -// } diff --git a/tools/evil-spammer/programs/params.go b/tools/evil-spammer/programs/params.go deleted file mode 100644 index f0e1d2070..000000000 --- a/tools/evil-spammer/programs/params.go +++ /dev/null @@ -1,22 +0,0 @@ -package programs - -import ( - "time" - - "github.com/iotaledger/iota-core/tools/evil-spammer/evilwallet" -) - -type CustomSpamParams struct { - ClientURLs []string - SpamTypes []string - Rates []int - Durations []time.Duration - BlkToBeSent []int - TimeUnit time.Duration - DelayBetweenConflicts time.Duration - NSpend int - Scenario evilwallet.EvilBatch - DeepSpam bool - EnableRateSetter bool - AccountAlias string -} diff --git a/tools/evil-spammer/programs/quick-test.go b/tools/evil-spammer/programs/quick-test.go deleted file mode 100644 index e17725594..000000000 --- a/tools/evil-spammer/programs/quick-test.go +++ /dev/null @@ -1,68 +0,0 @@ -package programs - -import ( - "time" - - "github.com/iotaledger/iota-core/tools/evil-spammer/evilwallet" - "github.com/iotaledger/iota-core/tools/evil-spammer/spammer" -) - -type QuickTestParams struct { - ClientURLs []string - Rate int - Duration time.Duration - TimeUnit time.Duration - DelayBetweenConflicts time.Duration - VerifyLedger bool - EnableRateSetter bool -} - -// QuickTest runs short spamming periods with stable mps. -func QuickTest(params *QuickTestParams) { - evilWallet := evilwallet.NewEvilWallet(evilwallet.WithClients(params.ClientURLs...)) - counter := spammer.NewErrorCount() - log.Info("Starting quick test") - - nWallets := 2 * spammer.BigWalletsNeeded(params.Rate, params.TimeUnit, params.Duration) - - log.Info("Start preparing funds") - evilWallet.RequestFreshBigFaucetWallets(nWallets) - - // define spammers - baseOptions := []spammer.Options{ - spammer.WithSpamRate(params.Rate, params.TimeUnit), - spammer.WithSpamDuration(params.Duration), - spammer.WithErrorCounter(counter), - spammer.WithEvilWallet(evilWallet), - } - - //nolint:gocritic // we want a copy here - blkOptions := append(baseOptions, - spammer.WithSpammingFunc(spammer.DataSpammingFunction), - ) - - dsScenario := evilwallet.NewEvilScenario( - evilwallet.WithScenarioCustomConflicts(evilwallet.NSpendBatch(2)), - ) - - //nolint:gocritic // we want a copy here - dsOptions := append(baseOptions, - spammer.WithEvilScenario(dsScenario), - ) - - blkSpammer := spammer.NewSpammer(blkOptions...) - txSpammer := spammer.NewSpammer(baseOptions...) - dsSpammer := spammer.NewSpammer(dsOptions...) - - // start test - txSpammer.Spam() - time.Sleep(5 * time.Second) - - blkSpammer.Spam() - time.Sleep(5 * time.Second) - - dsSpammer.Spam() - - log.Info(counter.GetErrorsSummary()) - log.Info("Quick Test finished") -} diff --git a/tools/evil-spammer/programs/spammers.go b/tools/evil-spammer/programs/spammers.go deleted file mode 100644 index b8ca4845f..000000000 --- a/tools/evil-spammer/programs/spammers.go +++ /dev/null @@ -1,230 +0,0 @@ -package programs - -import ( - "sync" - "time" - - "github.com/iotaledger/iota-core/tools/evil-spammer/accountwallet" - "github.com/iotaledger/iota-core/tools/evil-spammer/evilwallet" - "github.com/iotaledger/iota-core/tools/evil-spammer/logger" - "github.com/iotaledger/iota-core/tools/evil-spammer/spammer" -) - -var log = logger.New("customSpam") - -func CustomSpam(params *CustomSpamParams, accWallet *accountwallet.AccountWallet) { - w := evilwallet.NewEvilWallet(evilwallet.WithClients(params.ClientURLs...), evilwallet.WithAccountsWallet(accWallet)) - wg := sync.WaitGroup{} - - // funds are requested fro all spam types except SpammerTypeBlock - fundsNeeded := false - for _, st := range params.SpamTypes { - if st != spammer.TypeBlock { - fundsNeeded = true - } - } - if fundsNeeded { - err := w.RequestFreshBigFaucetWallet() - if err != nil { - panic(err) - } - } - - for i, sType := range params.SpamTypes { - log.Infof("Start spamming with rate: %d, time unit: %s, and spamming type: %s.", params.Rates[i], params.TimeUnit.String(), sType) - - switch sType { - case spammer.TypeBlock: - wg.Add(1) - go func(i int) { - defer wg.Done() - s := SpamBlocks(w, params.Rates[i], params.TimeUnit, params.Durations[i], params.BlkToBeSent[i], params.EnableRateSetter, params.AccountAlias) - if s == nil { - return - } - s.Spam() - }(i) - case spammer.TypeTx: - wg.Add(1) - go func(i int) { - defer wg.Done() - SpamTransaction(w, params.Rates[i], params.TimeUnit, params.Durations[i], params.DeepSpam, params.EnableRateSetter, params.AccountAlias) - }(i) - case spammer.TypeDs: - wg.Add(1) - go func(i int) { - defer wg.Done() - SpamDoubleSpends(w, params.Rates[i], params.NSpend, params.TimeUnit, params.Durations[i], params.DelayBetweenConflicts, params.DeepSpam, params.EnableRateSetter, params.AccountAlias) - }(i) - case spammer.TypeCustom: - wg.Add(1) - go func(i int) { - defer wg.Done() - s := SpamNestedConflicts(w, params.Rates[i], params.TimeUnit, params.Durations[i], params.Scenario, params.DeepSpam, false, params.EnableRateSetter, params.AccountAlias) - if s == nil { - return - } - s.Spam() - }(i) - case spammer.TypeCommitments: - wg.Add(1) - go func() { - defer wg.Done() - }() - case spammer.TypeAccounts: - wg.Add(1) - go func() { - defer wg.Done() - - s := SpamAccounts(w, params.Rates[i], params.TimeUnit, params.Durations[i], params.EnableRateSetter, params.AccountAlias) - if s == nil { - return - } - s.Spam() - }() - - default: - log.Warn("Spamming type not recognized. Try one of following: tx, ds, blk, custom, commitments") - } - } - - wg.Wait() - log.Info("Basic spamming finished!") -} - -func SpamTransaction(w *evilwallet.EvilWallet, rate int, timeUnit, duration time.Duration, deepSpam, enableRateSetter bool, accountAlias string) { - if w.NumOfClient() < 1 { - log.Infof("Warning: At least one client is needed to spam.") - } - - scenarioOptions := []evilwallet.ScenarioOption{ - evilwallet.WithScenarioCustomConflicts(evilwallet.SingleTransactionBatch()), - } - if deepSpam { - outWallet := evilwallet.NewWallet(evilwallet.Reuse) - scenarioOptions = append(scenarioOptions, - evilwallet.WithScenarioDeepSpamEnabled(), - evilwallet.WithScenarioReuseOutputWallet(outWallet), - evilwallet.WithScenarioInputWalletForDeepSpam(outWallet), - ) - } - scenarioTx := evilwallet.NewEvilScenario(scenarioOptions...) - - options := []spammer.Options{ - spammer.WithSpamRate(rate, timeUnit), - spammer.WithSpamDuration(duration), - spammer.WithRateSetter(enableRateSetter), - spammer.WithEvilWallet(w), - spammer.WithEvilScenario(scenarioTx), - spammer.WithAccountAlias(accountAlias), - } - - s := spammer.NewSpammer(options...) - s.Spam() -} - -func SpamDoubleSpends(w *evilwallet.EvilWallet, rate, nSpent int, timeUnit, duration, delayBetweenConflicts time.Duration, deepSpam, enableRateSetter bool, accountAlias string) { - log.Debugf("Setting up double spend spammer with rate: %d, time unit: %s, and duration: %s.", rate, timeUnit.String(), duration.String()) - if w.NumOfClient() < 2 { - log.Infof("Warning: At least two client are needed to spam, and %d was provided", w.NumOfClient()) - } - - scenarioOptions := []evilwallet.ScenarioOption{ - evilwallet.WithScenarioCustomConflicts(evilwallet.NSpendBatch(nSpent)), - } - if deepSpam { - outWallet := evilwallet.NewWallet(evilwallet.Reuse) - scenarioOptions = append(scenarioOptions, - evilwallet.WithScenarioDeepSpamEnabled(), - evilwallet.WithScenarioReuseOutputWallet(outWallet), - evilwallet.WithScenarioInputWalletForDeepSpam(outWallet), - ) - } - scenarioDs := evilwallet.NewEvilScenario(scenarioOptions...) - options := []spammer.Options{ - spammer.WithSpamRate(rate, timeUnit), - spammer.WithSpamDuration(duration), - spammer.WithEvilWallet(w), - spammer.WithRateSetter(enableRateSetter), - spammer.WithTimeDelayForDoubleSpend(delayBetweenConflicts), - spammer.WithEvilScenario(scenarioDs), - spammer.WithAccountAlias(accountAlias), - } - - s := spammer.NewSpammer(options...) - s.Spam() -} - -func SpamNestedConflicts(w *evilwallet.EvilWallet, rate int, timeUnit, duration time.Duration, conflictBatch evilwallet.EvilBatch, deepSpam, reuseOutputs, enableRateSetter bool, accountAlias string) *spammer.Spammer { - scenarioOptions := []evilwallet.ScenarioOption{ - evilwallet.WithScenarioCustomConflicts(conflictBatch), - } - if deepSpam { - outWallet := evilwallet.NewWallet(evilwallet.Reuse) - scenarioOptions = append(scenarioOptions, - evilwallet.WithScenarioDeepSpamEnabled(), - evilwallet.WithScenarioReuseOutputWallet(outWallet), - evilwallet.WithScenarioInputWalletForDeepSpam(outWallet), - ) - } else if reuseOutputs { - outWallet := evilwallet.NewWallet(evilwallet.Reuse) - scenarioOptions = append(scenarioOptions, evilwallet.WithScenarioReuseOutputWallet(outWallet)) - } - scenario := evilwallet.NewEvilScenario(scenarioOptions...) - if scenario.NumOfClientsNeeded > w.NumOfClient() { - log.Infof("Warning: At least %d client are needed to spam, and %d was provided", scenario.NumOfClientsNeeded, w.NumOfClient()) - } - - options := []spammer.Options{ - spammer.WithSpamRate(rate, timeUnit), - spammer.WithSpamDuration(duration), - spammer.WithEvilWallet(w), - spammer.WithRateSetter(enableRateSetter), - spammer.WithEvilScenario(scenario), - spammer.WithAccountAlias(accountAlias), - } - - return spammer.NewSpammer(options...) -} - -func SpamBlocks(w *evilwallet.EvilWallet, rate int, timeUnit, duration time.Duration, numBlkToSend int, enableRateSetter bool, accountAlias string) *spammer.Spammer { - if w.NumOfClient() < 1 { - log.Infof("Warning: At least one client is needed to spam.") - } - - options := []spammer.Options{ - spammer.WithSpamRate(rate, timeUnit), - spammer.WithSpamDuration(duration), - spammer.WithBatchesSent(numBlkToSend), - spammer.WithRateSetter(enableRateSetter), - spammer.WithEvilWallet(w), - spammer.WithSpammingFunc(spammer.DataSpammingFunction), - spammer.WithAccountAlias(accountAlias), - } - - return spammer.NewSpammer(options...) -} - -func SpamAccounts(w *evilwallet.EvilWallet, rate int, timeUnit, duration time.Duration, enableRateSetter bool, accountAlias string) *spammer.Spammer { - if w.NumOfClient() < 1 { - log.Infof("Warning: At least one client is needed to spam.") - } - scenarioOptions := []evilwallet.ScenarioOption{ - evilwallet.WithScenarioCustomConflicts(evilwallet.SingleTransactionBatch()), - evilwallet.WithCreateAccounts(), - } - - scenarioAccount := evilwallet.NewEvilScenario(scenarioOptions...) - - options := []spammer.Options{ - spammer.WithSpamRate(rate, timeUnit), - spammer.WithSpamDuration(duration), - spammer.WithRateSetter(enableRateSetter), - spammer.WithEvilWallet(w), - spammer.WithSpammingFunc(spammer.AccountSpammingFunction), - spammer.WithEvilScenario(scenarioAccount), - spammer.WithAccountAlias(accountAlias), - } - - return spammer.NewSpammer(options...) -} diff --git a/tools/evil-spammer/spammer/clock.go b/tools/evil-spammer/spammer/clock.go deleted file mode 100644 index 87a3cc069..000000000 --- a/tools/evil-spammer/spammer/clock.go +++ /dev/null @@ -1,76 +0,0 @@ -package spammer - -// import ( -// "time" - -// iotago "github.com/iotaledger/iota.go/v4" -// "github.com/iotaledger/iota-core/tools/evil-spammer/wallet" -// ) - -// // region ClockSync //////////////evilspammerpkg///////////////////////////////////////////////////////////////////////////////// - -// // ClockSync is used to synchronize with connected nodes. -// type ClockSync struct { -// LatestCommittedSlotClock *SlotClock - -// syncTicker *time.Ticker -// clt wallet.Client -// } - -// func NewClockSync(slotDuration time.Duration, syncInterval time.Duration, clientList wallet.Client) *ClockSync { -// updateTicker := time.NewTicker(syncInterval) -// return &ClockSync{ -// LatestCommittedSlotClock: &SlotClock{slotDuration: slotDuration}, - -// syncTicker: updateTicker, -// clt: clientList, -// } -// } - -// // Start starts the clock synchronization in the background after the first sync is done.. -// func (c *ClockSync) Start() { -// c.Synchronize() -// go func() { -// for range c.syncTicker.C { -// c.Synchronize() -// } -// }() -// } - -// func (c *ClockSync) Shutdown() { -// c.syncTicker.Stop() -// } - -// func (c *ClockSync) Synchronize() { -// si, err := c.clt.GetLatestCommittedSlot() -// if err != nil { -// return -// } -// c.LatestCommittedSlotClock.Update(si) -// } - -// // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////// - -// // region SlotClock /////////////////////////////////////////////////////////////////////////////////////////////// - -// type SlotClock struct { -// lastUpdated time.Time -// updatedSlot slot.Index - -// slotDuration time.Duration -// } - -// func (c *SlotClock) Update(value slot.Index) { -// c.lastUpdated = time.Now() -// c.updatedSlot = value -// } - -// func (c *SlotClock) Get() slot.Index { -// return c.updatedSlot -// } - -// func (c *SlotClock) GetRelative() slot.Index { -// return c.updatedSlot + slot.Index(time.Since(c.lastUpdated)/c.slotDuration) -// } - -// // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/tools/evil-spammer/spammer/commitmentmanager.go b/tools/evil-spammer/spammer/commitmentmanager.go deleted file mode 100644 index dc9181f04..000000000 --- a/tools/evil-spammer/spammer/commitmentmanager.go +++ /dev/null @@ -1,245 +0,0 @@ -package spammer - -// import ( -// "crypto/sha256" -// "math/rand" -// "time" - -// "github.com/iotaledger/hive.go/core/slot" -// "github.com/iotaledger/iota-core/tools/evil-spammer/wallet" -// iotago "github.com/iotaledger/iota.go/v4" - -// "github.com/pkg/errors" -// ) - -// type CommitmentManagerParams struct { -// CommitmentType string -// ValidClientURL string -// ParentRefsCount int -// ClockResyncTime time.Duration -// GenesisTime time.Time -// SlotDuration time.Duration - -// OptionalForkAfter int -// } -// type CommitmentManager struct { -// Params *CommitmentManagerParams -// // we store here only the valid commitments to not request them again through API -// validChain map[slot.Index]*iotago.Commitment -// // commitments used to spam -// commitmentChain map[slot.Index]*iotago.Commitment - -// initiationSlot slot.Index -// forkIndex slot.Index -// latestCommitted slot.Index - -// clockSync *ClockSync -// validClient wallet.Client - -// log Logger -// } - -// func NewCommitmentManager() *CommitmentManager { -// return &CommitmentManager{ -// Params: &CommitmentManagerParams{ -// ParentRefsCount: 2, -// ClockResyncTime: 30 * time.Second, -// GenesisTime: time.Now(), -// SlotDuration: 5 * time.Second, -// }, -// validChain: make(map[slot.Index]*iotago.Commitment), -// commitmentChain: make(map[slot.Index]*iotago.Commitment), -// } -// } - -// func (c *CommitmentManager) Setup(l Logger) { -// c.log = l - -// c.log.Infof("Commitment Manager will be based on the valid client: %s", c.Params.ValidClientURL) -// c.validClient = wallet.NewWebClient(c.Params.ValidClientURL) -// c.setupTimeParams(c.validClient) - -// c.clockSync = NewClockSync(c.Params.SlotDuration, c.Params.ClockResyncTime, c.validClient) -// c.clockSync.Start() - -// c.setupForkingPoint() -// c.setupInitCommitment() -// } - -// // SetupInitCommitment sets the initiation commitment which is the current valid commitment requested from validClient. -// func (c *CommitmentManager) setupInitCommitment() { -// c.initiationSlot = c.clockSync.LatestCommittedSlotClock.Get() -// comm, err := c.getValidCommitment(c.initiationSlot) -// if err != nil { -// panic(errors.Wrapf(err, "failed to get initiation commitment")) -// } -// c.commitmentChain[comm.Slot()] = comm -// c.latestCommitted = comm.Slot() -// } - -// // SetupTimeParams requests through API and sets the genesis time and slot duration for the commitment manager. -// func (c *CommitmentManager) setupTimeParams(clt wallet.Client) { -// genesisTime, slotDuration, err := clt.GetTimeProvider() -// if err != nil { -// panic(errors.Wrapf(err, "failed to get time provider for the commitment manager setup")) -// } -// c.Params.GenesisTime = genesisTime -// c.Params.SlotDuration = slotDuration -// } - -// func (c *CommitmentManager) SetCommitmentType(commitmentType string) { -// c.Params.CommitmentType = commitmentType -// } - -// func (c *CommitmentManager) SetForkAfter(forkAfter int) { -// c.Params.OptionalForkAfter = forkAfter -// } - -// // SetupForkingPoint sets the forking point for the commitment manager. It uses ForkAfter parameter so need to be called after params are read. -// func (c *CommitmentManager) setupForkingPoint() { -// c.forkIndex = c.clockSync.LatestCommittedSlotClock.Get() + slot.Index(c.Params.OptionalForkAfter) -// } - -// func (c *CommitmentManager) Shutdown() { -// c.clockSync.Shutdown() -// } - -// func (c *CommitmentManager) commit(comm *iotago.Commitment) { -// c.commitmentChain[comm.Slot()] = comm -// if comm.Slot() > c.latestCommitted { -// if comm.Slot()-c.latestCommitted != 1 { -// panic("next committed slot is not sequential, lastCommitted: " + c.latestCommitted.String() + " nextCommitted: " + comm.Slot().String()) -// } -// c.latestCommitted = comm.Slot() -// } -// } - -// func (c *CommitmentManager) getLatestCommitment() *iotago.Commitment { -// return c.commitmentChain[c.latestCommitted] -// } - -// // GenerateCommitment generates a commitment based on the commitment type provided in spam details. -// func (c *CommitmentManager) GenerateCommitment(clt wallet.Client) (*iotago.Commitment, slot.Index, error) { -// switch c.Params.CommitmentType { -// // todo refactor this to work with chainsA -// case "latest": -// comm, err := clt.GetLatestCommitment() -// if err != nil { -// return nil, 0, errors.Wrap(err, "failed to get latest commitment") -// } -// index, err := clt.GetLatestConfirmedIndex() -// if err != nil { -// return nil, 0, errors.Wrap(err, "failed to get latest confirmed index") -// } -// return comm, index, err -// case "random": -// slot := c.clockSync.LatestCommittedSlotClock.Get() -// newCommitment := randomCommitmentChain(slot) - -// return newCommitment, slot - 10, nil - -// case "fork": -// // it should request time periodically, and be relative -// slot := c.clockSync.LatestCommittedSlotClock.Get() -// // make sure chain is upto date to the forking point -// uptoSlot := c.forkIndex -// // get minimum -// if slot < c.forkIndex { -// uptoSlot = slot -// } -// err := c.updateChainWithValidCommitment(uptoSlot) -// if err != nil { -// return nil, 0, errors.Wrap(err, "failed to update chain with valid commitment") -// } -// if c.isAfterForkPoint(slot) { -// c.updateForkedChain(slot) -// } -// comm := c.getLatestCommitment() -// index, err := clt.GetLatestConfirmedIndex() -// if err != nil { -// return nil, 0, errors.Wrap(err, "failed to get latest confirmed index") -// } -// return comm, index - 1, nil -// } -// return nil, 0, nil -// } - -// func (c *CommitmentManager) isAfterForkPoint(slot slot.Index) bool { -// return c.forkIndex != 0 && slot > c.forkIndex -// } - -// // updateChainWithValidCommitment commits the chain up to the given slot with the valid commitments. -// func (c *CommitmentManager) updateChainWithValidCommitment(s slot.Index) error { -// for i := c.latestCommitted + 1; i <= s; i++ { -// comm, err := c.getValidCommitment(i) -// if err != nil { -// return errors.Wrapf(err, "failed to get valid commitment for slot %d", i) -// } -// c.commit(comm) -// } -// return nil -// } - -// func (c *CommitmentManager) updateForkedChain(slot slot.Index) { -// for i := c.latestCommitted + 1; i <= slot; i++ { -// comm, err := c.getForkedCommitment(i) -// if err != nil { -// panic(errors.Wrapf(err, "failed to get forked commitment for slot %d", i)) -// } -// c.commit(comm) -// } -// } - -// // getValidCommitment returns the valid commitment for the given slot if not exists it requests it from the node and update the validChain. -// func (c *CommitmentManager) getValidCommitment(slot slot.Index) (*commitment.Commitment, error) { -// if comm, ok := c.validChain[slot]; ok { -// return comm, nil -// } -// // if not requested before then get it from the node -// comm, err := c.validClient.GetCommitment(int(slot)) -// if err != nil { -// return nil, errors.Wrapf(err, "failed to get commitment for slot %d", slot) -// } -// c.validChain[slot] = comm - -// return comm, nil -// } - -// func (c *CommitmentManager) getForkedCommitment(slot slot.Index) (*commitment.Commitment, error) { -// validComm, err := c.getValidCommitment(slot) -// if err != nil { -// return nil, errors.Wrapf(err, "failed to get valid commitment for slot %d", slot) -// } -// prevComm := c.commitmentChain[slot-1] -// forkedComm := commitment.New( -// validComm.Slot(), -// prevComm.ID(), -// randomRoot(), -// validComm.CumulativeWeight(), -// ) -// return forkedComm, nil -// } - -// func randomCommitmentChain(currSlot slot.Index) *commitment.Commitment { -// chain := make([]*commitment.Commitment, currSlot+1) -// chain[0] = commitment.NewEmptyCommitment() -// for i := slot.Index(0); i < currSlot-1; i++ { -// prevComm := chain[i] -// newCommitment := commitment.New( -// i, -// prevComm.ID(), -// randomRoot(), -// 100, -// ) -// chain[i+1] = newCommitment -// } -// return chain[currSlot-1] -// } - -// func randomRoot() [32]byte { -// data := make([]byte, 10) -// for i := range data { -// data[i] = byte(rand.Intn(256)) -// } -// return sha256.Sum256(data) -// } diff --git a/tools/evil-spammer/spammer/errors.go b/tools/evil-spammer/spammer/errors.go deleted file mode 100644 index 4c056ffaa..000000000 --- a/tools/evil-spammer/spammer/errors.go +++ /dev/null @@ -1,69 +0,0 @@ -package spammer - -import ( - "fmt" - - "go.uber.org/atomic" - - "github.com/iotaledger/hive.go/ierrors" - "github.com/iotaledger/hive.go/runtime/syncutils" -) - -var ( - ErrFailPostBlock = ierrors.New("failed to post block") - ErrFailSendDataBlock = ierrors.New("failed to send a data block") - ErrFailGetReferences = ierrors.New("failed to get references") - ErrTransactionIsNil = ierrors.New("provided transaction is nil") - ErrTransactionInvalid = ierrors.New("provided transaction is invalid") - ErrPayloadIsNil = ierrors.New("provided payload is nil") - ErrFailToPrepareBatch = ierrors.New("custom conflict batch could not be prepared") - ErrInsufficientClients = ierrors.New("insufficient clients to send conflicts") - ErrInputsNotSolid = ierrors.New("not all inputs are solid") - ErrFailPrepareBlock = ierrors.New("failed to prepare block") - ErrFailGetAccount = ierrors.New("failed to get account from the account wallet") -) - -// ErrorCounter counts errors that appeared during the spam, -// as during the spam they are ignored and allows to print the summary (might be useful for debugging). -type ErrorCounter struct { - errorsMap map[error]*atomic.Int64 - errInTotalCount *atomic.Int64 - mutex syncutils.RWMutex -} - -func NewErrorCount() *ErrorCounter { - e := &ErrorCounter{ - errorsMap: make(map[error]*atomic.Int64), - errInTotalCount: atomic.NewInt64(0), - } - - return e -} - -func (e *ErrorCounter) CountError(err error) { - e.mutex.Lock() - defer e.mutex.Unlock() - - // check if error is already in the map - if _, ok := e.errorsMap[err]; !ok { - e.errorsMap[err] = atomic.NewInt64(0) - } - e.errInTotalCount.Add(1) - e.errorsMap[err].Add(1) -} - -func (e *ErrorCounter) GetTotalErrorCount() int64 { - return e.errInTotalCount.Load() -} - -func (e *ErrorCounter) GetErrorsSummary() string { - if len(e.errorsMap) == 0 { - return "No errors encountered" - } - blk := "Errors encountered during spam:\n" - for key, value := range e.errorsMap { - blk += fmt.Sprintf("%s: %d\n", key.Error(), value.Load()) - } - - return blk -} diff --git a/tools/evil-spammer/spammer/options.go b/tools/evil-spammer/spammer/options.go deleted file mode 100644 index e42eca8a1..000000000 --- a/tools/evil-spammer/spammer/options.go +++ /dev/null @@ -1,165 +0,0 @@ -package spammer - -import ( - "time" - - "github.com/iotaledger/iota-core/tools/evil-spammer/evilwallet" - "github.com/iotaledger/iota-core/tools/evil-spammer/models" -) - -type Options func(*Spammer) - -// region Spammer general options /////////////////////////////////////////////////////////////////////////////////////////////////// - -// WithSpamRate provides spammer with options regarding rate, time unit, and finishing spam criteria. Provide 0 to one of max parameters to skip it. -func WithSpamRate(rate int, timeUnit time.Duration) Options { - return func(s *Spammer) { - if s.SpamDetails == nil { - s.SpamDetails = &SpamDetails{ - Rate: rate, - TimeUnit: timeUnit, - } - } else { - s.SpamDetails.Rate = rate - s.SpamDetails.TimeUnit = timeUnit - } - } -} - -// WithSpamDuration provides spammer with options regarding rate, time unit, and finishing spam criteria. Provide 0 to one of max parameters to skip it. -func WithSpamDuration(maxDuration time.Duration) Options { - return func(s *Spammer) { - if s.SpamDetails == nil { - s.SpamDetails = &SpamDetails{ - MaxDuration: maxDuration, - } - } else { - s.SpamDetails.MaxDuration = maxDuration - } - } -} - -// WithErrorCounter allows for setting an error counter object, if not provided a new instance will be created. -func WithErrorCounter(errCounter *ErrorCounter) Options { - return func(s *Spammer) { - s.ErrCounter = errCounter - } -} - -// WithLogTickerInterval allows for changing interval between progress spamming logs, default is 30s. -func WithLogTickerInterval(interval time.Duration) Options { - return func(s *Spammer) { - s.State.logTickTime = interval - } -} - -// WithSpammingFunc sets core function of the spammer with spamming logic, needs to use done spammer's channel to communicate. -// end of spamming and errors. Default one is the CustomConflictSpammingFunc. -func WithSpammingFunc(spammerFunc func(s *Spammer)) Options { - return func(s *Spammer) { - s.spamFunc = spammerFunc - } -} - -// WithAccountAlias sets the alias of the account that will be used to pay with mana for sent blocks. -func WithAccountAlias(alias string) Options { - return func(s *Spammer) { - s.IssuerAlias = alias - } -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// region Spammer EvilWallet options /////////////////////////////////////////////////////////////////////////////////////////////////// - -// WithRateSetter enables setting rate of spammer. -func WithRateSetter(enable bool) Options { - return func(s *Spammer) { - s.UseRateSetter = enable - } -} - -// WithBatchesSent provides spammer with options regarding rate, time unit, and finishing spam criteria. Provide 0 to one of max parameters to skip it. -func WithBatchesSent(maxBatchesSent int) Options { - return func(s *Spammer) { - if s.SpamDetails == nil { - s.SpamDetails = &SpamDetails{ - MaxBatchesSent: maxBatchesSent, - } - } else { - s.SpamDetails.MaxBatchesSent = maxBatchesSent - } - } -} - -// WithEvilWallet provides evil wallet instance, that will handle all spam logic according to provided EvilScenario. -func WithEvilWallet(initWallets *evilwallet.EvilWallet) Options { - return func(s *Spammer) { - s.EvilWallet = initWallets - } -} - -// WithEvilScenario provides initWallet of spammer, if omitted spammer will prepare funds based on maxBlkSent parameter. -func WithEvilScenario(scenario *evilwallet.EvilScenario) Options { - return func(s *Spammer) { - s.EvilScenario = scenario - } -} - -func WithTimeDelayForDoubleSpend(timeDelay time.Duration) Options { - return func(s *Spammer) { - s.TimeDelayBetweenConflicts = timeDelay - } -} - -// WithNumberOfSpends sets how many transactions should be created with the same input, e.g 3 for triple spend, -// 2 for double spend. For this to work user needs to make sure that there is enough number of clients. -func WithNumberOfSpends(n int) Options { - return func(s *Spammer) { - s.NumberOfSpends = n - } -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// region Spammer Commitment options /////////////////////////////////////////////////////////////////////////////////////////////////// - -func WithClientURL(clientURL string) Options { - return func(s *Spammer) { - s.Clients = models.NewWebClients([]string{clientURL}) - } -} - -// func WithValidClientURL(validClient string) Options { -// return func(s *Spammer) { -// s.CommitmentManager.Params.ValidClientURL = validClient -// } -// } - -// WithCommitmentType provides commitment type for the spammer, allowed types: fork, valid, random. Enables commitment spam and disables the wallet functionality. -// func WithCommitmentType(commitmentType string) Options { -// return func(s *Spammer) { -// s.SpamType = SpamCommitments -// s.CommitmentManager.SetCommitmentType(commitmentType) -// } -// } - -// WithForkAfter provides after how many slots from the spammer setup should fork bee created, this option can be used with CommitmentType: fork. -// func WithForkAfter(forkingAfter int) Options { -// return func(s *Spammer) { -// s.CommitmentManager.SetForkAfter(forkingAfter) -// } -// } - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -type SpamDetails struct { - Rate int - TimeUnit time.Duration - MaxDuration time.Duration - MaxBatchesSent int -} - -type CommitmentSpamDetails struct { - CommitmentType string -} diff --git a/tools/evil-spammer/spammer/spammer.go b/tools/evil-spammer/spammer/spammer.go deleted file mode 100644 index 83b5d94e8..000000000 --- a/tools/evil-spammer/spammer/spammer.go +++ /dev/null @@ -1,293 +0,0 @@ -package spammer - -import ( - "time" - - "go.uber.org/atomic" - - "github.com/iotaledger/hive.go/app/configuration" - appLogger "github.com/iotaledger/hive.go/app/logger" - "github.com/iotaledger/hive.go/ds/types" - "github.com/iotaledger/hive.go/ierrors" - "github.com/iotaledger/hive.go/logger" - "github.com/iotaledger/iota-core/pkg/protocol/snapshotcreator" - "github.com/iotaledger/iota-core/tools/evil-spammer/evilwallet" - "github.com/iotaledger/iota-core/tools/evil-spammer/models" - "github.com/iotaledger/iota-core/tools/genesis-snapshot/presets" - iotago "github.com/iotaledger/iota.go/v4" -) - -const ( - TypeBlock = "blk" - TypeTx = "tx" - TypeDs = "ds" - TypeCustom = "custom" - TypeCommitments = "commitments" - TypeAccounts = "accounts" -) - -// region Spammer ////////////////////////////////////////////////////////////////////////////////////////////////////// - -//nolint:revive -type SpammerFunc func(*Spammer) - -type State struct { - spamTicker *time.Ticker - logTicker *time.Ticker - spamStartTime time.Time - txSent *atomic.Int64 - batchPrepared *atomic.Int64 - - logTickTime time.Duration - spamDuration time.Duration -} - -type SpamType int - -const ( - SpamEvilWallet SpamType = iota - SpamCommitments -) - -// Spammer is a utility object for new spammer creations, can be modified by passing options. -// Mandatory options: WithClients, WithSpammingFunc -// Not mandatory options, if not provided spammer will use default settings: -// WithSpamDetails, WithEvilWallet, WithErrorCounter, WithLogTickerInterval. -type Spammer struct { - SpamDetails *SpamDetails - State *State - UseRateSetter bool - SpamType SpamType - Clients models.Connector - EvilWallet *evilwallet.EvilWallet - EvilScenario *evilwallet.EvilScenario - // CommitmentManager *CommitmentManager - ErrCounter *ErrorCounter - IssuerAlias string - - log Logger - api iotago.API - - // accessed from spamming functions - done chan bool - shutdown chan types.Empty - spamFunc SpammerFunc - - TimeDelayBetweenConflicts time.Duration - NumberOfSpends int -} - -// NewSpammer is a constructor of Spammer. -func NewSpammer(options ...Options) *Spammer { - protocolParams := snapshotcreator.NewOptions(presets.Docker...).ProtocolParameters - api := iotago.V3API(protocolParams) - - state := &State{ - txSent: atomic.NewInt64(0), - batchPrepared: atomic.NewInt64(0), - logTickTime: time.Second * 30, - } - s := &Spammer{ - SpamDetails: &SpamDetails{}, - spamFunc: CustomConflictSpammingFunc, - State: state, - SpamType: SpamEvilWallet, - EvilScenario: evilwallet.NewEvilScenario(), - // CommitmentManager: NewCommitmentManager(), - UseRateSetter: true, - done: make(chan bool), - shutdown: make(chan types.Empty), - NumberOfSpends: 2, - api: api, - } - - for _, opt := range options { - opt(s) - } - - s.setup() - - return s -} - -func (s *Spammer) BlocksSent() uint64 { - return uint64(s.State.txSent.Load()) -} - -func (s *Spammer) BatchesPrepared() uint64 { - return uint64(s.State.batchPrepared.Load()) -} - -func (s *Spammer) setup() { - if s.log == nil { - s.initLogger() - } - - switch s.SpamType { - case SpamEvilWallet: - if s.EvilWallet == nil { - s.EvilWallet = evilwallet.NewEvilWallet() - } - s.Clients = s.EvilWallet.Connector() - // case SpamCommitments: - // s.CommitmentManager.Setup(s.log) - } - s.setupSpamDetails() - - s.State.spamTicker = s.initSpamTicker() - s.State.logTicker = s.initLogTicker() - - if s.ErrCounter == nil { - s.ErrCounter = NewErrorCount() - } -} - -func (s *Spammer) setupSpamDetails() { - if s.SpamDetails.Rate <= 0 { - s.SpamDetails.Rate = 1 - } - if s.SpamDetails.TimeUnit == 0 { - s.SpamDetails.TimeUnit = time.Second - } - // provided only maxBlkSent, calculating the default max for maxDuration - if s.SpamDetails.MaxDuration == 0 && s.SpamDetails.MaxBatchesSent > 0 { - s.SpamDetails.MaxDuration = time.Hour * 100 - } - // provided only maxDuration, calculating the default max for maxBlkSent - if s.SpamDetails.MaxBatchesSent == 0 && s.SpamDetails.MaxDuration > 0 { - s.SpamDetails.MaxBatchesSent = int(s.SpamDetails.MaxDuration.Seconds()/s.SpamDetails.TimeUnit.Seconds()*float64(s.SpamDetails.Rate)) + 1 - } -} - -func (s *Spammer) initLogger() { - config := configuration.New() - _ = appLogger.InitGlobalLogger(config) - logger.SetLevel(logger.LevelDebug) - s.log = logger.NewLogger("Spammer") -} - -func (s *Spammer) initSpamTicker() *time.Ticker { - tickerTime := float64(s.SpamDetails.TimeUnit) / float64(s.SpamDetails.Rate) - return time.NewTicker(time.Duration(tickerTime)) -} - -func (s *Spammer) initLogTicker() *time.Ticker { - return time.NewTicker(s.State.logTickTime) -} - -// Spam runs the spammer. Function will stop after maxDuration time will pass or when maxBlkSent will be exceeded. -func (s *Spammer) Spam() { - s.log.Infof("Start spamming transactions with %d rate", s.SpamDetails.Rate) - - s.State.spamStartTime = time.Now() - timeExceeded := time.After(s.SpamDetails.MaxDuration) - - go func() { - goroutineCount := atomic.NewInt32(0) - for { - select { - case <-s.State.logTicker.C: - s.log.Infof("Blocks issued so far: %d, errors encountered: %d", s.State.txSent.Load(), s.ErrCounter.GetTotalErrorCount()) - case <-timeExceeded: - s.log.Infof("Maximum spam duration exceeded, stopping spammer....") - s.StopSpamming() - - return - case <-s.done: - s.StopSpamming() - return - case <-s.State.spamTicker.C: - if goroutineCount.Load() > 100 { - break - } - go func() { - goroutineCount.Inc() - defer goroutineCount.Dec() - s.spamFunc(s) - }() - } - } - }() - <-s.shutdown - s.log.Info(s.ErrCounter.GetErrorsSummary()) - s.log.Infof("Finishing spamming, total txns sent: %v, TotalTime: %v, Rate: %f", s.State.txSent.Load(), s.State.spamDuration.Seconds(), float64(s.State.txSent.Load())/s.State.spamDuration.Seconds()) -} - -func (s *Spammer) CheckIfAllSent() { - if s.State.batchPrepared.Load() >= int64(s.SpamDetails.MaxBatchesSent) { - s.log.Infof("Maximum number of blocks sent, stopping spammer...") - s.done <- true - } -} - -// StopSpamming finishes tasks before shutting down the spammer. -func (s *Spammer) StopSpamming() { - s.State.spamDuration = time.Since(s.State.spamStartTime) - s.State.spamTicker.Stop() - s.State.logTicker.Stop() - // s.CommitmentManager.Shutdown() - s.shutdown <- types.Void -} - -func (s *Spammer) PrepareAndPostBlock(txData *models.PayloadIssuanceData, issuerAlias string, clt models.Client) { - if txData.Payload == nil { - s.log.Debug(ErrPayloadIsNil) - s.ErrCounter.CountError(ErrPayloadIsNil) - - return - } - issuerAccount, err := s.EvilWallet.GetAccount(issuerAlias) - if err != nil { - s.log.Debug(ierrors.Wrapf(ErrFailGetAccount, err.Error())) - s.ErrCounter.CountError(ierrors.Wrapf(ErrFailGetAccount, err.Error())) - - return - } - blockID, err := s.EvilWallet.PrepareAndPostBlock(clt, txData.Payload, txData.CongestionResponse, issuerAccount) - if err != nil { - s.log.Debug(ierrors.Wrapf(ErrFailPostBlock, err.Error())) - s.ErrCounter.CountError(ierrors.Wrapf(ErrFailPostBlock, err.Error())) - - return - } - - if txData.Payload.PayloadType() != iotago.PayloadSignedTransaction { - return - } - - signedTx := txData.Payload.(*iotago.SignedTransaction) - - txID, err := signedTx.Transaction.ID() - if err != nil { - s.log.Debug(ierrors.Wrapf(ErrTransactionInvalid, err.Error())) - s.ErrCounter.CountError(ierrors.Wrapf(ErrTransactionInvalid, err.Error())) - - return - } - - // reuse outputs - if txData.Payload.PayloadType() == iotago.PayloadSignedTransaction { - if s.EvilScenario.OutputWallet.Type() == evilwallet.Reuse { - var outputIDs iotago.OutputIDs - for index := range signedTx.Transaction.Outputs { - outputIDs = append(outputIDs, iotago.OutputIDFromTransactionIDAndIndex(txID, uint16(index))) - } - s.EvilWallet.SetTxOutputsSolid(outputIDs, clt.URL()) - } - } - count := s.State.txSent.Add(1) - s.log.Debugf("Last block sent, ID: %s, txCount: %d", blockID.ToHex(), count) -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// - -type Logger interface { - Infof(template string, args ...interface{}) - Info(args ...interface{}) - Debugf(template string, args ...interface{}) - Debug(args ...interface{}) - Warn(args ...interface{}) - Warnf(template string, args ...interface{}) - Error(args ...interface{}) - Errorf(template string, args ...interface{}) -} diff --git a/tools/evil-spammer/spammer/spamming_functions.go b/tools/evil-spammer/spammer/spamming_functions.go deleted file mode 100644 index 2462c07ca..000000000 --- a/tools/evil-spammer/spammer/spamming_functions.go +++ /dev/null @@ -1,138 +0,0 @@ -package spammer - -import ( - "math/rand" - "sync" - "time" - - "github.com/iotaledger/hive.go/ierrors" - "github.com/iotaledger/iota-core/tools/evil-spammer/models" - iotago "github.com/iotaledger/iota.go/v4" -) - -func DataSpammingFunction(s *Spammer) { - clt := s.Clients.GetClient() - // sleep randomly to avoid issuing blocks in different goroutines at once - //nolint:gosec - time.Sleep(time.Duration(rand.Float64()*20) * time.Millisecond) - - s.PrepareAndPostBlock(&models.PayloadIssuanceData{ - Payload: &iotago.TaggedData{ - Tag: []byte("SPAM"), - }, - }, s.IssuerAlias, clt) - - s.State.batchPrepared.Add(1) - s.CheckIfAllSent() -} - -func CustomConflictSpammingFunc(s *Spammer) { - conflictBatch, aliases, err := s.EvilWallet.PrepareCustomConflictsSpam(s.EvilScenario, &models.IssuancePaymentStrategy{ - AllotmentStrategy: models.AllotmentStrategyAll, - IssuerAlias: s.IssuerAlias, - }) - - if err != nil { - s.log.Debugf(ierrors.Wrap(ErrFailToPrepareBatch, err.Error()).Error()) - s.ErrCounter.CountError(ierrors.Wrap(ErrFailToPrepareBatch, err.Error())) - } - - for _, txsData := range conflictBatch { - clients := s.Clients.GetClients(len(txsData)) - if len(txsData) > len(clients) { - s.log.Debug(ErrFailToPrepareBatch) - s.ErrCounter.CountError(ErrInsufficientClients) - } - - // send transactions in parallel - wg := sync.WaitGroup{} - for i, txData := range txsData { - wg.Add(1) - go func(clt models.Client, tx *models.PayloadIssuanceData) { - defer wg.Done() - - // sleep randomly to avoid issuing blocks in different goroutines at once - //nolint:gosec - time.Sleep(time.Duration(rand.Float64()*100) * time.Millisecond) - - s.PrepareAndPostBlock(tx, s.IssuerAlias, clt) - }(clients[i], txData) - } - wg.Wait() - } - s.State.batchPrepared.Add(1) - s.EvilWallet.ClearAliases(aliases) - s.CheckIfAllSent() -} - -func AccountSpammingFunction(s *Spammer) { - clt := s.Clients.GetClient() - // update scenario - txData, aliases, err := s.EvilWallet.PrepareAccountSpam(s.EvilScenario, &models.IssuancePaymentStrategy{ - AllotmentStrategy: models.AllotmentStrategyAll, - IssuerAlias: s.IssuerAlias, - }) - if err != nil { - s.log.Debugf(ierrors.Wrap(ErrFailToPrepareBatch, err.Error()).Error()) - s.ErrCounter.CountError(ierrors.Wrap(ErrFailToPrepareBatch, err.Error())) - } - s.PrepareAndPostBlock(txData, s.IssuerAlias, clt) - - s.State.batchPrepared.Add(1) - s.EvilWallet.ClearAliases(aliases) - s.CheckIfAllSent() -} - -// func CommitmentsSpammingFunction(s *Spammer) { -// clt := s.Clients.GetClient() -// p := payload.NewGenericDataPayload([]byte("SPAM")) -// payloadBytes, err := p.Bytes() -// if err != nil { -// s.ErrCounter.CountError(ErrFailToPrepareBatch) -// } -// parents, err := clt.GetReferences(payloadBytes, s.CommitmentManager.Params.ParentRefsCount) -// if err != nil { -// s.ErrCounter.CountError(ErrFailGetReferences) -// } -// localID := s.IdentityManager.GetIdentity() -// commitment, latestConfIndex, err := s.CommitmentManager.GenerateCommitment(clt) -// if err != nil { -// s.log.Debugf(errors.WithMessage(ErrFailToPrepareBatch, err.Error()).Error()) -// s.ErrCounter.CountError(errors.WithMessage(ErrFailToPrepareBatch, err.Error())) -// } -// block := models.NewBlock( -// models.WithParents(parents), -// models.WithIssuer(localID.PublicKey()), -// models.WithIssuingTime(time.Now()), -// models.WithPayload(p), -// models.WithLatestConfirmedSlot(latestConfIndex), -// models.WithCommitment(commitment), -// models.WithSignature(ed25519.EmptySignature), -// ) -// signature, err := wallet.SignBlock(block, localID) -// if err != nil { -// return -// } -// block.SetSignature(signature) -// timeProvider := slot.NewTimeProvider(s.CommitmentManager.Params.GenesisTime.Unix(), int64(s.CommitmentManager.Params.SlotDuration.Seconds())) -// if err = block.DetermineID(timeProvider); err != nil { -// s.ErrCounter.CountError(ErrFailPrepareBlock) -// } -// blockBytes, err := block.Bytes() -// if err != nil { -// s.ErrCounter.CountError(ErrFailPrepareBlock) -// } - -// blkID, err := clt.PostBlock(blockBytes) -// if err != nil { -// fmt.Println(err) -// s.ErrCounter.CountError(ErrFailSendDataBlock) -// } - -// count := s.State.txSent.Add(1) -// if count%int64(s.SpamDetails.Rate*4) == 0 { -// s.log.Debugf("Last sent block, ID: %s; %s blkCount: %d", blkID, commitment.ID().String(), count) -// } -// s.State.batchPrepared.Add(1) -// s.CheckIfAllSent() -// } diff --git a/tools/evil-spammer/spammer/utils.go b/tools/evil-spammer/spammer/utils.go deleted file mode 100644 index 997945723..000000000 --- a/tools/evil-spammer/spammer/utils.go +++ /dev/null @@ -1,16 +0,0 @@ -package spammer - -import ( - "time" - - "github.com/iotaledger/iota-core/tools/evil-spammer/evilwallet" -) - -// BigWalletsNeeded calculates how many big wallets needs to be prepared for a spam based on provided spam details. -func BigWalletsNeeded(rate int, timeUnit, duration time.Duration) int { - bigWalletSize := evilwallet.FaucetRequestSplitNumber * evilwallet.FaucetRequestSplitNumber - outputsNeeded := rate * int(duration/timeUnit) - walletsNeeded := outputsNeeded/bigWalletSize + 1 - - return walletsNeeded -}