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/Dockerfile b/Dockerfile
index 44a17fda1..ec10c0f75 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -35,6 +35,8 @@ RUN cp ./peering.json /app/peering.json
# using distroless cc "nonroot" image, which includes everything in the base image (glibc, libssl and openssl)
FROM gcr.io/distroless/cc-debian12:nonroot
+HEALTHCHECK --interval=10s --timeout=5s --retries=30 CMD ["/app/iota-core", "tools", "node-info"]
+
# Copy the app dir into distroless image
COPY --chown=nonroot:nonroot --from=build /app /app
diff --git a/Dockerfile.dev b/Dockerfile.dev
index e9073b384..f91006d26 100644
--- a/Dockerfile.dev
+++ b/Dockerfile.dev
@@ -61,6 +61,8 @@ RUN mkdir -p /app/data/peerdb
# using distroless cc "nonroot" image, which includes everything in the base image (glibc, libssl and openssl)
FROM gcr.io/distroless/cc-debian12:nonroot
+HEALTHCHECK --interval=10s --timeout=5s --retries=30 CMD ["/app/iota-core", "tools", "node-info"]
+
# Copy the app dir into distroless image
COPY --chown=nonroot:nonroot --from=build /app /app
diff --git a/components/app/app.go b/components/app/app.go
index 4cb029500..5c4b42957 100644
--- a/components/app/app.go
+++ b/components/app/app.go
@@ -1,6 +1,9 @@
package app
import (
+ "fmt"
+ "os"
+
"github.com/iotaledger/hive.go/app"
"github.com/iotaledger/hive.go/app/components/profiling"
"github.com/iotaledger/hive.go/app/components/shutdown"
@@ -15,6 +18,7 @@ import (
"github.com/iotaledger/iota-core/components/restapi"
coreapi "github.com/iotaledger/iota-core/components/restapi/core"
"github.com/iotaledger/iota-core/components/validator"
+ "github.com/iotaledger/iota-core/pkg/toolset"
)
var (
@@ -28,6 +32,12 @@ var (
func App() *app.App {
return app.New(Name, Version,
// app.WithVersionCheck("iotaledger", "iota-core"),
+ app.WithUsageText(fmt.Sprintf(`Usage of %s (%s %s):
+
+Run '%s tools' to list all available tools.
+
+Command line flags:
+`, os.Args[0], Name, Version, os.Args[0])),
app.WithInitComponent(InitComponent),
app.WithComponents(
shutdown.Component,
@@ -63,5 +73,15 @@ func init() {
AdditionalConfigs: []*app.ConfigurationSet{
app.NewConfigurationSet("peering", "peering", "peeringConfigFilePath", "peeringConfig", false, true, false, "peering.json", "n"),
},
+ Init: initialize,
+ }
+}
+
+func initialize(_ *app.App) error {
+ if toolset.ShouldHandleTools() {
+ toolset.HandleTools()
+ // HandleTools will call os.Exit
}
+
+ return nil
}
diff --git a/components/debugapi/node.go b/components/debugapi/node.go
index 893fb7c4e..280311b76 100644
--- a/components/debugapi/node.go
+++ b/components/debugapi/node.go
@@ -1,6 +1,7 @@
package debugapi
import (
+ "github.com/iotaledger/hive.go/ierrors"
"github.com/iotaledger/hive.go/lo"
"github.com/iotaledger/iota-core/pkg/core/account"
iotago "github.com/iotaledger/iota.go/v4"
@@ -10,8 +11,12 @@ import (
func validatorsSummary() (*ValidatorsSummaryResponse, error) {
seatManager := deps.Protocol.MainEngineInstance().SybilProtection.SeatManager()
latestSlotIndex := deps.Protocol.MainEngineInstance().Storage.Settings().LatestCommitment().Slot()
- latestCommittee := seatManager.Committee(latestSlotIndex)
- validatorSeats := []*Validator{}
+ latestCommittee, exists := seatManager.CommitteeInSlot(latestSlotIndex)
+ if !exists {
+ return nil, ierrors.Errorf("committee for slot %d was not selected", latestSlotIndex)
+ }
+
+ var validatorSeats []*Validator
latestCommittee.Accounts().ForEach(func(id iotago.AccountID, pool *account.Pool) bool {
validatorSeats = append(validatorSeats, &Validator{
AccountID: id,
diff --git a/components/metricstracker/params.go b/components/metricstracker/params.go
index a2886bd3a..9bd764443 100644
--- a/components/metricstracker/params.go
+++ b/components/metricstracker/params.go
@@ -14,6 +14,6 @@ var ParamsMetricsTracker = &ParametersMetricsTracker{}
var params = &app.ComponentParams{
Params: map[string]any{
- "metricstracker": ParamsMetricsTracker,
+ "metricsTracker": ParamsMetricsTracker,
},
}
diff --git a/components/p2p/component.go b/components/p2p/component.go
index 9635df585..dff432bb7 100644
--- a/components/p2p/component.go
+++ b/components/p2p/component.go
@@ -230,7 +230,7 @@ func provide(c *dig.Container) error {
if err := c.Provide(func(deps p2pDeps) p2pResult {
res := p2pResult{}
- privKeyFilePath := filepath.Join(deps.P2PDatabasePath, "identity.key")
+ privKeyFilePath := filepath.Join(deps.P2PDatabasePath, IdentityPrivateKeyFileName)
// make sure nobody copies around the peer store since it contains the private key of the node
Component.LogInfof(`WARNING: never share your "%s" folder as it contains your node's private key!`, deps.P2PDatabasePath)
diff --git a/components/p2p/params.go b/components/p2p/params.go
index 37f099d85..7b2cba4e7 100644
--- a/components/p2p/params.go
+++ b/components/p2p/params.go
@@ -6,7 +6,8 @@ import (
const (
// CfgPeers defines the static peers this node should retain a connection to (CLI).
- CfgPeers = "peers"
+ CfgPeers = "peers"
+ IdentityPrivateKeyFileName = "identity.key"
)
// ParametersP2P contains the definition of configuration parameters used by the p2p plugin.
diff --git a/components/protocol/component.go b/components/protocol/component.go
index 4bbd8b6e3..dd70e21cb 100644
--- a/components/protocol/component.go
+++ b/components/protocol/component.go
@@ -177,139 +177,139 @@ func provide(c *dig.Container) error {
func configure() error {
deps.Protocol.Events.Error.Hook(func(err error) {
- Component.LogErrorf("Error in Protocol: %s", err)
+ Component.LogErrorf("ProtocolError, error: %s", err)
})
deps.Protocol.Events.Network.Error.Hook(func(err error, id peer.ID) {
- Component.LogErrorf("NetworkError: %s Source: %s", err.Error(), id)
+ Component.LogErrorf("NetworkError, error: %s, peerID: %s", err.Error(), id)
})
deps.Protocol.Events.Network.BlockReceived.Hook(func(block *model.Block, source peer.ID) {
- Component.LogDebugf("BlockReceived: %s", block.ID())
+ Component.LogDebugf("BlockReceived, blockID: %s, peerID: %s", block.ID(), source)
})
deps.Protocol.Events.Engine.BlockProcessed.Hook(func(blockID iotago.BlockID) {
- Component.LogDebugf("BlockProcessed: %s", blockID)
+ Component.LogDebugf("BlockProcessed, blockID: %s", blockID)
})
deps.Protocol.Events.Engine.AcceptedBlockProcessed.Hook(func(block *blocks.Block) {
- Component.LogDebugf("AcceptedBlockProcessed: %s", block.ID())
+ Component.LogDebugf("AcceptedBlockProcessed, blockID: %s", block.ID())
})
deps.Protocol.Events.Engine.Filter.BlockPreFiltered.Hook(func(event *filter.BlockPreFilteredEvent) {
- Component.LogDebugf("BlockPreFiltered: %s - %s", event.Block.ID(), event.Reason.Error())
+ Component.LogDebugf("BlockPreFiltered, blockID: %s, reason: %s", event.Block.ID(), event.Reason.Error())
})
- deps.Protocol.Events.Engine.Filter.BlockPreAllowed.Hook(func(blk *model.Block) {
- Component.LogDebugf("BlockPreAllowed: %s - %s", blk.ID())
+ deps.Protocol.Events.Engine.Filter.BlockPreAllowed.Hook(func(block *model.Block) {
+ Component.LogDebugf("BlockPreAllowed, blockID: %s", block.ID())
})
deps.Protocol.Events.Engine.CommitmentFilter.BlockAllowed.Hook(func(block *blocks.Block) {
- Component.LogDebugf("CommitmentFilter.BlockAllowed: %s\n", block.ID())
+ Component.LogDebugf("CommitmentFilter.BlockAllowed, blockID: %s", block.ID())
})
deps.Protocol.Events.Engine.CommitmentFilter.BlockFiltered.Hook(func(event *commitmentfilter.BlockFilteredEvent) {
- Component.LogWarnf("CommitmentFilter.BlockFiltered: %s - %s\n", event.Block.ID(), event.Reason.Error())
+ Component.LogWarnf("CommitmentFilter.BlockFiltered, blockID: %s, reason: %s", event.Block.ID(), event.Reason.Error())
})
deps.Protocol.Events.Engine.TipManager.BlockAdded.Hook(func(tip tipmanager.TipMetadata) {
- Component.LogDebugf("BlockAdded to tip pool: %s; is strong: %v; is weak: %v", tip.ID(), tip.IsStrongTip(), tip.IsWeakTip())
+ Component.LogDebugf("TipManager.BlockAdded, blockID: %s, isStrong: %v, isWeak: %v", tip.ID(), tip.IsStrongTip(), tip.IsWeakTip())
})
deps.Protocol.Events.Engine.BlockDAG.BlockSolid.Hook(func(block *blocks.Block) {
- Component.LogDebugf("BlockSolid: %s", block.ID())
+ Component.LogDebugf("BlockDAG.BlockSolid, blockID: %s", block.ID())
})
deps.Protocol.Events.Engine.BlockDAG.BlockInvalid.Hook(func(block *blocks.Block, err error) {
- Component.LogDebugf("BlockInvalid in blockDAG: %s, error: %v", block.ID(), err.Error())
+ Component.LogDebugf("BlockDAG.BlockInvalid, blockID: %s, error: %v", block.ID(), err.Error())
})
deps.Protocol.Events.Engine.Booker.BlockBooked.Hook(func(block *blocks.Block) {
- Component.LogDebugf("BlockBooked: %s", block.ID())
+ Component.LogDebugf("BlockBooked, blockID: %s", block.ID())
})
deps.Protocol.Events.Engine.Booker.BlockInvalid.Hook(func(block *blocks.Block, err error) {
- Component.LogDebugf("BlockInvalid in booker: %s, error: %v", block.ID(), err.Error())
+ Component.LogDebugf("BlockInvalid in booker, blockID: %s, error: %v", block.ID(), err.Error())
})
deps.Protocol.Events.Engine.BlockGadget.BlockPreAccepted.Hook(func(block *blocks.Block) {
- Component.LogDebugf("BlockPreAccepted: %s", block.ID())
+ Component.LogDebugf("BlockPreAccepted, blockID: %s", block.ID())
})
deps.Protocol.Events.Engine.BlockGadget.BlockAccepted.Hook(func(block *blocks.Block) {
- Component.LogDebugf("BlockAccepted: %s", block.ID())
+ Component.LogDebugf("BlockAccepted, blockID: %s", block.ID())
})
deps.Protocol.Events.Engine.BlockGadget.BlockPreConfirmed.Hook(func(block *blocks.Block) {
- Component.LogDebugf("BlockPreConfirmed: %s", block.ID())
+ Component.LogDebugf("BlockPreConfirmed, blockID: %s", block.ID())
})
deps.Protocol.Events.Engine.BlockGadget.BlockConfirmed.Hook(func(block *blocks.Block) {
- Component.LogDebugf("BlockConfirmed: %s", block.ID())
+ Component.LogDebugf("BlockConfirmed, blockID: %s", block.ID())
})
deps.Protocol.Events.Engine.Clock.AcceptedTimeUpdated.Hook(func(time time.Time) {
- Component.LogDebugf("AcceptedTimeUpdated: Slot %d @ %s", deps.Protocol.CommittedAPI().TimeProvider().SlotFromTime(time), time)
+ Component.LogDebugf("AcceptedTimeUpdated, slot: %d @ %s", deps.Protocol.CommittedAPI().TimeProvider().SlotFromTime(time), time)
})
deps.Protocol.Events.Engine.Clock.ConfirmedTimeUpdated.Hook(func(time time.Time) {
- Component.LogDebugf("ConfirmedTimeUpdated: Slot %d @ %s", deps.Protocol.CommittedAPI().TimeProvider().SlotFromTime(time), time)
+ Component.LogDebugf("ConfirmedTimeUpdated, slot: %d @ %s", deps.Protocol.CommittedAPI().TimeProvider().SlotFromTime(time), time)
})
deps.Protocol.Events.Engine.Notarization.SlotCommitted.Hook(func(details *notarization.SlotCommittedDetails) {
- Component.LogInfof("SlotCommitted: %s - %d", details.Commitment.ID(), details.Commitment.Slot())
+ Component.LogInfof("SlotCommitted, commitmentID: %s, slot: %d", details.Commitment.ID(), details.Commitment.Slot())
})
- deps.Protocol.Events.Engine.SlotGadget.SlotFinalized.Hook(func(index iotago.SlotIndex) {
- Component.LogInfof("SlotFinalized: %d", index)
+ deps.Protocol.Events.Engine.SlotGadget.SlotFinalized.Hook(func(slot iotago.SlotIndex) {
+ Component.LogInfof("SlotFinalized, slot: %d", slot)
})
deps.Protocol.Events.Engine.Scheduler.BlockScheduled.Hook(func(block *blocks.Block) {
- Component.LogDebugf("BlockScheduled: %s", block.ID())
+ Component.LogDebugf("BlockScheduled, blockID: %s", block.ID())
})
deps.Protocol.Events.Engine.Scheduler.BlockDropped.Hook(func(block *blocks.Block, err error) {
- Component.LogDebugf("BlockDropped: %s; reason: %s", block.ID(), err)
+ Component.LogDebugf("BlockDropped, blockID: %s, reason: %s", block.ID(), err)
})
deps.Protocol.Events.Engine.Scheduler.BlockSkipped.Hook(func(block *blocks.Block) {
- Component.LogDebugf("BlockSkipped: %s", block.ID())
+ Component.LogDebugf("BlockSkipped, blockID: %s", block.ID())
})
deps.Protocol.Events.ChainManager.RequestCommitment.Hook(func(id iotago.CommitmentID) {
- Component.LogDebugf("RequestCommitment: %s", id)
+ Component.LogDebugf("RequestCommitment, commitmentID: %s", id)
})
deps.Protocol.Events.Network.SlotCommitmentRequestReceived.Hook(func(commitmentID iotago.CommitmentID, id peer.ID) {
- Component.LogDebugf("SlotCommitmentRequestReceived: %s", commitmentID)
+ Component.LogDebugf("SlotCommitmentRequestReceived, commitmentID: %s", commitmentID)
})
deps.Protocol.Events.Network.SlotCommitmentReceived.Hook(func(commitment *model.Commitment, id peer.ID) {
- Component.LogDebugf("SlotCommitmentReceived: %s", commitment.ID())
+ Component.LogDebugf("SlotCommitmentReceived, commitmentID: %s", commitment.ID())
})
deps.Protocol.Events.Engine.SybilProtection.CommitteeSelected.Hook(func(committee *account.Accounts, epoch iotago.EpochIndex) {
- Component.LogInfof("CommitteeSelected: Epoch %d - %s (reused: %t)", epoch, committee.IDs(), committee.IsReused())
+ Component.LogInfof("CommitteeSelected, epoch: %d, committeeIDs: %s, reused: %t", epoch, committee.IDs(), committee.IsReused())
})
deps.Protocol.Events.Engine.SybilProtection.RewardsCommitted.Hook(func(epoch iotago.EpochIndex) {
- Component.LogInfof("RewardsCommitted: Epoch %d", epoch)
+ Component.LogInfof("RewardsCommitted, epoch: %d", epoch)
})
deps.Protocol.Events.Engine.Booker.BlockInvalid.Hook(func(block *blocks.Block, err error) {
- Component.LogWarnf("Booker BlockInvalid: Block %s - %s", block.ID(), err.Error())
+ Component.LogWarnf("Booker BlockInvalid, blockID: %s, error: %s", block.ID(), err.Error())
})
deps.Protocol.Events.Engine.SeatManager.OnlineCommitteeSeatAdded.Hook(func(seatIndex account.SeatIndex, account iotago.AccountID) {
- Component.LogWarnf("OnlineCommitteeSeatAdded: %s - %d", account.ToHex(), seatIndex)
+ Component.LogWarnf("OnlineCommitteeSeatAdded, accountID: %s, seatIndex: %d", account.ToHex(), seatIndex)
})
deps.Protocol.Events.Engine.SeatManager.OnlineCommitteeSeatRemoved.Hook(func(seatIndex account.SeatIndex) {
- Component.LogWarnf("OnlineCommitteeSeatRemoved: seatIndex: %d", seatIndex)
+ Component.LogWarnf("OnlineCommitteeSeatRemoved, seatIndex: %d", seatIndex)
})
deps.Protocol.Events.Engine.Booker.TransactionInvalid.Hook(func(transaction mempool.TransactionMetadata, reason error) {
- Component.LogWarnf("TransactionInvalid: transaction %s - %s", transaction.ID(), reason.Error())
+ Component.LogWarnf("TransactionInvalid, transactionID: %s, error: %s", transaction.ID(), reason.Error())
})
return nil
diff --git a/components/restapi/core/accounts.go b/components/restapi/core/accounts.go
index ed6f62648..91c6211c3 100644
--- a/components/restapi/core/accounts.go
+++ b/components/restapi/core/accounts.go
@@ -26,10 +26,10 @@ func congestionForAccountID(c echo.Context) (*apimodels.CongestionResponse, erro
acc, exists, err := deps.Protocol.MainEngineInstance().Ledger.Account(accountID, commitment.Slot())
if err != nil {
- return nil, ierrors.Wrapf(err, "failed to get account: %s form the Ledger", accountID.ToHex())
+ return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to get account %s from the Ledger: %s", accountID.ToHex(), err)
}
if !exists {
- return nil, ierrors.Errorf("account not found: %s", accountID.ToHex())
+ return nil, ierrors.Wrapf(echo.ErrNotFound, "account not found: %s", accountID.ToHex())
}
return &apimodels.CongestionResponse{
@@ -46,7 +46,7 @@ func validators(c echo.Context) (*apimodels.ValidatorsResponse, error) {
if len(c.QueryParam(restapipkg.QueryParameterPageSize)) > 0 {
pageSize, err = httpserver.ParseUint32QueryParam(c, restapipkg.QueryParameterPageSize)
if err != nil {
- return nil, ierrors.Wrapf(err, "failed to parse the %s parameter", restapipkg.QueryParameterPageSize)
+ return nil, ierrors.Wrapf(err, "failed to parse page size %s", c.Param(restapipkg.QueryParameterPageSize))
}
if pageSize > restapi.ParamsRestAPI.MaxPageSize {
pageSize = restapi.ParamsRestAPI.MaxPageSize
@@ -59,13 +59,13 @@ func validators(c echo.Context) (*apimodels.ValidatorsResponse, error) {
if len(c.QueryParam(restapipkg.QueryParameterCursor)) != 0 {
requestedSlot, cursorIndex, err = httpserver.ParseCursorQueryParam(c, restapipkg.QueryParameterCursor)
if err != nil {
- return nil, ierrors.Wrapf(err, "failed to parse the %s parameter", restapipkg.QueryParameterCursor)
+ return nil, ierrors.Wrapf(err, "failed to parse cursor %s", c.Param(restapipkg.QueryParameterCursor))
}
}
// do not respond to really old requests
if requestedSlot+iotago.SlotIndex(restapi.ParamsRestAPI.MaxRequestedSlotAge) < latestCommittedSlot {
- return nil, ierrors.Errorf("request is too old, request started at %d, latest committed slot index is %d", requestedSlot, latestCommittedSlot)
+ return nil, ierrors.Wrapf(echo.ErrBadRequest, "request is too old, request started at %d, latest committed slot index is %d", requestedSlot, latestCommittedSlot)
}
nextEpoch := deps.Protocol.APIForSlot(latestCommittedSlot).TimeProvider().EpochFromSlot(latestCommittedSlot) + 1
@@ -75,7 +75,7 @@ func validators(c echo.Context) (*apimodels.ValidatorsResponse, error) {
if !exists {
registeredValidators, err = deps.Protocol.MainEngineInstance().SybilProtection.OrderedRegisteredCandidateValidatorsList(nextEpoch)
if err != nil {
- return nil, ierrors.Wrapf(err, "failed to get ordered registered validators list for epoch %d", nextEpoch)
+ return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to get ordered registered validators list for epoch %d : %s", nextEpoch, err)
}
deps.Protocol.MainEngineInstance().Retainer.RetainRegisteredValidatorsCache(slotRange, registeredValidators)
}
@@ -98,19 +98,23 @@ func validators(c echo.Context) (*apimodels.ValidatorsResponse, error) {
func validatorByAccountID(c echo.Context) (*apimodels.ValidatorResponse, error) {
accountID, err := httpserver.ParseAccountIDParam(c, restapipkg.ParameterAccountID)
if err != nil {
- return nil, ierrors.Wrapf(err, "failed to parse the %s parameter", restapipkg.ParameterAccountID)
+ return nil, ierrors.Wrapf(err, "failed to parse account ID %s", c.Param(restapipkg.ParameterAccountID))
}
latestCommittedSlot := deps.Protocol.MainEngineInstance().SyncManager.LatestCommitment().Slot()
accountData, exists, err := deps.Protocol.MainEngineInstance().Ledger.Account(accountID, latestCommittedSlot)
if err != nil {
- return nil, ierrors.Wrapf(err, "failed to get account: %s form the Ledger", accountID.ToHex())
+ return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to get account %s from the Ledger: %s", accountID.ToHex(), err)
}
if !exists {
- return nil, ierrors.Errorf("account not found: %s for latest committedSlot %d", accountID.ToHex(), latestCommittedSlot)
+ return nil, ierrors.Wrapf(echo.ErrNotFound, "account %s not found for latest committedSlot %d", accountID.ToHex(), latestCommittedSlot)
}
nextEpoch := deps.Protocol.APIForSlot(latestCommittedSlot).TimeProvider().EpochFromSlot(latestCommittedSlot) + 1
- active := deps.Protocol.MainEngineInstance().SybilProtection.IsCandidateActive(accountID, nextEpoch)
+
+ active, err := deps.Protocol.MainEngineInstance().SybilProtection.IsCandidateActive(accountID, nextEpoch)
+ if err != nil {
+ return nil, ierrors.Wrapf(err, "failed to check if account %s is an active candidate", accountID.ToHex())
+ }
return &apimodels.ValidatorResponse{
AccountID: accountID,
@@ -127,12 +131,12 @@ func validatorByAccountID(c echo.Context) (*apimodels.ValidatorResponse, error)
func rewardsByOutputID(c echo.Context) (*apimodels.ManaRewardsResponse, error) {
outputID, err := httpserver.ParseOutputIDParam(c, restapipkg.ParameterOutputID)
if err != nil {
- return nil, ierrors.Wrapf(err, "failed to parse the %s parameter", restapipkg.ParameterOutputID)
+ return nil, ierrors.Wrapf(err, "failed to parse output ID %s", c.Param(restapipkg.ParameterOutputID))
}
utxoOutput, err := deps.Protocol.MainEngineInstance().Ledger.Output(outputID)
if err != nil {
- return nil, ierrors.Wrapf(err, "failed to get output %s from ledger", outputID.ToHex())
+ return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to get output %s from ledger: %s", outputID.ToHex(), err)
}
var reward iotago.Mana
@@ -143,7 +147,7 @@ func rewardsByOutputID(c echo.Context) (*apimodels.ManaRewardsResponse, error) {
accountOutput := utxoOutput.Output().(*iotago.AccountOutput)
feature, exists := accountOutput.FeatureSet()[iotago.FeatureStaking]
if !exists {
- return nil, ierrors.Errorf("account %s is not a validator", outputID)
+ return nil, ierrors.Wrapf(echo.ErrBadRequest, "account %s is not a validator", outputID.ToHex())
}
//nolint:forcetypeassert
@@ -174,7 +178,7 @@ func rewardsByOutputID(c echo.Context) (*apimodels.ManaRewardsResponse, error) {
)
}
if err != nil {
- return nil, ierrors.Wrapf(err, "failed to calculate reward for output %s", outputID)
+ return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to calculate reward for output %s: %s", outputID.ToHex(), err)
}
return &apimodels.ManaRewardsResponse{
@@ -198,7 +202,13 @@ func selectedCommittee(c echo.Context) *apimodels.CommitteeResponse {
slot = timeProvider.EpochEnd(epoch)
}
- seatedAccounts := deps.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(slot)
+ seatedAccounts, exists := deps.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(slot)
+ if !exists {
+ return &apimodels.CommitteeResponse{
+ Epoch: epoch,
+ }
+ }
+
committee := make([]*apimodels.CommitteeMemberResponse, 0, seatedAccounts.Accounts().Size())
seatedAccounts.Accounts().ForEach(func(accountID iotago.AccountID, seat *account.Pool) bool {
committee = append(committee, &apimodels.CommitteeMemberResponse{
diff --git a/components/restapi/core/blocks.go b/components/restapi/core/blocks.go
index 03c3598d6..5db11730f 100644
--- a/components/restapi/core/blocks.go
+++ b/components/restapi/core/blocks.go
@@ -15,12 +15,12 @@ import (
func blockByID(c echo.Context) (*model.Block, error) {
blockID, err := httpserver.ParseBlockIDParam(c, restapi.ParameterBlockID)
if err != nil {
- return nil, ierrors.Wrapf(err, "failed to parse block ID: %s", c.Param(restapi.ParameterBlockID))
+ return nil, ierrors.Wrapf(err, "failed to parse block ID %s", c.Param(restapi.ParameterBlockID))
}
block, exists := deps.Protocol.MainEngineInstance().Block(blockID)
if !exists {
- return nil, ierrors.Errorf("block not found: %s", blockID.ToHex())
+ return nil, ierrors.Wrapf(echo.ErrNotFound, "block not found: %s", blockID.ToHex())
}
return block, nil
@@ -29,7 +29,7 @@ func blockByID(c echo.Context) (*model.Block, error) {
func blockMetadataByBlockID(blockID iotago.BlockID) (*apimodels.BlockMetadataResponse, error) {
blockMetadata, err := deps.Protocol.MainEngineInstance().Retainer.BlockMetadata(blockID)
if err != nil {
- return nil, ierrors.Wrapf(err, "failed to get block metadata: %s", blockID.ToHex())
+ return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to get block metadata %s: %s", blockID.ToHex(), err)
}
return blockMetadata.BlockMetadataResponse(), nil
@@ -38,7 +38,7 @@ func blockMetadataByBlockID(blockID iotago.BlockID) (*apimodels.BlockMetadataRes
func blockMetadataByID(c echo.Context) (*apimodels.BlockMetadataResponse, error) {
blockID, err := httpserver.ParseBlockIDParam(c, restapi.ParameterBlockID)
if err != nil {
- return nil, ierrors.Wrapf(err, "failed to parse block ID: %s", c.Param(restapi.ParameterBlockID))
+ return nil, ierrors.Wrapf(err, "failed to parse block ID %s", c.Param(restapi.ParameterBlockID))
}
return blockMetadataByBlockID(blockID)
@@ -55,7 +55,7 @@ func blockIssuanceBySlot(slotIndex iotago.SlotIndex) (*apimodels.IssuanceBlockHe
} else {
slotCommitment, err = deps.Protocol.MainEngineInstance().Storage.Commitments().Load(slotIndex)
if err != nil {
- return nil, ierrors.Wrapf(err, "failed to load commitment for requested slot %d", slotIndex)
+ return nil, ierrors.Wrapf(echo.ErrNotFound, "failed to load commitment for requested slot %d: %s", slotIndex, err)
}
}
@@ -77,7 +77,7 @@ func blockIssuanceBySlot(slotIndex iotago.SlotIndex) (*apimodels.IssuanceBlockHe
func sendBlock(c echo.Context) (*apimodels.BlockCreatedResponse, error) {
iotaBlock, err := httpserver.ParseRequestByHeader(c, deps.Protocol.CommittedAPI(), iotago.ProtocolBlockFromBytes(deps.Protocol))
if err != nil {
- return nil, err
+ return nil, ierrors.Wrapf(err, "failed to parse iotablock")
}
blockID, err := deps.BlockHandler.AttachBlock(c.Request().Context(), iotaBlock)
diff --git a/components/restapi/core/commitment.go b/components/restapi/core/commitment.go
index 99059825c..4ff0f581d 100644
--- a/components/restapi/core/commitment.go
+++ b/components/restapi/core/commitment.go
@@ -13,7 +13,7 @@ import (
func indexByCommitmentID(c echo.Context) (iotago.SlotIndex, error) {
commitmentID, err := httpserver.ParseCommitmentIDParam(c, restapipkg.ParameterCommitmentID)
if err != nil {
- return iotago.SlotIndex(0), ierrors.Wrapf(err, "failed to parse commitment ID: %s", c.Param(restapipkg.ParameterCommitmentID))
+ return iotago.SlotIndex(0), ierrors.Wrapf(err, "failed to parse commitment ID %s", c.Param(restapipkg.ParameterCommitmentID))
}
return commitmentID.Slot(), nil
@@ -22,7 +22,7 @@ func indexByCommitmentID(c echo.Context) (iotago.SlotIndex, error) {
func getCommitmentDetails(index iotago.SlotIndex) (*iotago.Commitment, error) {
commitment, err := deps.Protocol.MainEngineInstance().Storage.Commitments().Load(index)
if err != nil {
- return nil, ierrors.Wrapf(err, "failed to load commitment: %d", index)
+ return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to load commitment %d: %s", index, err)
}
return commitment.Commitment(), nil
@@ -31,7 +31,7 @@ func getCommitmentDetails(index iotago.SlotIndex) (*iotago.Commitment, error) {
func getUTXOChanges(slot iotago.SlotIndex) (*apimodels.UTXOChangesResponse, error) {
diffs, err := deps.Protocol.MainEngineInstance().Ledger.SlotDiffs(slot)
if err != nil {
- return nil, ierrors.Wrapf(err, "failed to get slot diffs: %d", slot)
+ return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to get slot diffs %d: %s", slot, err)
}
createdOutputs := make(iotago.OutputIDs, len(diffs.Outputs))
diff --git a/components/restapi/core/transaction.go b/components/restapi/core/transaction.go
index 44575426a..93f660134 100644
--- a/components/restapi/core/transaction.go
+++ b/components/restapi/core/transaction.go
@@ -14,7 +14,7 @@ import (
func blockIDByTransactionID(c echo.Context) (iotago.BlockID, error) {
txID, err := httpserver.ParseTransactionIDParam(c, restapipkg.ParameterTransactionID)
if err != nil {
- return iotago.EmptyBlockID, ierrors.Wrapf(err, "failed to parse transaction ID: %s", c.Param(restapipkg.ParameterTransactionID))
+ return iotago.EmptyBlockID, ierrors.Wrapf(err, "failed to parse transaction ID %s", c.Param(restapipkg.ParameterTransactionID))
}
return blockIDFromTransactionID(txID)
@@ -26,7 +26,7 @@ func blockIDFromTransactionID(transactionID iotago.TransactionID) (iotago.BlockI
output, spent, err := deps.Protocol.MainEngineInstance().Ledger.OutputOrSpent(outputID)
if err != nil {
- return iotago.EmptyBlockID, ierrors.Wrapf(err, "failed to get output: %s", outputID.ToHex())
+ return iotago.EmptyBlockID, ierrors.Wrapf(echo.ErrInternalServerError, "failed to get output %s: %s", outputID.ToHex(), err)
}
if output != nil {
@@ -39,12 +39,12 @@ func blockIDFromTransactionID(transactionID iotago.TransactionID) (iotago.BlockI
func blockByTransactionID(c echo.Context) (*model.Block, error) {
blockID, err := blockIDByTransactionID(c)
if err != nil {
- return nil, ierrors.Wrapf(err, "failed to get block ID by transaction ID")
+ return nil, ierrors.Wrapf(echo.ErrBadRequest, "failed to get block ID by transaction ID: %s", err)
}
block, exists := deps.Protocol.MainEngineInstance().Block(blockID)
if !exists {
- return nil, ierrors.Errorf("block not found: %s", blockID.String())
+ return nil, ierrors.Wrapf(echo.ErrNotFound, "block not found: %s", blockID.ToHex())
}
return block, nil
@@ -53,7 +53,7 @@ func blockByTransactionID(c echo.Context) (*model.Block, error) {
func blockMetadataFromTransactionID(c echo.Context) (*apimodels.BlockMetadataResponse, error) {
blockID, err := blockIDByTransactionID(c)
if err != nil {
- return nil, ierrors.Wrapf(err, "failed to get block ID by transaction ID")
+ return nil, ierrors.Wrapf(echo.ErrBadRequest, "failed to get block ID by transaction ID: %s", err)
}
return blockMetadataByBlockID(blockID)
diff --git a/components/restapi/core/utxo.go b/components/restapi/core/utxo.go
index 196b6c514..bdf62c89c 100644
--- a/components/restapi/core/utxo.go
+++ b/components/restapi/core/utxo.go
@@ -13,12 +13,12 @@ import (
func getOutput(c echo.Context) (*apimodels.OutputResponse, error) {
outputID, err := httpserver.ParseOutputIDParam(c, restapipkg.ParameterOutputID)
if err != nil {
- return nil, ierrors.Wrapf(err, "failed to parse output ID param: %s", c.Param(restapipkg.ParameterOutputID))
+ return nil, ierrors.Wrapf(err, "failed to parse output ID %s", c.Param(restapipkg.ParameterOutputID))
}
output, err := deps.Protocol.MainEngineInstance().Ledger.Output(outputID)
if err != nil {
- return nil, ierrors.Wrapf(err, "failed to get output: %s from the Ledger", outputID.String())
+ return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to get output %s from the Ledger: %s", outputID.ToHex(), err)
}
return &apimodels.OutputResponse{
@@ -30,12 +30,12 @@ func getOutput(c echo.Context) (*apimodels.OutputResponse, error) {
func getOutputMetadata(c echo.Context) (*apimodels.OutputMetadata, error) {
outputID, err := httpserver.ParseOutputIDParam(c, restapipkg.ParameterOutputID)
if err != nil {
- return nil, ierrors.Wrapf(err, "failed to parse output ID param: %s", c.Param(restapipkg.ParameterOutputID))
+ return nil, ierrors.Wrapf(err, "failed to parse output ID %s", c.Param(restapipkg.ParameterOutputID))
}
output, spent, err := deps.Protocol.MainEngineInstance().Ledger.OutputOrSpent(outputID)
if err != nil {
- return nil, ierrors.Wrapf(err, "failed to get output: %s from the Ledger", outputID.String())
+ return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to get output %s from the Ledger: %s", outputID.ToHex(), err)
}
if spent != nil {
@@ -48,18 +48,18 @@ func getOutputMetadata(c echo.Context) (*apimodels.OutputMetadata, error) {
func getOutputWithMetadata(c echo.Context) (*apimodels.OutputWithMetadataResponse, error) {
outputID, err := httpserver.ParseOutputIDParam(c, restapipkg.ParameterOutputID)
if err != nil {
- return nil, ierrors.Wrapf(err, "failed to parse output ID param: %s", c.Param(restapipkg.ParameterOutputID))
+ return nil, ierrors.Wrapf(err, "failed to parse output ID %s", c.Param(restapipkg.ParameterOutputID))
}
output, spent, err := deps.Protocol.MainEngineInstance().Ledger.OutputOrSpent(outputID)
if err != nil {
- return nil, ierrors.Wrapf(err, "failed to get output: %s from the Ledger", outputID.String())
+ return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to get output %s from the Ledger: %s", outputID.ToHex(), err)
}
if spent != nil {
metadata, err := newSpentMetadataResponse(spent)
if err != nil {
- return nil, err
+ return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to load spent output metadata: %s", err)
}
return &apimodels.OutputWithMetadataResponse{
@@ -96,7 +96,7 @@ func newOutputMetadataResponse(output *utxoledger.Output) (*apimodels.OutputMeta
if includedSlotIndex <= latestCommitment.Slot() {
includedCommitment, err := deps.Protocol.MainEngineInstance().Storage.Commitments().Load(includedSlotIndex)
if err != nil {
- return nil, ierrors.Wrapf(err, "failed to load commitment with index: %d", includedSlotIndex)
+ return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to load commitment with index %d: %s", includedSlotIndex, err)
}
resp.IncludedCommitmentID = includedCommitment.ID()
}
@@ -120,7 +120,7 @@ func newSpentMetadataResponse(spent *utxoledger.Spent) (*apimodels.OutputMetadat
if includedSlotIndex <= latestCommitment.Slot() {
includedCommitment, err := deps.Protocol.MainEngineInstance().Storage.Commitments().Load(includedSlotIndex)
if err != nil {
- return nil, ierrors.Wrapf(err, "failed to load commitment with index: %d", includedSlotIndex)
+ return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to load commitment with index %d: %s", includedSlotIndex, err)
}
resp.IncludedCommitmentID = includedCommitment.ID()
}
@@ -129,7 +129,7 @@ func newSpentMetadataResponse(spent *utxoledger.Spent) (*apimodels.OutputMetadat
if spentSlotIndex <= latestCommitment.Slot() {
spentCommitment, err := deps.Protocol.MainEngineInstance().Storage.Commitments().Load(spentSlotIndex)
if err != nil {
- return nil, ierrors.Wrapf(err, "failed to load commitment with index: %d", spentSlotIndex)
+ return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to load commitment with index %d: %s", spentSlotIndex, err)
}
resp.CommitmentIDSpent = spentCommitment.ID()
}
diff --git a/components/restapi/params.go b/components/restapi/params.go
index 440fdc102..121d07ae8 100644
--- a/components/restapi/params.go
+++ b/components/restapi/params.go
@@ -9,7 +9,7 @@ type ParametersRestAPI struct {
// Enabled defines whether the REST API plugin is enabled.
Enabled bool `default:"true" usage:"whether the REST API plugin is enabled"`
// the bind address on which the REST API listens on
- BindAddress string `default:"0.0.0.0:8080" usage:"the bind address on which the REST API listens on"`
+ BindAddress string `default:"0.0.0.0:14265" usage:"the bind address on which the REST API listens on"`
// the HTTP REST routes which can be called without authorization. Wildcards using * are allowed
PublicRoutes []string `usage:"the HTTP REST routes which can be called without authorization. Wildcards using * are allowed"`
// the HTTP REST routes which need to be called with authorization. Wildcards using * are allowed
diff --git a/components/validator/issuer.go b/components/validator/issuer.go
index 7f325777c..8a748b58f 100644
--- a/components/validator/issuer.go
+++ b/components/validator/issuer.go
@@ -81,7 +81,15 @@ func issueValidatorBlock(ctx context.Context) {
return
}
- if !engineInstance.SybilProtection.SeatManager().Committee(deps.Protocol.CommittedAPI().TimeProvider().SlotFromTime(blockIssuingTime)).HasAccount(validatorAccount.ID()) {
+ blockSlot := deps.Protocol.CommittedAPI().TimeProvider().SlotFromTime(blockIssuingTime)
+ committee, exists := engineInstance.SybilProtection.SeatManager().CommitteeInSlot(blockSlot)
+ if !exists {
+ Component.LogWarnf("committee for slot %d not selected: %s", blockSlot, err.Error())
+
+ return
+ }
+
+ if !committee.HasAccount(validatorAccount.ID()) {
// update nextBroadcast value here, so that this updated value is used in the `defer`
// callback to schedule issuing of the next block at a different interval than for committee members
nextBroadcast = blockIssuingTime.Add(ParamsValidator.CandidateBroadcastInterval)
diff --git a/config_defaults.json b/config_defaults.json
index 2dc55de16..f24471e14 100644
--- a/config_defaults.json
+++ b/config_defaults.json
@@ -44,7 +44,7 @@
},
"restAPI": {
"enabled": true,
- "bindAddress": "0.0.0.0:8080",
+ "bindAddress": "0.0.0.0:14265",
"publicRoutes": [
"/health",
"/api/routes",
@@ -83,7 +83,7 @@
"pruningThreshold": 1,
"dbGranularity": 100
},
- "metricstracker": {
+ "metricsTracker": {
"enabled": true
},
"database": {
diff --git a/deploy/ansible/hosts/feature.yml b/deploy/ansible/hosts/feature.yml
index 52405d60a..ac8e44cfc 100644
--- a/deploy/ansible/hosts/feature.yml
+++ b/deploy/ansible/hosts/feature.yml
@@ -8,22 +8,20 @@ cores:
hosts:
node-01.feature.shimmer.iota.cafe:
validatorAccount: "{{ NODE_01_ACCOUNTID }}"
- validatorPrivKey: "{{ NODE_01_VALIDATOR_PRIVKEY }}"
- p2pIdentityPrivateKey: "{{ NODE_01_P2PIDENTITYPRIVATEKEY }}"
+ validatorPrvKey: "{{ NODE_01_VALIDATOR_PRIVKEY }}"
+ p2pIdentityPrvKey: "{{ NODE_01_P2PIDENTITYPRIVATEKEY }}"
node-02.feature.shimmer.iota.cafe:
validatorAccount: "{{ NODE_02_ACCOUNTID }}"
- validatorPrivKey: "{{ NODE_02_VALIDATOR_PRIVKEY }}"
- p2pIdentityPrivateKey: "{{ NODE_02_P2PIDENTITYPRIVATEKEY }}"
+ validatorPrvKey: "{{ NODE_02_VALIDATOR_PRIVKEY }}"
+ p2pIdentityPrvKey: "{{ NODE_02_P2PIDENTITYPRIVATEKEY }}"
node-03.feature.shimmer.iota.cafe:
validatorAccount: "{{ NODE_03_ACCOUNTID }}"
- validatorPrivKey: "{{ NODE_03_VALIDATOR_PRIVKEY }}"
- p2pIdentityPrivateKey: "{{ NODE_03_P2PIDENTITYPRIVATEKEY }}"
+ validatorPrvKey: "{{ NODE_03_VALIDATOR_PRIVKEY }}"
+ p2pIdentityPrvKey: "{{ NODE_03_P2PIDENTITYPRIVATEKEY }}"
node-04.feature.shimmer.iota.cafe:
- validatorAccount: "{{ NODE_04_ACCOUNTID }}"
- validatorPrivKey: "{{ NODE_04_VALIDATOR_PRIVKEY }}"
- p2pIdentityPrivateKey: "{{ NODE_04_P2PIDENTITYPRIVATEKEY }}"
+ p2pIdentityPrvKey: "{{ NODE_04_P2PIDENTITYPRIVATEKEY }}"
+ blockissuerPrvKey: "{{ NODE_04_BLOCKISSUER_PRV_KEY }}"
+ faucetPrvKey: "{{ NODE_04_FAUCET_PRV_KEY }}"
node-05.feature.shimmer.iota.cafe:
- validatorAccount: "{{ NODE_05_ACCOUNTID }}"
- validatorPrivKey: "{{ NODE_05_VALIDATOR_PRIVKEY }}"
- p2pIdentityPrivateKey: "{{ NODE_05_P2PIDENTITYPRIVATEKEY }}"
+ p2pIdentityPrvKey: "{{ NODE_05_P2PIDENTITYPRIVATEKEY }}"
vars:
diff --git a/deploy/ansible/roles/iota-core-node/templates/docker-compose-iota-core.yml.j2 b/deploy/ansible/roles/iota-core-node/templates/docker-compose-iota-core.yml.j2
index e86b3be51..d3bc9d82b 100644
--- a/deploy/ansible/roles/iota-core-node/templates/docker-compose-iota-core.yml.j2
+++ b/deploy/ansible/roles/iota-core-node/templates/docker-compose-iota-core.yml.j2
@@ -9,58 +9,120 @@
version: '3.3'
services:
+
+###################
+# IOTA-CORE Nodes #
+###################
+
iota_core:
image: {{iota_core_docker_image_repo}}:{{iota_core_docker_image_tag}}
container_name: iota-core
+ stop_grace_period: 1m
+ restart: unless-stopped
+ ulimits:
+ nofile:
+ soft: 16384
+ hard: 16384
+ ports:
+ - "14666:14666/tcp" # P2P
+ - "6061:6061/tcp" # pprof
+ - "8080:14265/tcp" # REST-API
+ - "8081:8081/tcp" # Dashboard
+ - "9311:9311/tcp" # Prometheus
+ - "9029:9029/tcp" # INX
volumes:
- - ./snapshot.bin:/app/data/snapshot.bin:ro
- ./config.json:/app/config.json:ro
- ./data:/app/data/
+ - ./snapshot.bin:/app/data/snapshot.bin:ro
- /etc/localtime:/etc/localtime:ro
- ports:
- - "0.0.0.0:14666:14666/tcp"
- - "0.0.0.0:8080:8080/tcp"
- - "0.0.0.0:8081:8081/tcp"
- - "0.0.0.0:6061:6061/tcp"
- # prometheus
- - "0.0.0.0:9311:9311/tcp"
- environment:
- - WEBAPI_BINDADDRESS=0.0.0.0:8080
- - DASHBOARD_BINDADDRESS=0.0.0.0:8081
- - PROFILING_BINDADDRESS=0.0.0.0:6061
command: >
- -c config.json
+ -c
+ config.json
--logger.level=debug
--logger.disableCaller=false
- --logger.disableStacktrace=false
- --logger.encoding=console
- --logger.outputPaths=stdout
- --database.path=/app/data/database
+ --p2p.peers=/dns/node-01.feature/tcp/14666/p2p/12D3KooWCrjmh4dUCWfGVQT6ivzArieJB9Z3eKdy2mdEEN95NDPS
+ --p2p.externalMultiAddresses={{ ips | join(',') }}
+ --p2p.identityPrivateKey={{p2pIdentityPrvKey}}
--p2p.db.path=/app/data/peerdb
- --profiling.bindAddress=0.0.0.0:6061
--profiling.enabled=true
+ --profiling.bindAddress=0.0.0.0:6061
+ --restAPI.bindAddress=0.0.0.0:14265
+ --database.path=/app/data/database
--protocol.snapshot.path=/app/data/snapshot.bin
{% if 'node-01' in inventory_hostname or 'node-02' in inventory_hostname or 'node-03' in inventory_hostname %}
--validator.enabled=true
- --validator.account={{validatorAccount}}
- --validator.privateKey={{validatorPrivKey}}
- {% endif %}
- {% if 'node-01' in inventory_hostname %}
+ {% if 'node-01' in inventory_hostname %}
--validator.ignoreBootstrapped=true
+ {% endif %}
+ --validator.account={{validatorAccount}}
+ --validator.privateKey={{validatorPrvKey}}
{% endif %}
- --p2p.peers=/dns/node-01.feature/tcp/14666/p2p/12D3KooWCrjmh4dUCWfGVQT6ivzArieJB9Z3eKdy2mdEEN95NDPS
- --p2p.externalMultiAddresses={{ ips | join(',') }}
- --p2p.identityPrivateKey={{p2pIdentityPrivateKey}}
+ --dashboard.bindAddress=0.0.0.0:8081
+ --metrics.bindAddress=iota-core:9311
--inx.enabled=true
--inx.bindAddress=iota-core:9029
+##################
+# INX Extensions #
+##################
+
inx-indexer:
container_name: inx-indexer
image: iotaledger/inx-indexer:2.0-alpha
- stop_grace_period: 5m
+ stop_grace_period: 1m
+ restart: unless-stopped
+ depends_on:
+ iota-core:
+ condition: service_healthy
+ ulimits:
+ nofile:
+ soft: 16384
+ hard: 16384
volumes:
- ./data:/app/database
- command:
- - "--inx.address=iota-core:9029"
- - "--indexer.db.sqlite.path=database/indexer"
- - "--restAPI.bindAddress=inx-indexer:9091"
\ No newline at end of file
+ command: >
+ --inx.address=iota-core:9029
+ --indexer.db.sqlite.path=database/indexer
+ --restAPI.bindAddress=inx-indexer:9091
+
+{% if 'node-04' in inventory_hostname %}
+ inx-blockissuer:
+ container_name: inx-blockissuer
+ image: iotaledger/inx-blockissuer:1.0-alpha
+ stop_grace_period: 1m
+ restart: unless-stopped
+ depends_on:
+ iota-core:
+ condition: service_healthy
+ inx-indexer:
+ condition: service_started
+ environment:
+ - "BLOCKISSUER_PRV_KEY={{blockissuerPrvKey}}"
+ command: >
+ --inx.address=iota-core:9029
+ --restAPI.bindAddress=inx-blockissuer:9086
+ --blockIssuer.accountAddress=rms1pqas0clgfsf8du9e6dw0yx9nwclqe0dd4f728pvgmcshpscm8r5mkddrrfc
+ --blockIssuer.proofOfWork.targetTrailingZeros=5
+
+ inx-faucet:
+ container_name: inx-faucet
+ image: iotaledger/inx-faucet:2.0-alpha
+ stop_grace_period: 1m
+ restart: unless-stopped
+ depends_on:
+ iota-core:
+ condition: service_healthy
+ inx-indexer:
+ condition: service_started
+ inx-blockissuer:
+ condition: service_started
+ networks:
+ - iota-core
+ ports:
+ - "8091:8091/tcp" # Faucet Frontend
+ environment:
+ - "FAUCET_PRV_KEY={{faucetPrvKey}}"
+ command: >
+ --inx.address=iota-core:9029
+ --faucet.bindAddress=0.0.0.0:8091
+{% endif %}
\ No newline at end of file
diff --git a/deploy/ansible/roles/metrics/files/grafana/provisioning/dashboards/local_dashboard.json b/deploy/ansible/roles/metrics/files/grafana/provisioning/dashboards/local_dashboard.json
index f464af4c6..571712c82 100644
--- a/deploy/ansible/roles/metrics/files/grafana/provisioning/dashboards/local_dashboard.json
+++ b/deploy/ansible/roles/metrics/files/grafana/provisioning/dashboards/local_dashboard.json
@@ -1340,8 +1340,8 @@
{
"current": {
"selected": false,
- "text": "validator-3:9311",
- "value": "validator-3:9311"
+ "text": "node-3-validator:9311",
+ "value": "node-3-validator:9311"
},
"datasource": {
"type": "prometheus",
diff --git a/deploy/ansible/roles/metrics/files/grafana/provisioning/dashboards/local_dashboard_old.json b/deploy/ansible/roles/metrics/files/grafana/provisioning/dashboards/local_dashboard_old.json
index 71fb84826..320e8c1a6 100644
--- a/deploy/ansible/roles/metrics/files/grafana/provisioning/dashboards/local_dashboard_old.json
+++ b/deploy/ansible/roles/metrics/files/grafana/provisioning/dashboards/local_dashboard_old.json
@@ -4847,8 +4847,8 @@
{
"current": {
"selected": false,
- "text": "validator-3:9311",
- "value": "validator-3:9311"
+ "text": "node-3-validator:9311",
+ "value": "node-3-validator:9311"
},
"datasource": {
"type": "prometheus",
diff --git a/deploy/ansible/roles/metrics/files/grafana/provisioning/dashboards/scheduler-metrics.json b/deploy/ansible/roles/metrics/files/grafana/provisioning/dashboards/scheduler-metrics.json
index c3d8798c3..e76dd6b1c 100644
--- a/deploy/ansible/roles/metrics/files/grafana/provisioning/dashboards/scheduler-metrics.json
+++ b/deploy/ansible/roles/metrics/files/grafana/provisioning/dashboards/scheduler-metrics.json
@@ -772,8 +772,8 @@
{
"current": {
"selected": true,
- "text": "validator-2:9311",
- "value": "validator-2:9311"
+ "text": "node-2-validator:9311",
+ "value": "node-2-validator:9311"
},
"datasource": {
"type": "prometheus",
diff --git a/deploy/ansible/run.sh b/deploy/ansible/run.sh
index 766e1e3df..3d800611d 100755
--- a/deploy/ansible/run.sh
+++ b/deploy/ansible/run.sh
@@ -16,20 +16,23 @@ wireguard_server_private_key=$WIREGUARD_SERVER_PRIVKEY
elkElasticUser=$ELASTIC_USER
elkElasticPassword=$ELASTIC_PASSWORD
grafanaAdminPassword=$GRAFANA_ADMIN_PASSWORD
+
NODE_01_ACCOUNTID=$NODE_01_ACCOUNTID
NODE_01_VALIDATOR_PRIVKEY=$NODE_01_VALIDATOR_PRIVKEY
NODE_01_P2PIDENTITYPRIVATEKEY=$NODE_01_P2PIDENTITYPRIVATEKEY
+
NODE_02_ACCOUNTID=$NODE_02_ACCOUNTID
NODE_02_VALIDATOR_PRIVKEY=$NODE_02_VALIDATOR_PRIVKEY
NODE_02_P2PIDENTITYPRIVATEKEY=$NODE_02_P2PIDENTITYPRIVATEKEY
+
NODE_03_ACCOUNTID=$NODE_03_ACCOUNTID
NODE_03_VALIDATOR_PRIVKEY=$NODE_03_VALIDATOR_PRIVKEY
NODE_03_P2PIDENTITYPRIVATEKEY=$NODE_03_P2PIDENTITYPRIVATEKEY
-NODE_04_ACCOUNTID=$NODE_04_ACCOUNTID
-NODE_04_VALIDATOR_PRIVKEY=$NODE_04_VALIDATOR_PRIVKEY
+
NODE_04_P2PIDENTITYPRIVATEKEY=$NODE_04_P2PIDENTITYPRIVATEKEY
-NODE_05_ACCOUNTID=$NODE_05_ACCOUNTID
-NODE_05_VALIDATOR_PRIVKEY=$NODE_05_VALIDATOR_PRIVKEY
+NODE_04_BLOCKISSUER_PRV_KEY=$NODE_04_BLOCKISSUER_PRIVKEY
+NODE_04_FAUCET_PRV_KEY=$NODE_04_FAUCET_PRIVKEY
+
NODE_05_P2PIDENTITYPRIVATEKEY=$NODE_05_P2PIDENTITYPRIVATEKEY
-NODE_05_P2PPUBKEY=$NODE_05_P2PPUBKEY" \
+
${ARGS[@]:2} deploy/ansible/"${2:-deploy.yml}"
diff --git a/documentation/docs/references/configuration.md b/documentation/docs/references/configuration.md
index acf7bfa3f..ba034fde1 100644
--- a/documentation/docs/references/configuration.md
+++ b/documentation/docs/references/configuration.md
@@ -175,7 +175,7 @@ Example:
| Name | Description | Type | Default value |
| ------------------------------ | ---------------------------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| enabled | Whether the REST API plugin is enabled | boolean | true |
-| bindAddress | The bind address on which the REST API listens on | string | "0.0.0.0:8080" |
+| bindAddress | The bind address on which the REST API listens on | string | "0.0.0.0:14265" |
| publicRoutes | The HTTP REST routes which can be called without authorization. Wildcards using \* are allowed | array | /health
/api/routes
/api/core/v3/info
/api/core/v3/blocks\*
/api/core/v3/transactions\*
/api/core/v3/commitments\*
/api/core/v3/outputs\*
/api/core/v3/accounts\*
/api/core/v3/validators\*
/api/core/v3/rewards\*
/api/core/v3/committee
/api/debug/v2/\*
/api/indexer/v2/\*
/api/mqtt/v2 |
| protectedRoutes | The HTTP REST routes which need to be called with authorization. Wildcards using \* are allowed | array | /api/\* |
| debugRequestLoggerEnabled | Whether the debug logging for requests should be enabled | boolean | false |
@@ -204,7 +204,7 @@ Example:
{
"restAPI": {
"enabled": true,
- "bindAddress": "0.0.0.0:8080",
+ "bindAddress": "0.0.0.0:14265",
"publicRoutes": [
"/health",
"/api/routes",
@@ -263,7 +263,7 @@ Example:
}
```
-## 7. Metricstracker
+## 7. MetricsTracker
| Name | Description | Type | Default value |
| ------- | --------------------------------------------- | ------- | ------------- |
@@ -273,7 +273,7 @@ Example:
```json
{
- "metricstracker": {
+ "metricsTracker": {
"enabled": true
}
}
diff --git a/go.mod b/go.mod
index 5ed1609ae..2fa655a8c 100644
--- a/go.mod
+++ b/go.mod
@@ -23,18 +23,20 @@ require (
github.com/iotaledger/hive.go/runtime v0.0.0-20231020115340-13da292c580b
github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231020115340-13da292c580b
github.com/iotaledger/hive.go/stringify v0.0.0-20231020115340-13da292c580b
- github.com/iotaledger/inx-app v1.0.0-rc.3.0.20231023191159-38919c4705e0
- github.com/iotaledger/inx/go v1.0.0-rc.2.0.20231023190837-6e7b2cdfd4fd
- github.com/iotaledger/iota.go/v4 v4.0.0-20231023190719-1a9daaa83ca8
+ github.com/iotaledger/inx-app v1.0.0-rc.3.0.20231026154618-820b8eaed2cc
+ github.com/iotaledger/inx/go v1.0.0-rc.2.0.20231026154523-9ee0c47a283a
+ github.com/iotaledger/iota.go/v4 v4.0.0-20231026154111-efd63ff4f03d
github.com/labstack/echo/v4 v4.11.2
github.com/labstack/gommon v0.4.0
github.com/libp2p/go-libp2p v0.31.0
github.com/libp2p/go-libp2p-kad-dht v0.25.1
+ github.com/mr-tron/base58 v1.2.0
github.com/multiformats/go-multiaddr v0.12.0
github.com/multiformats/go-varint v0.0.7
github.com/orcaman/writerseeker v0.0.0-20200621085525-1d3f536ff85e
github.com/otiai10/copy v1.14.0
github.com/prometheus/client_golang v1.17.0
+ github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.8.4
github.com/wollac/iota-crypto-demo v0.0.0-20221117162917-b10619eccb98
github.com/zyedidia/generic v1.2.1
@@ -126,7 +128,6 @@ require (
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/mr-tron/base58 v1.2.0 // indirect
github.com/multiformats/go-base32 v0.1.0 // indirect
github.com/multiformats/go-base36 v0.2.0 // indirect
github.com/multiformats/go-multiaddr-dns v0.3.1 // indirect
@@ -157,7 +158,6 @@ require (
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/tcnksm/go-latest v0.0.0-20170313132115-e3007ae9052e // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
diff --git a/go.sum b/go.sum
index c3565bd8b..d8951f2a0 100644
--- a/go.sum
+++ b/go.sum
@@ -305,12 +305,12 @@ github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231020115340-13da292
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/inx-app v1.0.0-rc.3.0.20231023191159-38919c4705e0 h1:/8pbFXhTSroJvjJMfJqfHjzoT9N8B4LUY3SbKruD5MM=
-github.com/iotaledger/inx-app v1.0.0-rc.3.0.20231023191159-38919c4705e0/go.mod h1:My1SB4vZj42EgTDNJ/dgW8lUpLNmvtzu8f89J5y2kP0=
-github.com/iotaledger/inx/go v1.0.0-rc.2.0.20231023190837-6e7b2cdfd4fd h1:hh5mAnnaZHOYAi4CIqR9K/mv786ex9AQgpisbJ4ZMow=
-github.com/iotaledger/inx/go v1.0.0-rc.2.0.20231023190837-6e7b2cdfd4fd/go.mod h1:MK0SHfNicBmcaZb3qS3tA8NEJIWKNbcNtNNKuSDKqXY=
-github.com/iotaledger/iota.go/v4 v4.0.0-20231023190719-1a9daaa83ca8 h1:81aWESXFC04iKI9I140eDrBb9zBWXfVoAUMp9berk0c=
-github.com/iotaledger/iota.go/v4 v4.0.0-20231023190719-1a9daaa83ca8/go.mod h1:jqbLYq4a/FwuiPBqFfkAwwxU8vs3+kReRq2/tyX5qRA=
+github.com/iotaledger/inx-app v1.0.0-rc.3.0.20231026154618-820b8eaed2cc h1:Foz7Q1vNh0Ts+YTEODHO3LSKVGM/uNV3RqbBJS6u7yA=
+github.com/iotaledger/inx-app v1.0.0-rc.3.0.20231026154618-820b8eaed2cc/go.mod h1:gaQbe/L+wjjUeQj5N8+o/XdZnSosFmDQfxmfyrK05hc=
+github.com/iotaledger/inx/go v1.0.0-rc.2.0.20231026154523-9ee0c47a283a h1:8JbC44pNud1rT091fJA4bDC+35MozksapuCXf8M1kmg=
+github.com/iotaledger/inx/go v1.0.0-rc.2.0.20231026154523-9ee0c47a283a/go.mod h1:iXG/tO+GQZQzgIUyITnQDigb6Ny1wSueHFIYne4HBkc=
+github.com/iotaledger/iota.go/v4 v4.0.0-20231026154111-efd63ff4f03d h1:gcJz0J3xFELIPT7y4xqW+q25oOcK6QMlxNnrfFu8srA=
+github.com/iotaledger/iota.go/v4 v4.0.0-20231026154111-efd63ff4f03d/go.mod h1:jqbLYq4a/FwuiPBqFfkAwwxU8vs3+kReRq2/tyX5qRA=
github.com/ipfs/boxo v0.13.1 h1:nQ5oQzcMZR3oL41REJDcTbrvDvuZh3J9ckc9+ILeRQI=
github.com/ipfs/boxo v0.13.1/go.mod h1:btrtHy0lmO1ODMECbbEY1pxNtrLilvKSYLoGQt1yYCk=
github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s=
diff --git a/pkg/protocol/commitment_verifier.go b/pkg/protocol/commitment_verifier.go
index 1df7c3a97..85e715d89 100644
--- a/pkg/protocol/commitment_verifier.go
+++ b/pkg/protocol/commitment_verifier.go
@@ -19,15 +19,18 @@ type CommitmentVerifier struct {
validatorAccountsAtFork map[iotago.AccountID]*accounts.AccountData
}
-func NewCommitmentVerifier(mainEngine *engine.Engine, lastCommonCommitmentBeforeFork *model.Commitment) *CommitmentVerifier {
- committeeAtForkingPoint := mainEngine.SybilProtection.SeatManager().Committee(lastCommonCommitmentBeforeFork.Slot()).Accounts().IDs()
+func NewCommitmentVerifier(mainEngine *engine.Engine, lastCommonCommitmentBeforeFork *model.Commitment) (*CommitmentVerifier, error) {
+ committeeAtForkingPoint, exists := mainEngine.SybilProtection.SeatManager().CommitteeInSlot(lastCommonCommitmentBeforeFork.Slot())
+ if !exists {
+ return nil, ierrors.Errorf("committee in slot %d does not exist", lastCommonCommitmentBeforeFork.Slot())
+ }
return &CommitmentVerifier{
engine: mainEngine,
cumulativeWeight: lastCommonCommitmentBeforeFork.CumulativeWeight(),
- validatorAccountsAtFork: lo.PanicOnErr(mainEngine.Ledger.PastAccounts(committeeAtForkingPoint, lastCommonCommitmentBeforeFork.Slot())),
+ validatorAccountsAtFork: lo.PanicOnErr(mainEngine.Ledger.PastAccounts(committeeAtForkingPoint.Accounts().IDs(), lastCommonCommitmentBeforeFork.Slot())),
// TODO: what happens if the committee rotated after the fork?
- }
+ }, nil
}
func (c *CommitmentVerifier) verifyCommitment(commitment *model.Commitment, attestations []*iotago.Attestation, merkleProof *merklehasher.Proof[iotago.Identifier]) (blockIDsFromAttestations iotago.BlockIDs, cumulativeWeight uint64, err error) {
@@ -153,7 +156,13 @@ func (c *CommitmentVerifier) verifyAttestations(attestations []*iotago.Attestati
if err != nil {
return nil, 0, ierrors.Wrap(err, "error calculating blockID from attestation")
}
- if _, seatExists := c.engine.SybilProtection.SeatManager().Committee(attestationBlockID.Slot()).GetSeat(att.IssuerID); seatExists {
+
+ committee, exists := c.engine.SybilProtection.SeatManager().CommitteeInSlot(attestationBlockID.Slot())
+ if !exists {
+ return nil, 0, ierrors.Errorf("committee for slot %d does not exist", attestationBlockID.Slot())
+ }
+
+ if _, seatExists := committee.GetSeat(att.IssuerID); seatExists {
seatCount++
}
diff --git a/pkg/protocol/engine/attestation/attestations.go b/pkg/protocol/engine/attestation/attestations.go
index 06572e53b..1803a4b0f 100644
--- a/pkg/protocol/engine/attestation/attestations.go
+++ b/pkg/protocol/engine/attestation/attestations.go
@@ -17,7 +17,7 @@ type Attestations interface {
// GetMap returns the attestations that are included in the commitment of the given slot as ads.Map.
// If attestationCommitmentOffset=3 and commitment is 10, then the returned attestations are blocks from 7 to 10 that commit to at least 7.
GetMap(index iotago.SlotIndex) (attestations ads.Map[iotago.Identifier, iotago.AccountID, *iotago.Attestation], err error)
- AddAttestationFromValidationBlock(block *blocks.Block)
+ AddAttestationFromValidationBlock(block *blocks.Block) error
Commit(index iotago.SlotIndex) (newCW uint64, attestationsRoot iotago.Identifier, err error)
Import(reader io.ReadSeeker) (err error)
diff --git a/pkg/protocol/engine/attestation/slotattestation/manager.go b/pkg/protocol/engine/attestation/slotattestation/manager.go
index 890ffe56b..6b27b5b2d 100644
--- a/pkg/protocol/engine/attestation/slotattestation/manager.go
+++ b/pkg/protocol/engine/attestation/slotattestation/manager.go
@@ -49,7 +49,7 @@ const (
// - obtain and evict from it attestations that *commit to* lastCommittedSlot-attestationCommitmentOffset
// - committed attestations: retrieved at slot that we are committing, stored at slot lastCommittedSlot-attestationCommitmentOffset
type Manager struct {
- committeeFunc func(slot iotago.SlotIndex) *account.SeatedAccounts
+ committeeFunc func(slot iotago.SlotIndex) (*account.SeatedAccounts, bool)
futureAttestations *memstorage.IndexedStorage[iotago.SlotIndex, iotago.AccountID, *iotago.Attestation]
pendingAttestations *memstorage.IndexedStorage[iotago.SlotIndex, iotago.AccountID, *iotago.Attestation]
@@ -73,7 +73,7 @@ func NewProvider() module.Provider[*engine.Engine, attestation.Attestations] {
latestCommitment.Slot(),
latestCommitment.CumulativeWeight(),
e.Storage.Attestations,
- e.SybilProtection.SeatManager().Committee,
+ e.SybilProtection.SeatManager().CommitteeInSlot,
e,
)
})
@@ -83,7 +83,7 @@ func NewManager(
lastCommittedSlot iotago.SlotIndex,
lastCumulativeWeight uint64,
bucketedStorage func(slot iotago.SlotIndex) (kvstore.KVStore, error),
- committeeFunc func(slot iotago.SlotIndex) *account.SeatedAccounts,
+ committeeFunc func(slot iotago.SlotIndex) (*account.SeatedAccounts, bool),
apiProvider iotago.APIProvider,
) *Manager {
m := &Manager{
@@ -145,15 +145,19 @@ func (m *Manager) GetMap(slot iotago.SlotIndex) (ads.Map[iotago.Identifier, iota
}
// AddAttestationFromValidationBlock adds an attestation from a block to the future attestations (beyond the attestation window).
-func (m *Manager) AddAttestationFromValidationBlock(block *blocks.Block) {
+func (m *Manager) AddAttestationFromValidationBlock(block *blocks.Block) error {
// Only track validator blocks.
if _, isValidationBlock := block.ValidationBlock(); !isValidationBlock {
- return
+ return nil
}
+ committee, exists := m.committeeFunc(block.ID().Slot())
+ if !exists {
+ return ierrors.Errorf("committee for slot %d does not exist", block.ID().Slot())
+ }
// Only track attestations of active committee members.
- if _, exists := m.committeeFunc(block.ID().Slot()).GetSeat(block.ProtocolBlock().IssuerID); !exists {
- return
+ if _, exists := committee.GetSeat(block.ProtocolBlock().IssuerID); !exists {
+ return nil
}
m.commitmentMutex.RLock()
@@ -161,7 +165,7 @@ func (m *Manager) AddAttestationFromValidationBlock(block *blocks.Block) {
// We only care about attestations that are newer than the last committed slot.
if block.ID().Slot() <= m.lastCommittedSlot {
- return
+ return nil
}
newAttestation := iotago.NewAttestation(m.apiProvider.APIForSlot(block.ID().Slot()), block.ProtocolBlock())
@@ -179,6 +183,8 @@ func (m *Manager) AddAttestationFromValidationBlock(block *blocks.Block) {
return currentValue
})
+
+ return nil
}
func (m *Manager) applyToPendingAttestations(attestation *iotago.Attestation, cutoffSlot iotago.SlotIndex) {
@@ -254,9 +260,14 @@ func (m *Manager) Commit(slot iotago.SlotIndex) (newCW uint64, attestationsRoot
}
// Add all attestations to the tree and calculate the new cumulative weight.
+ committee, exists := m.committeeFunc(slot)
+ if !exists {
+ return 0, iotago.Identifier{}, ierrors.Wrapf(err, "failed to get committee when committing slot %d", slot)
+ }
+
for _, a := range attestations {
// TODO: which weight are we using here? The current one? Or the one of the slot of the attestation/commitmentID?
- if _, exists := m.committeeFunc(slot).GetSeat(a.IssuerID); exists {
+ if _, exists := committee.GetSeat(a.IssuerID); exists {
if err := tree.Set(a.IssuerID, a); err != nil {
return 0, iotago.Identifier{}, ierrors.Wrapf(err, "failed to set attestation %s in tree", a.IssuerID)
}
diff --git a/pkg/protocol/engine/attestation/slotattestation/testframework_test.go b/pkg/protocol/engine/attestation/slotattestation/testframework_test.go
index b931d2a2a..ad77b48fe 100644
--- a/pkg/protocol/engine/attestation/slotattestation/testframework_test.go
+++ b/pkg/protocol/engine/attestation/slotattestation/testframework_test.go
@@ -57,7 +57,7 @@ func NewTestFramework(test *testing.T) *TestFramework {
})), nil
}
- committeeFunc := func(index iotago.SlotIndex) *account.SeatedAccounts {
+ committeeFunc := func(index iotago.SlotIndex) (*account.SeatedAccounts, bool) {
accounts := account.NewAccounts()
var members []iotago.AccountID
t.issuerByAlias.ForEach(func(alias string, issuer *issuer) bool {
@@ -65,7 +65,7 @@ func NewTestFramework(test *testing.T) *TestFramework {
members = append(members, issuer.accountID)
return true
})
- return accounts.SelectCommittee(members...)
+ return accounts.SelectCommittee(members...), true
}
t.testAPI = iotago.V3API(
diff --git a/pkg/protocol/engine/congestioncontrol/scheduler/drr/scheduler.go b/pkg/protocol/engine/congestioncontrol/scheduler/drr/scheduler.go
index dacc96faa..034ead081 100644
--- a/pkg/protocol/engine/congestioncontrol/scheduler/drr/scheduler.go
+++ b/pkg/protocol/engine/congestioncontrol/scheduler/drr/scheduler.go
@@ -66,12 +66,18 @@ func NewProvider(opts ...options.Option[Scheduler]) module.Provider[*engine.Engi
})
e.Events.Notarization.LatestCommitmentUpdated.Hook(func(commitment *model.Commitment) {
// when the last slot of an epoch is committed, remove the queues of validators that are no longer in the committee.
- if s.apiProvider.CommittedAPI().TimeProvider().SlotsBeforeNextEpoch(commitment.Slot()) == 0 {
+ if s.apiProvider.APIForSlot(commitment.Slot()).TimeProvider().SlotsBeforeNextEpoch(commitment.Slot()) == 0 {
s.bufferMutex.Lock()
defer s.bufferMutex.Unlock()
+ committee, exists := s.seatManager.CommitteeInSlot(commitment.Slot() + 1)
+ if !exists {
+ s.errorHandler(ierrors.Errorf("committee does not exist in committed slot %d", commitment.Slot()+1))
+
+ return
+ }
s.validatorBuffer.buffer.ForEach(func(accountID iotago.AccountID, validatorQueue *ValidatorQueue) bool {
- if !s.seatManager.Committee(commitment.Slot() + 1).HasAccount(accountID) {
+ if !committee.HasAccount(accountID) {
s.shutdownValidatorQueue(validatorQueue)
s.validatorBuffer.Delete(accountID)
}
@@ -615,7 +621,7 @@ func (s *Scheduler) updateDeficit(accountID iotago.AccountID, delta Deficit) err
func (s *Scheduler) incrementDeficit(issuerID iotago.AccountID, rounds Deficit, slot iotago.SlotIndex) error {
quantum, err := s.quantumFunc(issuerID, slot)
if err != nil {
- return err
+ return ierrors.Wrap(err, "failed to retrieve quantum")
}
delta, err := safemath.SafeMul(quantum, rounds)
diff --git a/pkg/protocol/engine/consensus/blockgadget/testframework_test.go b/pkg/protocol/engine/consensus/blockgadget/testframework_test.go
index 144307109..a2d7371bc 100644
--- a/pkg/protocol/engine/consensus/blockgadget/testframework_test.go
+++ b/pkg/protocol/engine/consensus/blockgadget/testframework_test.go
@@ -9,14 +9,17 @@ import (
"github.com/stretchr/testify/require"
"github.com/iotaledger/hive.go/ds/shrinkingmap"
+ "github.com/iotaledger/hive.go/kvstore"
"github.com/iotaledger/hive.go/kvstore/mapdb"
"github.com/iotaledger/hive.go/lo"
+ "github.com/iotaledger/iota-core/pkg/core/account"
"github.com/iotaledger/iota-core/pkg/model"
"github.com/iotaledger/iota-core/pkg/protocol/engine/blocks"
"github.com/iotaledger/iota-core/pkg/protocol/engine/consensus/blockgadget"
"github.com/iotaledger/iota-core/pkg/protocol/engine/consensus/blockgadget/thresholdblockgadget"
"github.com/iotaledger/iota-core/pkg/protocol/engine/eviction"
"github.com/iotaledger/iota-core/pkg/protocol/sybilprotection/seatmanager/mock"
+ "github.com/iotaledger/iota-core/pkg/storage/prunable/epochstore"
"github.com/iotaledger/iota-core/pkg/storage/prunable/slotstore"
iotago "github.com/iotaledger/iota.go/v4"
"github.com/iotaledger/iota.go/v4/api"
@@ -40,7 +43,7 @@ func NewTestFramework(test *testing.T) *TestFramework {
T: test,
blocks: shrinkingmap.New[string, *blocks.Block](),
- SeatManager: mock.NewManualPOA(),
+ SeatManager: mock.NewManualPOA(api.SingleVersionProvider(tpkg.TestAPI), epochstore.NewStore(kvstore.Realm{}, kvstore.Realm{}, mapdb.NewMapDB(), 0, (*account.Accounts).Bytes, account.AccountsFromBytes)),
}
evictionState := eviction.NewState(mapdb.NewMapDB(), func(slot iotago.SlotIndex) (*slotstore.Store[iotago.BlockID, iotago.CommitmentID], error) {
@@ -53,7 +56,10 @@ func NewTestFramework(test *testing.T) *TestFramework {
})
t.blockCache = blocks.New(evictionState, api.SingleVersionProvider(tpkg.TestAPI))
- instance := thresholdblockgadget.New(t.blockCache, t.SeatManager)
+ instance := thresholdblockgadget.New(t.blockCache, t.SeatManager, func(err error) {
+ fmt.Printf(">> Gadget.Error: %s\n", err)
+ })
+
t.Events = instance.Events()
t.Instance = instance
diff --git a/pkg/protocol/engine/consensus/blockgadget/thresholdblockgadget/gadget.go b/pkg/protocol/engine/consensus/blockgadget/thresholdblockgadget/gadget.go
index 55f53835a..9ee2becde 100644
--- a/pkg/protocol/engine/consensus/blockgadget/thresholdblockgadget/gadget.go
+++ b/pkg/protocol/engine/consensus/blockgadget/thresholdblockgadget/gadget.go
@@ -5,6 +5,7 @@ import (
"github.com/iotaledger/hive.go/ds"
"github.com/iotaledger/hive.go/ds/walker"
+ "github.com/iotaledger/hive.go/ierrors"
"github.com/iotaledger/hive.go/runtime/event"
"github.com/iotaledger/hive.go/runtime/module"
"github.com/iotaledger/hive.go/runtime/options"
@@ -22,8 +23,9 @@ import (
type Gadget struct {
events *blockgadget.Events
- seatManager seatmanager.SeatManager
- blockCache *blocks.Blocks
+ seatManager seatmanager.SeatManager
+ blockCache *blocks.Blocks
+ errorHandler func(error)
optsAcceptanceThreshold float64
optsConfirmationThreshold float64
@@ -34,7 +36,7 @@ type Gadget struct {
func NewProvider(opts ...options.Option[Gadget]) module.Provider[*engine.Engine, blockgadget.Gadget] {
return module.Provide(func(e *engine.Engine) blockgadget.Gadget {
- g := New(e.BlockCache, e.SybilProtection.SeatManager(), opts...)
+ g := New(e.BlockCache, e.SybilProtection.SeatManager(), e.ErrorHandler("gadget"), opts...)
wp := e.Workers.CreatePool("ThresholdBlockGadget", workerpool.WithWorkerCount(1))
e.Events.Booker.BlockBooked.Hook(g.TrackWitnessWeight, event.WithWorkerPool(wp))
@@ -45,11 +47,12 @@ func NewProvider(opts ...options.Option[Gadget]) module.Provider[*engine.Engine,
})
}
-func New(blockCache *blocks.Blocks, seatManager seatmanager.SeatManager, opts ...options.Option[Gadget]) *Gadget {
+func New(blockCache *blocks.Blocks, seatManager seatmanager.SeatManager, errorHandler func(error), opts ...options.Option[Gadget]) *Gadget {
return options.Apply(&Gadget{
- events: blockgadget.NewEvents(),
- seatManager: seatManager,
- blockCache: blockCache,
+ events: blockgadget.NewEvents(),
+ seatManager: seatManager,
+ blockCache: blockCache,
+ errorHandler: errorHandler,
optsAcceptanceThreshold: 0.67,
optsConfirmationThreshold: 0.67,
@@ -98,8 +101,15 @@ func (g *Gadget) isCommitteeValidationBlock(block *blocks.Block) (seat account.S
return 0, false
}
+ committee, exists := g.seatManager.CommitteeInSlot(block.ID().Slot())
+ if !exists {
+ g.errorHandler(ierrors.Errorf("committee for slot %d does not exist", block.ID().Slot()))
+
+ return 0, false
+ }
+
// Only accept blocks for issuers that are part of the committee.
- return g.seatManager.Committee(block.ID().Slot()).GetSeat(block.ProtocolBlock().IssuerID)
+ return committee.GetSeat(block.ProtocolBlock().IssuerID)
}
func anyChildInSet(block *blocks.Block, set ds.Set[iotago.BlockID]) bool {
diff --git a/pkg/protocol/engine/engine.go b/pkg/protocol/engine/engine.go
index 6406d5b28..1bb299aa8 100644
--- a/pkg/protocol/engine/engine.go
+++ b/pkg/protocol/engine/engine.go
@@ -407,7 +407,7 @@ func (e *Engine) acceptanceHandler() {
e.Events.BlockGadget.BlockAccepted.Hook(func(block *blocks.Block) {
e.Ledger.TrackBlock(block)
- e.SybilProtection.TrackValidationBlock(block)
+ e.SybilProtection.TrackBlock(block)
e.UpgradeOrchestrator.TrackValidationBlock(block)
e.Events.AcceptedBlockProcessed.Trigger(block)
diff --git a/pkg/protocol/engine/filter/blockfilter/filter.go b/pkg/protocol/engine/filter/blockfilter/filter.go
index 6b20f66f6..0ed6900ec 100644
--- a/pkg/protocol/engine/filter/blockfilter/filter.go
+++ b/pkg/protocol/engine/filter/blockfilter/filter.go
@@ -29,7 +29,7 @@ type Filter struct {
optsMaxAllowedWallClockDrift time.Duration
- committeeFunc func(iotago.SlotIndex) *account.SeatedAccounts
+ committeeFunc func(iotago.SlotIndex) (*account.SeatedAccounts, bool)
module.Module
}
@@ -42,7 +42,7 @@ func NewProvider(opts ...options.Option[Filter]) module.Provider[*engine.Engine,
e.HookConstructed(func() {
e.Events.Filter.LinkTo(f.events)
e.SybilProtection.HookInitialized(func() {
- f.committeeFunc = e.SybilProtection.SeatManager().Committee
+ f.committeeFunc = e.SybilProtection.SeatManager().CommitteeInSlot
})
f.TriggerInitialized()
})
@@ -92,7 +92,18 @@ func (f *Filter) ProcessReceivedBlock(block *model.Block, source peer.ID) {
if _, isValidation := block.ValidationBlock(); isValidation {
blockSlot := block.ProtocolBlock().API.TimeProvider().SlotFromTime(block.ProtocolBlock().IssuingTime)
- if !f.committeeFunc(blockSlot).HasAccount(block.ProtocolBlock().IssuerID) {
+ committee, exists := f.committeeFunc(blockSlot)
+ if !exists {
+ f.events.BlockPreFiltered.Trigger(&filter.BlockPreFilteredEvent{
+ Block: block,
+ Reason: ierrors.Wrapf(ErrValidatorNotInCommittee, "no committee for slot %d", blockSlot),
+ Source: source,
+ })
+
+ return
+ }
+
+ if !committee.HasAccount(block.ProtocolBlock().IssuerID) {
f.events.BlockPreFiltered.Trigger(&filter.BlockPreFilteredEvent{
Block: block,
Reason: ierrors.Wrapf(ErrValidatorNotInCommittee, "validation block issuer %s is not part of the committee for slot %d", block.ProtocolBlock().IssuerID, blockSlot),
diff --git a/pkg/protocol/engine/filter/blockfilter/filter_test.go b/pkg/protocol/engine/filter/blockfilter/filter_test.go
index 1e7347fc7..02646bb56 100644
--- a/pkg/protocol/engine/filter/blockfilter/filter_test.go
+++ b/pkg/protocol/engine/filter/blockfilter/filter_test.go
@@ -96,14 +96,14 @@ func (t *TestFramework) IssueBlockAtSlotWithVersion(alias string, slot iotago.Sl
return t.processBlock(alias, block)
}
-func mockedCommitteeFunc(validatorAccountID iotago.AccountID) func(iotago.SlotIndex) *account.SeatedAccounts {
+func mockedCommitteeFunc(validatorAccountID iotago.AccountID) func(iotago.SlotIndex) (*account.SeatedAccounts, bool) {
mockedAccounts := account.NewAccounts()
mockedAccounts.Set(validatorAccountID, new(account.Pool))
seatedAccounts := account.NewSeatedAccounts(mockedAccounts)
seatedAccounts.Set(account.SeatIndex(0), validatorAccountID)
- return func(slot iotago.SlotIndex) *account.SeatedAccounts {
- return seatedAccounts
+ return func(slot iotago.SlotIndex) (*account.SeatedAccounts, bool) {
+ return seatedAccounts, true
}
}
diff --git a/pkg/protocol/engine/ledger/ledger/ledger.go b/pkg/protocol/engine/ledger/ledger/ledger.go
index 316055615..a6c177204 100644
--- a/pkg/protocol/engine/ledger/ledger/ledger.go
+++ b/pkg/protocol/engine/ledger/ledger/ledger.go
@@ -655,7 +655,7 @@ func (l *Ledger) processStateDiffTransactions(stateDiff mempool.StateDiff) (spen
continue
}
- accountDiff.BICChange += iotago.BlockIssuanceCredits(allotment.Value)
+ accountDiff.BICChange += iotago.BlockIssuanceCredits(allotment.Mana)
accountDiff.PreviousUpdatedTime = accountData.Credits.UpdateTime
// we are not transitioning the allotted account, so the new and previous expiry slots are the same
@@ -748,7 +748,12 @@ func (l *Ledger) resolveState(stateRef mempool.StateReference) *promise.Promise[
func (l *Ledger) blockPreAccepted(block *blocks.Block) {
voteRank := ledger.NewBlockVoteRank(block.ID(), block.ProtocolBlock().IssuingTime)
- seat, exists := l.sybilProtection.SeatManager().Committee(block.ID().Slot()).GetSeat(block.ProtocolBlock().IssuerID)
+ committee, exists := l.sybilProtection.SeatManager().CommitteeInSlot(block.ID().Slot())
+ if !exists {
+ panic("committee should exist because we pre-accepted the block")
+ }
+
+ seat, exists := committee.GetSeat(block.ProtocolBlock().IssuerID)
if !exists {
return
}
diff --git a/pkg/protocol/engine/notarization/slotnotarization/manager.go b/pkg/protocol/engine/notarization/slotnotarization/manager.go
index 1555dc0d1..217b3288b 100644
--- a/pkg/protocol/engine/notarization/slotnotarization/manager.go
+++ b/pkg/protocol/engine/notarization/slotnotarization/manager.go
@@ -143,9 +143,7 @@ func (m *Manager) notarizeAcceptedBlock(block *blocks.Block) (err error) {
return ierrors.Wrap(err, "failed to add accepted block to slot mutations")
}
- m.attestation.AddAttestationFromValidationBlock(block)
-
- return
+ return m.attestation.AddAttestationFromValidationBlock(block)
}
func (m *Manager) tryCommitSlotUntil(acceptedBlockIndex iotago.SlotIndex) {
diff --git a/pkg/protocol/engine/upgrade/signalingupgradeorchestrator/orchestrator.go b/pkg/protocol/engine/upgrade/signalingupgradeorchestrator/orchestrator.go
index 48b7f0e4d..4e5ee6026 100644
--- a/pkg/protocol/engine/upgrade/signalingupgradeorchestrator/orchestrator.go
+++ b/pkg/protocol/engine/upgrade/signalingupgradeorchestrator/orchestrator.go
@@ -146,7 +146,11 @@ func (o *Orchestrator) TrackValidationBlock(block *blocks.Block) {
}
newSignaledBlock := model.NewSignaledBlock(block.ID(), block.ProtocolBlock(), validationBlock)
- committee := o.seatManager.Committee(block.ID().Slot())
+ committee, exists := o.seatManager.CommitteeInSlot(block.ID().Slot())
+ if !exists {
+ return
+ }
+
seat, exists := committee.GetSeat(block.ProtocolBlock().IssuerID)
if !exists {
return
diff --git a/pkg/protocol/protocol_fork.go b/pkg/protocol/protocol_fork.go
index 88e508028..1425276cd 100644
--- a/pkg/protocol/protocol_fork.go
+++ b/pkg/protocol/protocol_fork.go
@@ -194,7 +194,11 @@ func (p *Protocol) processFork(fork *chainmanager.Fork) (anchorBlockIDs iotago.B
ch := make(chan *commitmentVerificationResult)
defer close(ch)
- commitmentVerifier := NewCommitmentVerifier(p.MainEngineInstance(), fork.MainChain.Commitment(fork.ForkingPoint.Slot()-1).Commitment())
+ commitmentVerifier, err := NewCommitmentVerifier(p.MainEngineInstance(), fork.MainChain.Commitment(fork.ForkingPoint.Slot()-1).Commitment())
+ if err != nil {
+ return nil, false, true, ierrors.Wrapf(err, "failed to create commitment verifier for %s", fork.MainChain.Commitment(fork.ForkingPoint.Slot()-1).ID())
+ }
+
verifyCommitmentFunc := func(commitment *model.Commitment, attestations []*iotago.Attestation, merkleProof *merklehasher.Proof[iotago.Identifier], _ peer.ID) {
blockIDs, actualCumulativeWeight, err := commitmentVerifier.verifyCommitment(commitment, attestations, merkleProof)
diff --git a/pkg/protocol/sybilprotection/activitytracker/activitytracker.go b/pkg/protocol/sybilprotection/activitytracker/activitytracker.go
new file mode 100644
index 000000000..07b95bb39
--- /dev/null
+++ b/pkg/protocol/sybilprotection/activitytracker/activitytracker.go
@@ -0,0 +1,14 @@
+package activitytracker
+
+import (
+ "time"
+
+ "github.com/iotaledger/hive.go/ds"
+ "github.com/iotaledger/iota-core/pkg/core/account"
+ iotago "github.com/iotaledger/iota.go/v4"
+)
+
+type ActivityTracker interface {
+ OnlineCommittee() ds.Set[account.SeatIndex]
+ MarkSeatActive(seat account.SeatIndex, id iotago.AccountID, seatActivityTime time.Time)
+}
diff --git a/pkg/protocol/sybilprotection/activitytracker/activitytrackerv1/activitytracker.go b/pkg/protocol/sybilprotection/activitytracker/activitytrackerv1/activitytracker.go
new file mode 100644
index 000000000..e167e2ff5
--- /dev/null
+++ b/pkg/protocol/sybilprotection/activitytracker/activitytrackerv1/activitytracker.go
@@ -0,0 +1,83 @@
+package activitytrackerv1
+
+import (
+ "time"
+
+ "github.com/iotaledger/hive.go/ds"
+ "github.com/iotaledger/hive.go/ds/shrinkingmap"
+ "github.com/iotaledger/hive.go/runtime/syncutils"
+ "github.com/iotaledger/hive.go/runtime/timed"
+ "github.com/iotaledger/iota-core/pkg/core/account"
+ "github.com/iotaledger/iota-core/pkg/protocol/sybilprotection/activitytracker"
+ iotago "github.com/iotaledger/iota.go/v4"
+)
+
+// ActivityTracker is a sybil protection module for tracking activity of committee members.
+type ActivityTracker struct {
+ Events *activitytracker.Events
+
+ onlineCommittee ds.Set[account.SeatIndex]
+ inactivityQueue timed.PriorityQueue[account.SeatIndex]
+ lastActivities *shrinkingmap.ShrinkingMap[account.SeatIndex, time.Time]
+ lastActivityTime time.Time
+ activityMutex syncutils.RWMutex
+
+ activityWindow time.Duration
+}
+
+func NewActivityTracker(activityWindow time.Duration) *ActivityTracker {
+ return &ActivityTracker{
+ Events: activitytracker.NewEvents(),
+ onlineCommittee: ds.NewSet[account.SeatIndex](),
+ inactivityQueue: timed.NewPriorityQueue[account.SeatIndex](true),
+ lastActivities: shrinkingmap.New[account.SeatIndex, time.Time](),
+
+ activityWindow: activityWindow,
+ }
+}
+
+// OnlineCommittee returns the set of validators selected to be part of the committee that has been seen recently.
+func (a *ActivityTracker) OnlineCommittee() ds.Set[account.SeatIndex] {
+ a.activityMutex.RLock()
+ defer a.activityMutex.RUnlock()
+
+ return a.onlineCommittee
+}
+
+func (a *ActivityTracker) MarkSeatActive(seat account.SeatIndex, id iotago.AccountID, seatActivityTime time.Time) {
+ a.activityMutex.Lock()
+ defer a.activityMutex.Unlock()
+
+ if lastActivity, exists := a.lastActivities.Get(seat); (exists && lastActivity.After(seatActivityTime)) || seatActivityTime.Before(a.lastActivityTime.Add(-a.activityWindow)) {
+ return
+ } else if !exists {
+ a.onlineCommittee.Add(seat)
+ a.Events.OnlineCommitteeSeatAdded.Trigger(seat, id)
+ }
+
+ a.lastActivities.Set(seat, seatActivityTime)
+
+ a.inactivityQueue.Push(seat, seatActivityTime)
+
+ if seatActivityTime.Before(a.lastActivityTime) {
+ return
+ }
+
+ a.lastActivityTime = seatActivityTime
+
+ activityThreshold := seatActivityTime.Add(-a.activityWindow)
+ for _, inactiveSeat := range a.inactivityQueue.PopUntil(activityThreshold) {
+ if lastActivityForInactiveSeat, exists := a.lastActivities.Get(inactiveSeat); exists && lastActivityForInactiveSeat.After(activityThreshold) {
+ continue
+ }
+
+ a.markSeatInactive(inactiveSeat)
+ }
+}
+
+func (a *ActivityTracker) markSeatInactive(seat account.SeatIndex) {
+ a.lastActivities.Delete(seat)
+ a.onlineCommittee.Delete(seat)
+
+ a.Events.OnlineCommitteeSeatRemoved.Trigger(seat)
+}
diff --git a/pkg/protocol/sybilprotection/activitytracker/events.go b/pkg/protocol/sybilprotection/activitytracker/events.go
new file mode 100644
index 000000000..1c210f50b
--- /dev/null
+++ b/pkg/protocol/sybilprotection/activitytracker/events.go
@@ -0,0 +1,22 @@
+package activitytracker
+
+import (
+ "github.com/iotaledger/hive.go/runtime/event"
+ "github.com/iotaledger/iota-core/pkg/core/account"
+ iotago "github.com/iotaledger/iota.go/v4"
+)
+
+type Events struct {
+ OnlineCommitteeSeatAdded *event.Event2[account.SeatIndex, iotago.AccountID]
+ OnlineCommitteeSeatRemoved *event.Event1[account.SeatIndex]
+
+ event.Group[Events, *Events]
+}
+
+// NewEvents contains the constructor of the Events object (it is generated by a generic factory).
+var NewEvents = event.CreateGroupConstructor(func() (newEvents *Events) {
+ return &Events{
+ OnlineCommitteeSeatAdded: event.New2[account.SeatIndex, iotago.AccountID](),
+ OnlineCommitteeSeatRemoved: event.New1[account.SeatIndex](),
+ }
+})
diff --git a/pkg/protocol/sybilprotection/seatmanager/mock/mockseatmanager.go b/pkg/protocol/sybilprotection/seatmanager/mock/mockseatmanager.go
index 223f212df..d8b53b4a1 100644
--- a/pkg/protocol/sybilprotection/seatmanager/mock/mockseatmanager.go
+++ b/pkg/protocol/sybilprotection/seatmanager/mock/mockseatmanager.go
@@ -2,20 +2,27 @@ package mock
import (
"fmt"
+ "time"
"github.com/iotaledger/hive.go/ds"
"github.com/iotaledger/hive.go/ds/shrinkingmap"
+ "github.com/iotaledger/hive.go/ierrors"
"github.com/iotaledger/hive.go/runtime/module"
"github.com/iotaledger/iota-core/pkg/core/account"
"github.com/iotaledger/iota-core/pkg/protocol/engine"
+ "github.com/iotaledger/iota-core/pkg/protocol/engine/accounts"
"github.com/iotaledger/iota-core/pkg/protocol/engine/blocks"
"github.com/iotaledger/iota-core/pkg/protocol/sybilprotection/seatmanager"
+ "github.com/iotaledger/iota-core/pkg/storage/prunable/epochstore"
iotago "github.com/iotaledger/iota.go/v4"
"github.com/iotaledger/iota.go/v4/tpkg"
)
type ManualPOA struct {
- events *seatmanager.Events
+ events *seatmanager.Events
+ apiProvider iotago.APIProvider
+ committeeStore *epochstore.Store[*account.Accounts]
+
accounts *account.Accounts
committee *account.SeatedAccounts
online ds.Set[account.SeatIndex]
@@ -24,12 +31,14 @@ type ManualPOA struct {
module.Module
}
-func NewManualPOA() *ManualPOA {
+func NewManualPOA(e iotago.APIProvider, committeeStore *epochstore.Store[*account.Accounts]) *ManualPOA {
m := &ManualPOA{
- events: seatmanager.NewEvents(),
- accounts: account.NewAccounts(),
- online: ds.NewSet[account.SeatIndex](),
- aliases: shrinkingmap.New[string, iotago.AccountID](),
+ events: seatmanager.NewEvents(),
+ apiProvider: e,
+ committeeStore: committeeStore,
+ accounts: account.NewAccounts(),
+ online: ds.NewSet[account.SeatIndex](),
+ aliases: shrinkingmap.New[string, iotago.AccountID](),
}
m.committee = m.accounts.SelectCommittee()
@@ -38,7 +47,7 @@ func NewManualPOA() *ManualPOA {
func NewManualPOAProvider() module.Provider[*engine.Engine, seatmanager.SeatManager] {
return module.Provide(func(e *engine.Engine) seatmanager.SeatManager {
- poa := NewManualPOA()
+ poa := NewManualPOA(e, e.Storage.Committee())
e.Events.CommitmentFilter.BlockAllowed.Hook(func(block *blocks.Block) {
poa.events.BlockProcessed.Trigger(block)
})
@@ -52,17 +61,36 @@ func NewManualPOAProvider() module.Provider[*engine.Engine, seatmanager.SeatMana
func (m *ManualPOA) AddRandomAccount(alias string) iotago.AccountID {
id := iotago.AccountID(tpkg.Rand32ByteArray())
id.RegisterAlias(alias)
- m.accounts.Set(id, &account.Pool{}) // We don't care about pools with PoA
+ m.accounts.Set(id, &account.Pool{
+ PoolStake: 1,
+ ValidatorStake: 1,
+ FixedCost: 1,
+ }) // We don't care about pools with PoA, but need to set something to avoid division by zero errors.
+
m.aliases.Set(alias, id)
- m.committee.Set(account.SeatIndex(m.committee.SeatCount()), id)
+
+ m.committee = m.accounts.SelectCommittee(m.accounts.IDs()...)
+
+ if err := m.committeeStore.Store(0, m.accounts); err != nil {
+ panic(err)
+ }
return id
}
func (m *ManualPOA) AddAccount(id iotago.AccountID, alias string) iotago.AccountID {
- m.accounts.Set(id, &account.Pool{}) // We don't care about pools with PoA
+ m.accounts.Set(id, &account.Pool{
+ PoolStake: 1,
+ ValidatorStake: 1,
+ FixedCost: 1,
+ }) // We don't care about pools with PoA, but need to set something to avoid division by zero errors.
m.aliases.Set(alias, id)
- m.committee.Set(account.SeatIndex(m.committee.SeatCount()), id)
+
+ m.committee = m.accounts.SelectCommittee(m.accounts.IDs()...)
+
+ if err := m.committeeStore.Store(0, m.accounts); err != nil {
+ panic(err)
+ }
return id
}
@@ -100,8 +128,27 @@ func (m *ManualPOA) Accounts() *account.Accounts {
return m.accounts
}
-func (m *ManualPOA) Committee(_ iotago.SlotIndex) *account.SeatedAccounts {
- return m.committee
+// CommitteeInSlot returns the set of validators selected to be part of the committee in the given slot.
+func (m *ManualPOA) CommitteeInSlot(slot iotago.SlotIndex) (*account.SeatedAccounts, bool) {
+ return m.committeeInEpoch(m.apiProvider.APIForSlot(slot).TimeProvider().EpochFromSlot(slot))
+}
+
+// CommitteeInEpoch returns the set of validators selected to be part of the committee in the given epoch.
+func (m *ManualPOA) CommitteeInEpoch(epoch iotago.EpochIndex) (*account.SeatedAccounts, bool) {
+ return m.committeeInEpoch(epoch)
+}
+
+func (m *ManualPOA) committeeInEpoch(epoch iotago.EpochIndex) (*account.SeatedAccounts, bool) {
+ c, err := m.committeeStore.Load(epoch)
+ if err != nil {
+ panic(ierrors.Wrapf(err, "failed to load committee for epoch %d", epoch))
+ }
+
+ if c == nil {
+ return nil, false
+ }
+
+ return c.SelectCommittee(c.IDs()...), true
}
func (m *ManualPOA) OnlineCommittee() ds.Set[account.SeatIndex] {
@@ -112,16 +159,42 @@ func (m *ManualPOA) SeatCount() int {
return m.committee.SeatCount()
}
-func (m *ManualPOA) RotateCommittee(_ iotago.EpochIndex, _ *account.Accounts) *account.SeatedAccounts {
- return m.committee
+func (m *ManualPOA) RotateCommittee(epoch iotago.EpochIndex, validators accounts.AccountsData) (*account.SeatedAccounts, error) {
+ if m.committee == nil || m.accounts.Size() == 0 {
+ m.accounts = account.NewAccounts()
+
+ for _, validatorData := range validators {
+ m.accounts.Set(validatorData.ID, &account.Pool{
+ PoolStake: validatorData.ValidatorStake + validatorData.DelegationStake,
+ ValidatorStake: validatorData.ValidatorStake,
+ FixedCost: validatorData.FixedCost,
+ })
+ }
+ m.committee = m.accounts.SelectCommittee(m.accounts.IDs()...)
+ }
+
+ if err := m.committeeStore.Store(epoch, m.accounts); err != nil {
+ panic(err)
+ }
+
+ return m.committee, nil
}
-func (m *ManualPOA) SetCommittee(_ iotago.EpochIndex, _ *account.Accounts) {
+func (m *ManualPOA) SetCommittee(epoch iotago.EpochIndex, validators *account.Accounts) error {
+ if m.committee == nil || m.accounts.Size() == 0 {
+ m.accounts = validators
+ m.committee = m.accounts.SelectCommittee(validators.IDs()...)
+ }
+
+ if err := m.committeeStore.Store(epoch, validators); err != nil {
+ panic(err)
+ }
+
+ return nil
}
-func (m *ManualPOA) ImportCommittee(_ iotago.EpochIndex, validators *account.Accounts) {
- m.accounts = validators
- m.committee = m.accounts.SelectCommittee(validators.IDs()...)
+func (m *ManualPOA) InitializeCommittee(_ iotago.EpochIndex, _ time.Time) error {
+ return nil
}
func (m *ManualPOA) Shutdown() {}
diff --git a/pkg/protocol/sybilprotection/seatmanager/poa/poa.go b/pkg/protocol/sybilprotection/seatmanager/poa/poa.go
index 8d5de21dd..8a0256e61 100644
--- a/pkg/protocol/sybilprotection/seatmanager/poa/poa.go
+++ b/pkg/protocol/sybilprotection/seatmanager/poa/poa.go
@@ -4,34 +4,31 @@ import (
"time"
"github.com/iotaledger/hive.go/ds"
- "github.com/iotaledger/hive.go/ds/shrinkingmap"
+ "github.com/iotaledger/hive.go/ierrors"
"github.com/iotaledger/hive.go/runtime/module"
"github.com/iotaledger/hive.go/runtime/options"
"github.com/iotaledger/hive.go/runtime/syncutils"
- "github.com/iotaledger/hive.go/runtime/timed"
- "github.com/iotaledger/hive.go/runtime/workerpool"
"github.com/iotaledger/iota-core/pkg/core/account"
"github.com/iotaledger/iota-core/pkg/protocol/engine"
+ "github.com/iotaledger/iota-core/pkg/protocol/engine/accounts"
"github.com/iotaledger/iota-core/pkg/protocol/engine/blocks"
- "github.com/iotaledger/iota-core/pkg/protocol/engine/clock"
+ "github.com/iotaledger/iota-core/pkg/protocol/sybilprotection/activitytracker"
+ "github.com/iotaledger/iota-core/pkg/protocol/sybilprotection/activitytracker/activitytrackerv1"
"github.com/iotaledger/iota-core/pkg/protocol/sybilprotection/seatmanager"
+ "github.com/iotaledger/iota-core/pkg/storage/prunable/epochstore"
iotago "github.com/iotaledger/iota.go/v4"
)
// SeatManager is a sybil protection module for the engine that manages the weights of actors according to their stake.
type SeatManager struct {
- events *seatmanager.Events
-
- clock clock.Clock
- workers *workerpool.Group
- accounts *account.Accounts
- committee *account.SeatedAccounts
- onlineCommittee ds.Set[account.SeatIndex]
- inactivityQueue timed.PriorityQueue[account.SeatIndex]
- lastActivities *shrinkingmap.ShrinkingMap[account.SeatIndex, time.Time]
- lastActivityTime time.Time
- activityMutex syncutils.RWMutex
- committeeMutex syncutils.RWMutex
+ events *seatmanager.Events
+ apiProvider iotago.APIProvider
+
+ committee *account.SeatedAccounts
+ committeeStore *epochstore.Store[*account.Accounts]
+ activityTracker activitytracker.ActivityTracker
+
+ committeeMutex syncutils.RWMutex
optsActivityWindow time.Duration
optsOnlineCommitteeStartup []iotago.AccountID
@@ -39,34 +36,39 @@ type SeatManager struct {
module.Module
}
-// NewProvider returns a new sybil protection provider that uses the ProofOfStake module.
+// NewProvider returns a new sybil protection provider that uses the ProofOfAuthority module.
func NewProvider(opts ...options.Option[SeatManager]) module.Provider[*engine.Engine, seatmanager.SeatManager] {
return module.Provide(func(e *engine.Engine) seatmanager.SeatManager {
return options.Apply(
&SeatManager{
- events: seatmanager.NewEvents(),
- workers: e.Workers.CreateGroup("SeatManager"),
- accounts: account.NewAccounts(),
- onlineCommittee: ds.NewSet[account.SeatIndex](),
- inactivityQueue: timed.NewPriorityQueue[account.SeatIndex](true),
- lastActivities: shrinkingmap.New[account.SeatIndex, time.Time](),
+ events: seatmanager.NewEvents(),
+ apiProvider: e,
+ committeeStore: e.Storage.Committee(),
optsActivityWindow: time.Second * 30,
}, opts, func(s *SeatManager) {
+ activityTracker := activitytrackerv1.NewActivityTracker(s.optsActivityWindow)
+ s.activityTracker = activityTracker
+ s.events.OnlineCommitteeSeatAdded.LinkTo(activityTracker.Events.OnlineCommitteeSeatAdded)
+ s.events.OnlineCommitteeSeatRemoved.LinkTo(activityTracker.Events.OnlineCommitteeSeatRemoved)
+
e.Events.SeatManager.LinkTo(s.events)
e.HookConstructed(func() {
- s.clock = e.Clock
-
s.TriggerConstructed()
// We need to mark validators as active upon solidity of blocks as otherwise we would not be able to
// recover if no node was part of the online committee anymore.
e.Events.CommitmentFilter.BlockAllowed.Hook(func(block *blocks.Block) {
// Only track identities that are part of the committee.
- seat, exists := s.Committee(block.ID().Slot()).GetSeat(block.ProtocolBlock().IssuerID)
+ committee, exists := s.CommitteeInSlot(block.ID().Slot())
+ if !exists {
+ panic(ierrors.Errorf("committee not selected for slot %d, but received block in that slot", block.ID().Slot()))
+ }
+
+ seat, exists := committee.GetSeat(block.ProtocolBlock().IssuerID)
if exists {
- s.markSeatActive(seat, block.ProtocolBlock().IssuerID, block.IssuingTime())
+ s.activityTracker.MarkSeatActive(seat, block.ProtocolBlock().IssuerID, block.IssuingTime())
}
s.events.BlockProcessed.Trigger(block)
@@ -78,29 +80,64 @@ func NewProvider(opts ...options.Option[SeatManager]) module.Provider[*engine.En
var _ seatmanager.SeatManager = &SeatManager{}
-func (s *SeatManager) RotateCommittee(_ iotago.EpochIndex, _ *account.Accounts) *account.SeatedAccounts {
+func (s *SeatManager) RotateCommittee(epoch iotago.EpochIndex, validators accounts.AccountsData) (*account.SeatedAccounts, error) {
+ s.committeeMutex.RLock()
+ defer s.committeeMutex.RUnlock()
+
+ // if committee is not set, then set it according to passed validators (used for creating a snapshot)
+ if s.committee == nil {
+ committeeAccounts := account.NewAccounts()
+
+ for _, validatorData := range validators {
+ committeeAccounts.Set(validatorData.ID, &account.Pool{
+ PoolStake: validatorData.ValidatorStake + validatorData.DelegationStake,
+ ValidatorStake: validatorData.ValidatorStake,
+ FixedCost: validatorData.FixedCost,
+ })
+ }
+ s.committee = committeeAccounts.SelectCommittee(committeeAccounts.IDs()...)
+ }
+
+ err := s.committeeStore.Store(epoch, s.committee.Accounts())
+ if err != nil {
+ return nil, ierrors.Wrapf(err, "error while storing committee for epoch %d", epoch)
+ }
+
+ return s.committee, nil
+}
+
+// CommitteeInSlot returns the set of validators selected to be part of the committee in the given slot.
+func (s *SeatManager) CommitteeInSlot(slot iotago.SlotIndex) (*account.SeatedAccounts, bool) {
s.committeeMutex.RLock()
defer s.committeeMutex.RUnlock()
- // we do nothing on PoA, we keep the same accounts and committee
- return s.committee
+ return s.committeeInEpoch(s.apiProvider.APIForSlot(slot).TimeProvider().EpochFromSlot(slot))
}
-// Committee returns the set of validators selected to be part of the committee.
-func (s *SeatManager) Committee(_ iotago.SlotIndex) *account.SeatedAccounts {
+// CommitteeInEpoch returns the set of validators selected to be part of the committee in the given epoch.
+func (s *SeatManager) CommitteeInEpoch(epoch iotago.EpochIndex) (*account.SeatedAccounts, bool) {
s.committeeMutex.RLock()
defer s.committeeMutex.RUnlock()
- // Note: we have PoA so our committee do not rotate right now
- return s.committee
+ return s.committeeInEpoch(epoch)
+}
+
+func (s *SeatManager) committeeInEpoch(epoch iotago.EpochIndex) (*account.SeatedAccounts, bool) {
+ c, err := s.committeeStore.Load(epoch)
+ if err != nil {
+ panic(ierrors.Wrapf(err, "failed to load committee for epoch %d", epoch))
+ }
+
+ if c == nil {
+ return nil, false
+ }
+
+ return c.SelectCommittee(c.IDs()...), true
}
// OnlineCommittee returns the set of validators selected to be part of the committee that has been seen recently.
func (s *SeatManager) OnlineCommittee() ds.Set[account.SeatIndex] {
- s.activityMutex.RLock()
- defer s.activityMutex.RUnlock()
-
- return s.onlineCommittee
+ return s.activityTracker.OnlineCommittee()
}
func (s *SeatManager) SeatCount() int {
@@ -112,76 +149,47 @@ func (s *SeatManager) SeatCount() int {
func (s *SeatManager) Shutdown() {
s.TriggerStopped()
- s.workers.Shutdown()
}
-func (s *SeatManager) ImportCommittee(_ iotago.EpochIndex, validators *account.Accounts) {
+func (s *SeatManager) InitializeCommittee(epoch iotago.EpochIndex, activityTime time.Time) error {
s.committeeMutex.Lock()
defer s.committeeMutex.Unlock()
- s.accounts = validators
- s.committee = s.accounts.SelectCommittee(validators.IDs()...)
+ committeeAccounts, err := s.committeeStore.Load(epoch)
+ if err != nil {
+ return ierrors.Wrapf(err, "failed to load PoA committee for epoch %d", epoch)
+ }
+
+ s.committee = committeeAccounts.SelectCommittee(committeeAccounts.IDs()...)
- onlineValidators := s.accounts.IDs()
+ onlineValidators := committeeAccounts.IDs()
if len(s.optsOnlineCommitteeStartup) > 0 {
onlineValidators = s.optsOnlineCommitteeStartup
}
for _, v := range onlineValidators {
- activityTime := s.clock.Accepted().RelativeTime()
-
seat, exists := s.committee.GetSeat(v)
if !exists {
// Only track identities that are part of the committee.
- return
+ continue
}
- s.markSeatActive(seat, v, activityTime)
+ s.activityTracker.MarkSeatActive(seat, v, activityTime)
}
+
+ return nil
}
-func (s *SeatManager) SetCommittee(_ iotago.EpochIndex, validators *account.Accounts) {
+func (s *SeatManager) SetCommittee(epoch iotago.EpochIndex, validators *account.Accounts) error {
s.committeeMutex.Lock()
defer s.committeeMutex.Unlock()
- s.accounts = validators
- s.committee = s.accounts.SelectCommittee(validators.IDs()...)
-}
-
-func (s *SeatManager) markSeatActive(seat account.SeatIndex, id iotago.AccountID, seatActivityTime time.Time) {
- s.activityMutex.Lock()
- defer s.activityMutex.Unlock()
-
- if lastActivity, exists := s.lastActivities.Get(seat); (exists && lastActivity.After(seatActivityTime)) || seatActivityTime.Before(s.lastActivityTime.Add(-s.optsActivityWindow)) {
- return
- } else if !exists {
- s.onlineCommittee.Add(seat)
- s.events.OnlineCommitteeSeatAdded.Trigger(seat, id)
- }
-
- s.lastActivities.Set(seat, seatActivityTime)
-
- s.inactivityQueue.Push(seat, seatActivityTime)
+ s.committee = validators.SelectCommittee(validators.IDs()...)
- if seatActivityTime.Before(s.lastActivityTime) {
- return
+ err := s.committeeStore.Store(epoch, s.committee.Accounts())
+ if err != nil {
+ return ierrors.Wrapf(err, "failed to set committee for epoch %d", epoch)
}
- s.lastActivityTime = seatActivityTime
-
- activityThreshold := seatActivityTime.Add(-s.optsActivityWindow)
- for _, inactiveSeat := range s.inactivityQueue.PopUntil(activityThreshold) {
- if lastActivityForInactiveSeat, exists := s.lastActivities.Get(inactiveSeat); exists && lastActivityForInactiveSeat.After(activityThreshold) {
- continue
- }
-
- s.markSeatInactive(inactiveSeat)
- }
-}
-
-func (s *SeatManager) markSeatInactive(seat account.SeatIndex) {
- s.lastActivities.Delete(seat)
- s.onlineCommittee.Delete(seat)
-
- s.events.OnlineCommitteeSeatRemoved.Trigger(seat)
+ return nil
}
diff --git a/pkg/protocol/sybilprotection/seatmanager/seatmanager.go b/pkg/protocol/sybilprotection/seatmanager/seatmanager.go
index 32f282569..fe87322c4 100644
--- a/pkg/protocol/sybilprotection/seatmanager/seatmanager.go
+++ b/pkg/protocol/sybilprotection/seatmanager/seatmanager.go
@@ -1,27 +1,33 @@
package seatmanager
import (
+ "time"
+
"github.com/iotaledger/hive.go/ds"
"github.com/iotaledger/hive.go/runtime/module"
"github.com/iotaledger/iota-core/pkg/core/account"
+ "github.com/iotaledger/iota-core/pkg/protocol/engine/accounts"
iotago "github.com/iotaledger/iota.go/v4"
)
// SeatManager is the minimal interface for the SeatManager component of the IOTA protocol.
type SeatManager interface {
// RotateCommittee rotates the committee evaluating the given set of candidates to produce the new committee.
- RotateCommittee(epoch iotago.EpochIndex, candidates *account.Accounts) *account.SeatedAccounts
+ RotateCommittee(epoch iotago.EpochIndex, candidates accounts.AccountsData) (*account.SeatedAccounts, error)
// SetCommittee sets the committee for a given slot.
// This is used when re-using the same committee for consecutive epochs.
- SetCommittee(epoch iotago.EpochIndex, committee *account.Accounts)
+ SetCommittee(epoch iotago.EpochIndex, committee *account.Accounts) error
- // ImportCommittee sets the committee for a given slot and marks the whole committee as active.
+ // InitializeCommittee initializes the committee for the current slot by marking whole or a subset of the committee as active.
// This is used when initializing committee after node startup (loaded from snapshot or database).
- ImportCommittee(epoch iotago.EpochIndex, committee *account.Accounts)
+ InitializeCommittee(epoch iotago.EpochIndex, activityTime time.Time) error
+
+ // CommitteeInSlot returns the set of validators that is used to track confirmation at a given slot.
+ CommitteeInSlot(slot iotago.SlotIndex) (*account.SeatedAccounts, bool)
- // Committee returns the set of validators that is used to track confirmation.
- Committee(slot iotago.SlotIndex) *account.SeatedAccounts
+ // CommitteeInEpoch returns the set of validators that is used to track confirmation in a given epoch.
+ CommitteeInEpoch(epoch iotago.EpochIndex) (*account.SeatedAccounts, bool)
// OnlineCommittee returns the set of online validators that is used to track acceptance.
OnlineCommittee() ds.Set[account.SeatIndex]
diff --git a/pkg/protocol/sybilprotection/seatmanager/topstakers/options.go b/pkg/protocol/sybilprotection/seatmanager/topstakers/options.go
new file mode 100644
index 000000000..4f190f182
--- /dev/null
+++ b/pkg/protocol/sybilprotection/seatmanager/topstakers/options.go
@@ -0,0 +1,27 @@
+package topstakers
+
+import (
+ "time"
+
+ "github.com/iotaledger/hive.go/runtime/options"
+ iotago "github.com/iotaledger/iota.go/v4"
+)
+
+// WithActivityWindow sets the duration for which a validator is recognized as active after issuing a block.
+func WithActivityWindow(activityWindow time.Duration) options.Option[SeatManager] {
+ return func(p *SeatManager) {
+ p.optsActivityWindow = activityWindow
+ }
+}
+
+func WithOnlineCommitteeStartup(optsOnlineCommittee ...iotago.AccountID) options.Option[SeatManager] {
+ return func(p *SeatManager) {
+ p.optsOnlineCommitteeStartup = optsOnlineCommittee
+ }
+}
+
+func WithSeatCount(optsSeatCount uint32) options.Option[SeatManager] {
+ return func(p *SeatManager) {
+ p.optsSeatCount = optsSeatCount
+ }
+}
diff --git a/pkg/protocol/sybilprotection/seatmanager/topstakers/topstakers.go b/pkg/protocol/sybilprotection/seatmanager/topstakers/topstakers.go
new file mode 100644
index 000000000..5d85f85bf
--- /dev/null
+++ b/pkg/protocol/sybilprotection/seatmanager/topstakers/topstakers.go
@@ -0,0 +1,245 @@
+package topstakers
+
+import (
+ "bytes"
+ "sort"
+ "time"
+
+ "github.com/iotaledger/hive.go/ds"
+ "github.com/iotaledger/hive.go/ierrors"
+ "github.com/iotaledger/hive.go/runtime/module"
+ "github.com/iotaledger/hive.go/runtime/options"
+ "github.com/iotaledger/hive.go/runtime/syncutils"
+ "github.com/iotaledger/iota-core/pkg/core/account"
+ "github.com/iotaledger/iota-core/pkg/protocol/engine"
+ "github.com/iotaledger/iota-core/pkg/protocol/engine/accounts"
+ "github.com/iotaledger/iota-core/pkg/protocol/engine/blocks"
+ "github.com/iotaledger/iota-core/pkg/protocol/sybilprotection/activitytracker"
+ "github.com/iotaledger/iota-core/pkg/protocol/sybilprotection/activitytracker/activitytrackerv1"
+ "github.com/iotaledger/iota-core/pkg/protocol/sybilprotection/seatmanager"
+ "github.com/iotaledger/iota-core/pkg/storage/prunable/epochstore"
+ iotago "github.com/iotaledger/iota.go/v4"
+)
+
+// SeatManager is a sybil protection module for the engine that manages the weights of actors according to their stake.
+type SeatManager struct {
+ apiProvider iotago.APIProvider
+ events *seatmanager.Events
+
+ committeeStore *epochstore.Store[*account.Accounts]
+ committeeMutex syncutils.RWMutex
+ activityTracker activitytracker.ActivityTracker
+
+ optsSeatCount uint32
+ optsActivityWindow time.Duration
+ optsOnlineCommitteeStartup []iotago.AccountID
+
+ module.Module
+}
+
+// NewProvider returns a new sybil protection provider that uses the ProofOfStake module.
+func NewProvider(opts ...options.Option[SeatManager]) module.Provider[*engine.Engine, seatmanager.SeatManager] {
+ return module.Provide(func(e *engine.Engine) seatmanager.SeatManager {
+ return options.Apply(
+ &SeatManager{
+ apiProvider: e,
+ events: seatmanager.NewEvents(),
+ committeeStore: e.Storage.Committee(),
+
+ optsActivityWindow: time.Second * 30,
+ }, opts, func(s *SeatManager) {
+ activityTracker := activitytrackerv1.NewActivityTracker(s.optsActivityWindow)
+ s.activityTracker = activityTracker
+ s.events.OnlineCommitteeSeatAdded.LinkTo(activityTracker.Events.OnlineCommitteeSeatAdded)
+ s.events.OnlineCommitteeSeatRemoved.LinkTo(activityTracker.Events.OnlineCommitteeSeatRemoved)
+
+ e.Events.SeatManager.LinkTo(s.events)
+
+ e.HookConstructed(func() {
+ s.TriggerConstructed()
+
+ // We need to mark validators as active upon solidity of blocks as otherwise we would not be able to
+ // recover if no node was part of the online committee anymore.
+ e.Events.CommitmentFilter.BlockAllowed.Hook(func(block *blocks.Block) {
+ // Only track identities that are part of the committee.
+ committee, exists := s.CommitteeInSlot(block.ID().Slot())
+ if !exists {
+ panic(ierrors.Errorf("committee not selected for slot %d, but received block in that slot", block.ID().Slot()))
+ }
+
+ seat, exists := committee.GetSeat(block.ProtocolBlock().IssuerID)
+ if exists {
+ s.activityTracker.MarkSeatActive(seat, block.ProtocolBlock().IssuerID, block.IssuingTime())
+ }
+
+ s.events.BlockProcessed.Trigger(block)
+ })
+ })
+ })
+ })
+}
+
+var _ seatmanager.SeatManager = &SeatManager{}
+
+func (s *SeatManager) RotateCommittee(epoch iotago.EpochIndex, candidates accounts.AccountsData) (*account.SeatedAccounts, error) {
+ s.committeeMutex.Lock()
+ defer s.committeeMutex.Unlock()
+
+ // If there are fewer candidates than required for epoch 0, then the previous committee cannot be copied.
+ if len(candidates) < s.SeatCount() && epoch == 0 {
+ return nil, ierrors.Errorf("at least %d candidates are required for committee in epoch 0, got %d", s.SeatCount(), len(candidates))
+ }
+
+ // If there are fewer candidates than required, then re-use the previous committee.
+ if len(candidates) < s.SeatCount() {
+ // TODO: what if staking period of a committee member ends in the next epoch?
+ committee, exists := s.committeeInEpoch(epoch - 1)
+ if !exists {
+ return nil, ierrors.Errorf("cannot re-use previous committee from epoch %d as it does not exist", epoch-1)
+ }
+
+ err := s.committeeStore.Store(epoch, committee.Accounts())
+ if err != nil {
+ return nil, ierrors.Wrapf(err, "error while storing committee for epoch %d", epoch)
+ }
+
+ return committee, nil
+ }
+
+ committee := s.selectNewCommittee(candidates)
+
+ err := s.committeeStore.Store(epoch, committee.Accounts())
+ if err != nil {
+ return nil, ierrors.Wrapf(err, "error while storing committee for epoch %d", epoch)
+ }
+
+ return committee, nil
+}
+
+// CommitteeInSlot returns the set of validators selected to be part of the committee in the given slot.
+func (s *SeatManager) CommitteeInSlot(slot iotago.SlotIndex) (*account.SeatedAccounts, bool) {
+ s.committeeMutex.RLock()
+ defer s.committeeMutex.RUnlock()
+
+ return s.committeeInEpoch(s.apiProvider.APIForSlot(slot).TimeProvider().EpochFromSlot(slot))
+}
+
+// CommitteeInEpoch returns the set of validators selected to be part of the committee in the given epoch.
+func (s *SeatManager) CommitteeInEpoch(epoch iotago.EpochIndex) (*account.SeatedAccounts, bool) {
+ s.committeeMutex.RLock()
+ defer s.committeeMutex.RUnlock()
+
+ return s.committeeInEpoch(epoch)
+}
+
+func (s *SeatManager) committeeInEpoch(epoch iotago.EpochIndex) (*account.SeatedAccounts, bool) {
+ c, err := s.committeeStore.Load(epoch)
+ if err != nil {
+ panic(ierrors.Wrapf(err, "failed to load committee for epoch %d", epoch))
+ }
+
+ if c == nil {
+ return nil, false
+ }
+
+ return c.SelectCommittee(c.IDs()...), true
+}
+
+// OnlineCommittee returns the set of validators selected to be part of the committee that has been seen recently.
+func (s *SeatManager) OnlineCommittee() ds.Set[account.SeatIndex] {
+ return s.activityTracker.OnlineCommittee()
+}
+
+func (s *SeatManager) SeatCount() int {
+ return int(s.optsSeatCount)
+}
+
+func (s *SeatManager) Shutdown() {
+ s.TriggerStopped()
+}
+
+func (s *SeatManager) InitializeCommittee(epoch iotago.EpochIndex, activityTime time.Time) error {
+ s.committeeMutex.Lock()
+ defer s.committeeMutex.Unlock()
+
+ committeeAccounts, err := s.committeeStore.Load(epoch)
+ if err != nil {
+ return ierrors.Wrapf(err, "failed to load PoA committee for epoch %d", epoch)
+ }
+
+ committee := committeeAccounts.SelectCommittee(committeeAccounts.IDs()...)
+
+ onlineValidators := committeeAccounts.IDs()
+ if len(s.optsOnlineCommitteeStartup) > 0 {
+ onlineValidators = s.optsOnlineCommitteeStartup
+ }
+
+ for _, v := range onlineValidators {
+ seat, exists := committee.GetSeat(v)
+ if !exists {
+ // Only track identities that are part of the committee.
+ continue
+ }
+
+ s.activityTracker.MarkSeatActive(seat, v, activityTime)
+ }
+
+ return nil
+}
+
+func (s *SeatManager) SetCommittee(epoch iotago.EpochIndex, validators *account.Accounts) error {
+ s.committeeMutex.Lock()
+ defer s.committeeMutex.Unlock()
+
+ if validators.Size() != int(s.optsSeatCount) {
+ return ierrors.Errorf("invalid number of validators: %d, expected: %d", validators.Size(), s.optsSeatCount)
+ }
+
+ err := s.committeeStore.Store(epoch, validators)
+ if err != nil {
+ return ierrors.Wrapf(err, "failed to set committee for epoch %d", epoch)
+ }
+
+ return nil
+}
+
+func (s *SeatManager) selectNewCommittee(candidates accounts.AccountsData) *account.SeatedAccounts {
+ sort.Slice(candidates, func(i, j int) bool {
+ // Prioritize the candidate that has a larger pool stake.
+ if candidates[i].ValidatorStake+candidates[i].DelegationStake != candidates[j].ValidatorStake+candidates[j].DelegationStake {
+ return candidates[i].ValidatorStake+candidates[i].DelegationStake > candidates[j].ValidatorStake+candidates[j].DelegationStake
+ }
+
+ // Prioritize the candidate that has a larger validator stake.
+ if candidates[i].ValidatorStake != candidates[j].ValidatorStake {
+ return candidates[i].ValidatorStake > candidates[j].ValidatorStake
+ }
+
+ // Prioritize the candidate that declares a longer staking period.
+ if candidates[i].StakeEndEpoch != candidates[j].StakeEndEpoch {
+ return candidates[i].StakeEndEpoch > candidates[j].StakeEndEpoch
+ }
+
+ // Prioritize the candidate that has smaller FixedCost.
+ if candidates[i].FixedCost != candidates[j].FixedCost {
+ return candidates[i].FixedCost < candidates[j].FixedCost
+ }
+
+ // two candidates never have the same account ID because they come in a map
+ return bytes.Compare(candidates[i].ID[:], candidates[j].ID[:]) > 0
+ })
+
+ // Create new Accounts instance that only included validators selected to be part of the committee.
+ newCommitteeAccounts := account.NewAccounts()
+
+ for _, candidateData := range candidates[:s.optsSeatCount] {
+ newCommitteeAccounts.Set(candidateData.ID, &account.Pool{
+ PoolStake: candidateData.ValidatorStake + candidateData.DelegationStake,
+ ValidatorStake: candidateData.ValidatorStake,
+ FixedCost: candidateData.FixedCost,
+ })
+ }
+ committee := newCommitteeAccounts.SelectCommittee(newCommitteeAccounts.IDs()...)
+
+ return committee
+}
diff --git a/pkg/protocol/sybilprotection/seatmanager/topstakers/topstakers_test.go b/pkg/protocol/sybilprotection/seatmanager/topstakers/topstakers_test.go
new file mode 100644
index 000000000..4b539e72f
--- /dev/null
+++ b/pkg/protocol/sybilprotection/seatmanager/topstakers/topstakers_test.go
@@ -0,0 +1,227 @@
+package topstakers
+
+import (
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+
+ "github.com/iotaledger/hive.go/ds"
+ "github.com/iotaledger/hive.go/kvstore"
+ "github.com/iotaledger/hive.go/kvstore/mapdb"
+ "github.com/iotaledger/hive.go/lo"
+ "github.com/iotaledger/iota-core/pkg/core/account"
+ "github.com/iotaledger/iota-core/pkg/protocol/engine/accounts"
+ "github.com/iotaledger/iota-core/pkg/protocol/sybilprotection/activitytracker/activitytrackerv1"
+ "github.com/iotaledger/iota-core/pkg/protocol/sybilprotection/seatmanager"
+ "github.com/iotaledger/iota-core/pkg/storage/prunable/epochstore"
+ iotago "github.com/iotaledger/iota.go/v4"
+ "github.com/iotaledger/iota.go/v4/api"
+ "github.com/iotaledger/iota.go/v4/tpkg"
+)
+
+func TestTopStakers_InitializeCommittee(t *testing.T) {
+ committeeStore := epochstore.NewStore(kvstore.Realm{}, kvstore.Realm{}, mapdb.NewMapDB(), 0, (*account.Accounts).Bytes, account.AccountsFromBytes)
+
+ topStakersSeatManager := &SeatManager{
+ apiProvider: api.SingleVersionProvider(tpkg.TestAPI),
+ committeeStore: committeeStore,
+ events: seatmanager.NewEvents(),
+ activityTracker: activitytrackerv1.NewActivityTracker(time.Second * 30),
+
+ optsSeatCount: 3,
+ }
+
+ // Create committee for epoch 0
+ initialCommittee := account.NewAccounts()
+ for i := 0; i < 3; i++ {
+ initialCommittee.Set(tpkg.RandAccountID(), &account.Pool{
+ PoolStake: 1900,
+ ValidatorStake: 900,
+ FixedCost: 11,
+ })
+ }
+ // Try setting committee that is too small - should return an error.
+ err := topStakersSeatManager.SetCommittee(0, initialCommittee)
+ require.NoError(t, err)
+ weightedSeats, exists := topStakersSeatManager.CommitteeInEpoch(0)
+ require.True(t, exists)
+ initialCommitteeAccountIDs := initialCommittee.IDs()
+
+ // Make sure that the online committee is handled correctly.
+ require.True(t, topStakersSeatManager.OnlineCommittee().IsEmpty())
+
+ require.NoError(t, topStakersSeatManager.InitializeCommittee(0, time.Time{}))
+ assertOnlineCommittee(t, topStakersSeatManager.OnlineCommittee(), lo.Return1(weightedSeats.GetSeat(initialCommitteeAccountIDs[0])), lo.Return1(weightedSeats.GetSeat(initialCommitteeAccountIDs[2])), lo.Return1(weightedSeats.GetSeat(initialCommitteeAccountIDs[2])))
+
+}
+
+func TestTopStakers_RotateCommittee(t *testing.T) {
+ committeeStore := epochstore.NewStore(kvstore.Realm{}, kvstore.Realm{}, mapdb.NewMapDB(), 0, (*account.Accounts).Bytes, account.AccountsFromBytes)
+
+ topStakersSeatManager := &SeatManager{
+ apiProvider: api.SingleVersionProvider(tpkg.TestAPI),
+ committeeStore: committeeStore,
+ events: seatmanager.NewEvents(),
+ activityTracker: activitytrackerv1.NewActivityTracker(time.Second * 30),
+
+ optsSeatCount: 3,
+ }
+
+ // Committee should not exist because it was never set.
+ _, exists := topStakersSeatManager.CommitteeInSlot(10)
+ require.False(t, exists)
+
+ _, exists = topStakersSeatManager.CommitteeInEpoch(0)
+ require.False(t, exists)
+
+ // Create committee for epoch 0
+ initialCommittee := account.NewAccounts()
+ initialCommittee.Set(tpkg.RandAccountID(), &account.Pool{
+ PoolStake: 1900,
+ ValidatorStake: 900,
+ FixedCost: 11,
+ })
+
+ initialCommittee.Set(tpkg.RandAccountID(), &account.Pool{
+ PoolStake: 1900,
+ ValidatorStake: 900,
+ FixedCost: 11,
+ })
+
+ // Try setting committee that is too small - should return an error.
+ err := topStakersSeatManager.SetCommittee(0, initialCommittee)
+ require.Error(t, err)
+
+ initialCommittee.Set(tpkg.RandAccountID(), &account.Pool{
+ PoolStake: 1900,
+ ValidatorStake: 900,
+ FixedCost: 11,
+ })
+
+ // Set committee with the correct size
+ err = topStakersSeatManager.SetCommittee(0, initialCommittee)
+ require.NoError(t, err)
+ weightedSeats, exists := topStakersSeatManager.CommitteeInEpoch(0)
+ require.True(t, exists)
+ initialCommitteeAccountIDs := initialCommittee.IDs()
+
+ // Make sure that the online committee is handled correctly.
+ require.True(t, topStakersSeatManager.OnlineCommittee().IsEmpty())
+
+ topStakersSeatManager.activityTracker.MarkSeatActive(lo.Return1(weightedSeats.GetSeat(initialCommitteeAccountIDs[0])), initialCommitteeAccountIDs[0], tpkg.TestAPI.TimeProvider().SlotStartTime(1))
+ assertOnlineCommittee(t, topStakersSeatManager.OnlineCommittee(), lo.Return1(weightedSeats.GetSeat(initialCommitteeAccountIDs[0])))
+
+ topStakersSeatManager.activityTracker.MarkSeatActive(lo.Return1(weightedSeats.GetSeat(initialCommitteeAccountIDs[1])), initialCommitteeAccountIDs[1], tpkg.TestAPI.TimeProvider().SlotStartTime(2))
+ assertOnlineCommittee(t, topStakersSeatManager.OnlineCommittee(), lo.Return1(weightedSeats.GetSeat(initialCommitteeAccountIDs[0])), lo.Return1(weightedSeats.GetSeat(initialCommitteeAccountIDs[1])))
+
+ topStakersSeatManager.activityTracker.MarkSeatActive(lo.Return1(weightedSeats.GetSeat(initialCommitteeAccountIDs[2])), initialCommitteeAccountIDs[2], tpkg.TestAPI.TimeProvider().SlotStartTime(3))
+ assertOnlineCommittee(t, topStakersSeatManager.OnlineCommittee(), lo.Return1(weightedSeats.GetSeat(initialCommitteeAccountIDs[0])), lo.Return1(weightedSeats.GetSeat(initialCommitteeAccountIDs[1])), lo.Return1(weightedSeats.GetSeat(initialCommitteeAccountIDs[2])))
+
+ // Make sure that after a period of inactivity, the inactive seats are marked as offline.
+ topStakersSeatManager.activityTracker.MarkSeatActive(lo.Return1(weightedSeats.GetSeat(initialCommitteeAccountIDs[2])), initialCommitteeAccountIDs[2], tpkg.TestAPI.TimeProvider().SlotEndTime(7))
+ assertOnlineCommittee(t, topStakersSeatManager.OnlineCommittee(), lo.Return1(weightedSeats.GetSeat(initialCommitteeAccountIDs[2])))
+
+ // Make sure that the committee was assigned to the correct epoch.
+ _, exists = topStakersSeatManager.CommitteeInEpoch(1)
+ require.False(t, exists)
+
+ // Make sure that the committee members match the expected ones.
+ committee, exists := topStakersSeatManager.CommitteeInEpoch(0)
+ require.True(t, exists)
+ assertCommittee(t, initialCommittee, committee)
+
+ committee, exists = topStakersSeatManager.CommitteeInSlot(3)
+ require.True(t, exists)
+ assertCommittee(t, initialCommittee, committee)
+
+ // Design candidate list and expected committee members.
+ accountsContext := make(accounts.AccountsData, 0)
+ expectedCommittee := account.NewAccounts()
+ numCandidates := 10
+
+ // Add some candidates that have the same fields to test sorting by secondary fields.
+ candidate1ID := tpkg.RandAccountID()
+ accountsContext = append(accountsContext, &accounts.AccountData{
+ ID: candidate1ID,
+ ValidatorStake: 399,
+ DelegationStake: 800 - 399,
+ FixedCost: 3,
+ StakeEndEpoch: iotago.MaxEpochIndex,
+ })
+
+ candidate2ID := tpkg.RandAccountID()
+ accountsContext = append(accountsContext, &accounts.AccountData{
+ ID: candidate2ID,
+ ValidatorStake: 399,
+ DelegationStake: 800 - 399,
+ FixedCost: 3,
+ StakeEndEpoch: iotago.MaxEpochIndex,
+ })
+
+ for i := 1; i <= numCandidates; i++ {
+ candidateAccountID := tpkg.RandAccountID()
+ candidatePool := &account.Pool{
+ PoolStake: iotago.BaseToken(i * 100),
+ ValidatorStake: iotago.BaseToken(i * 50),
+ FixedCost: iotago.Mana(i),
+ }
+ accountsContext = append(accountsContext, &accounts.AccountData{
+ ID: candidateAccountID,
+ ValidatorStake: iotago.BaseToken(i * 50),
+ DelegationStake: iotago.BaseToken(i*100) - iotago.BaseToken(i*50),
+ FixedCost: tpkg.RandMana(iotago.MaxMana),
+ StakeEndEpoch: tpkg.RandEpoch(),
+ })
+
+ if i+topStakersSeatManager.SeatCount() > numCandidates {
+ expectedCommittee.Set(candidateAccountID, candidatePool)
+ }
+ }
+
+ // Rotate the committee and make sure that the returned committee matches the expected.
+ newCommittee, err := topStakersSeatManager.RotateCommittee(1, accountsContext)
+ require.NoError(t, err)
+ assertCommittee(t, expectedCommittee, newCommittee)
+
+ // Make sure that after committee rotation, the online committee is not changed.
+ assertOnlineCommittee(t, topStakersSeatManager.OnlineCommittee(), lo.Return1(weightedSeats.GetSeat(initialCommitteeAccountIDs[2])))
+
+ newCommitteeMemberIDs := newCommittee.Accounts().IDs()
+
+ // A new committee member appears online and makes the previously active committee seat inactive.
+ topStakersSeatManager.activityTracker.MarkSeatActive(lo.Return1(weightedSeats.GetSeat(newCommitteeMemberIDs[0])), newCommitteeMemberIDs[0], tpkg.TestAPI.TimeProvider().SlotEndTime(14))
+ assertOnlineCommittee(t, topStakersSeatManager.OnlineCommittee(), lo.Return1(weightedSeats.GetSeat(newCommitteeMemberIDs[0])))
+
+ // Make sure that the committee retrieved from the committee store matches the expected.
+ committee, exists = topStakersSeatManager.CommitteeInEpoch(1)
+ require.True(t, exists)
+ assertCommittee(t, expectedCommittee, committee)
+
+ committee, exists = topStakersSeatManager.CommitteeInSlot(tpkg.TestAPI.TimeProvider().EpochStart(1))
+ require.True(t, exists)
+ assertCommittee(t, expectedCommittee, committee)
+
+ // Make sure that the previous committee was not modified and is still accessible.
+ committee, exists = topStakersSeatManager.CommitteeInEpoch(0)
+ require.True(t, exists)
+ assertCommittee(t, initialCommittee, committee)
+
+ committee, exists = topStakersSeatManager.CommitteeInSlot(tpkg.TestAPI.TimeProvider().EpochEnd(0))
+ require.True(t, exists)
+ assertCommittee(t, initialCommittee, committee)
+}
+
+func assertCommittee(t *testing.T, expectedCommittee *account.Accounts, actualCommittee *account.SeatedAccounts) {
+ require.Equal(t, actualCommittee.Accounts().Size(), expectedCommittee.Size())
+ for _, memberID := range expectedCommittee.IDs() {
+ require.Truef(t, actualCommittee.Accounts().Has(memberID), "expected account %s to be part of committee, but it is not, actual committee members: %s", memberID, actualCommittee.Accounts().IDs())
+ }
+}
+
+func assertOnlineCommittee(t *testing.T, onlineCommittee ds.Set[account.SeatIndex], expectedOnlineSeats ...account.SeatIndex) {
+ require.Equal(t, onlineCommittee.Size(), len(expectedOnlineSeats))
+ for _, seatIndex := range expectedOnlineSeats {
+ require.Truef(t, onlineCommittee.Has(seatIndex), "expected account %s to be part of committee, but it is not, actual committee members: %s", seatIndex, onlineCommittee)
+ }
+}
diff --git a/pkg/protocol/sybilprotection/sybilprotection.go b/pkg/protocol/sybilprotection/sybilprotection.go
index 4935e4572..74db9aa0b 100644
--- a/pkg/protocol/sybilprotection/sybilprotection.go
+++ b/pkg/protocol/sybilprotection/sybilprotection.go
@@ -12,10 +12,10 @@ import (
)
type SybilProtection interface {
- TrackValidationBlock(block *blocks.Block)
+ TrackBlock(block *blocks.Block)
EligibleValidators(epoch iotago.EpochIndex) (accounts.AccountsData, error)
OrderedRegisteredCandidateValidatorsList(epoch iotago.EpochIndex) ([]*apimodels.ValidatorResponse, error)
- IsCandidateActive(validatorID iotago.AccountID, epoch iotago.EpochIndex) bool
+ IsCandidateActive(validatorID iotago.AccountID, epoch iotago.EpochIndex) (bool, error)
// ValidatorReward returns the amount of mana that a validator has earned in a given epoch range.
// The actual used epoch range is returned, only until usedEnd the decay was applied.
ValidatorReward(validatorID iotago.AccountID, stakeAmount iotago.BaseToken, epochStart, epochEnd iotago.EpochIndex) (validatorReward iotago.Mana, decayedStart, decayedEnd iotago.EpochIndex, err error)
diff --git a/pkg/protocol/sybilprotection/sybilprotectionv1/performance/performance.go b/pkg/protocol/sybilprotection/sybilprotectionv1/performance/performance.go
index 0a2468466..37f061d5d 100644
--- a/pkg/protocol/sybilprotection/sybilprotectionv1/performance/performance.go
+++ b/pkg/protocol/sybilprotection/sybilprotectionv1/performance/performance.go
@@ -5,9 +5,9 @@ import (
"time"
"github.com/iotaledger/hive.go/ds"
+ "github.com/iotaledger/hive.go/ds/shrinkingmap"
"github.com/iotaledger/hive.go/ierrors"
"github.com/iotaledger/hive.go/kvstore"
- "github.com/iotaledger/hive.go/lo"
"github.com/iotaledger/hive.go/runtime/syncutils"
"github.com/iotaledger/iota-core/pkg/core/account"
"github.com/iotaledger/iota-core/pkg/model"
@@ -18,12 +18,13 @@ import (
)
type Tracker struct {
- rewardsStorePerEpochFunc func(epoch iotago.EpochIndex) (kvstore.KVStore, error)
- poolStatsStore *epochstore.Store[*model.PoolsStats]
- committeeStore *epochstore.Store[*account.Accounts]
-
- validatorPerformancesFunc func(slot iotago.SlotIndex) (*slotstore.Store[iotago.AccountID, *model.ValidatorPerformance], error)
- latestAppliedEpoch iotago.EpochIndex
+ rewardsStorePerEpochFunc func(epoch iotago.EpochIndex) (kvstore.KVStore, error)
+ poolStatsStore *epochstore.Store[*model.PoolsStats]
+ committeeStore *epochstore.Store[*account.Accounts]
+ committeeCandidatesInEpochFunc func(epoch iotago.EpochIndex) (kvstore.KVStore, error)
+ nextEpochCommitteeCandidates *shrinkingmap.ShrinkingMap[iotago.AccountID, iotago.SlotIndex]
+ validatorPerformancesFunc func(slot iotago.SlotIndex) (*slotstore.Store[iotago.AccountID, *model.ValidatorPerformance], error)
+ latestAppliedEpoch iotago.EpochIndex
apiProvider iotago.APIProvider
@@ -33,44 +34,39 @@ type Tracker struct {
mutex syncutils.RWMutex
}
-func NewTracker(
- rewardsStorePerEpochFunc func(epoch iotago.EpochIndex) (kvstore.KVStore, error),
- poolStatsStore *epochstore.Store[*model.PoolsStats],
- committeeStore *epochstore.Store[*account.Accounts],
- validatorPerformancesFunc func(slot iotago.SlotIndex) (*slotstore.Store[iotago.AccountID, *model.ValidatorPerformance], error),
- latestAppliedEpoch iotago.EpochIndex,
- apiProvider iotago.APIProvider,
- errHandler func(error),
-) *Tracker {
+func NewTracker(rewardsStorePerEpochFunc func(epoch iotago.EpochIndex) (kvstore.KVStore, error), poolStatsStore *epochstore.Store[*model.PoolsStats], committeeStore *epochstore.Store[*account.Accounts], committeeCandidatesInEpochFunc func(epoch iotago.EpochIndex) (kvstore.KVStore, error), validatorPerformancesFunc func(slot iotago.SlotIndex) (*slotstore.Store[iotago.AccountID, *model.ValidatorPerformance], error), latestAppliedEpoch iotago.EpochIndex, apiProvider iotago.APIProvider, errHandler func(error)) *Tracker {
return &Tracker{
- rewardsStorePerEpochFunc: rewardsStorePerEpochFunc,
- poolStatsStore: poolStatsStore,
- committeeStore: committeeStore,
- validatorPerformancesFunc: validatorPerformancesFunc,
- latestAppliedEpoch: latestAppliedEpoch,
- apiProvider: apiProvider,
- errHandler: errHandler,
+ nextEpochCommitteeCandidates: shrinkingmap.New[iotago.AccountID, iotago.SlotIndex](),
+ rewardsStorePerEpochFunc: rewardsStorePerEpochFunc,
+ poolStatsStore: poolStatsStore,
+ committeeStore: committeeStore,
+ committeeCandidatesInEpochFunc: committeeCandidatesInEpochFunc,
+ validatorPerformancesFunc: validatorPerformancesFunc,
+ latestAppliedEpoch: latestAppliedEpoch,
+ apiProvider: apiProvider,
+ errHandler: errHandler,
}
}
-func (t *Tracker) RegisterCommittee(epoch iotago.EpochIndex, committee *account.Accounts) error {
- return t.committeeStore.Store(epoch, committee)
+func (t *Tracker) ClearCandidates() {
+ // clean the candidate cache stored in memory to make room for candidates in the next epoch
+ t.nextEpochCommitteeCandidates.Clear()
}
func (t *Tracker) TrackValidationBlock(block *blocks.Block) {
+ t.mutex.Lock()
+ defer t.mutex.Unlock()
+
validatorBlock, isValidationBlock := block.ValidationBlock()
if !isValidationBlock {
return
}
- t.mutex.Lock()
- defer t.mutex.Unlock()
-
t.performanceFactorsMutex.Lock()
defer t.performanceFactorsMutex.Unlock()
isCommitteeMember, err := t.isCommitteeMember(block.ID().Slot(), block.ProtocolBlock().IssuerID)
if err != nil {
- t.errHandler(ierrors.Errorf("failed to check if account %s is committee member", block.ProtocolBlock().IssuerID))
+ t.errHandler(ierrors.Wrapf(err, "error while checking if account %s is a committee member in slot %d", block.ProtocolBlock().IssuerID, block.ID().Slot()))
return
}
@@ -80,35 +76,103 @@ func (t *Tracker) TrackValidationBlock(block *blocks.Block) {
}
}
-func (t *Tracker) EligibleValidatorCandidates(epoch iotago.EpochIndex) ds.Set[iotago.AccountID] {
- // TODO: to be implemented for 1.1, for now we just pick previous committee
+func (t *Tracker) TrackCandidateBlock(block *blocks.Block) {
+ t.mutex.Lock()
+ defer t.mutex.Unlock()
- eligible := ds.NewSet[iotago.AccountID]()
+ blockEpoch := t.apiProvider.APIForSlot(block.ID().Slot()).TimeProvider().EpochFromSlot(block.ID().Slot())
- lo.PanicOnErr(t.committeeStore.Load(epoch - 1)).ForEach(func(accountID iotago.AccountID, _ *account.Pool) bool {
- eligible.Add(accountID)
+ if block.Payload().PayloadType() != iotago.PayloadCandidacyAnnouncement {
+ return
+ }
- return true
+ var rollback bool
+ t.nextEpochCommitteeCandidates.Compute(block.ProtocolBlock().IssuerID, func(currentValue iotago.SlotIndex, exists bool) iotago.SlotIndex {
+ if !exists || currentValue > block.ID().Slot() {
+ committeeCandidatesStore, err := t.committeeCandidatesInEpochFunc(blockEpoch)
+ if err != nil {
+ // if there is an error, and we don't register a candidate, then we might eventually create a different commitment
+ t.errHandler(ierrors.Wrapf(err, "error while retrieving candidate storage for epoch %d", blockEpoch))
+
+ // rollback on error if entry did not exist before
+ rollback = !exists
+
+ return currentValue
+ }
+
+ err = committeeCandidatesStore.Set(block.ProtocolBlock().IssuerID[:], block.ID().Slot().MustBytes())
+ if err != nil {
+ // if there is an error, and we don't register a candidate, then we might eventually create a different commitment
+ t.errHandler(ierrors.Wrapf(err, "error while updating candidate activity for epoch %d", blockEpoch))
+
+ // rollback on error if entry did not exist before
+ rollback = !exists
+
+ return currentValue
+ }
+
+ return block.ID().Slot()
+ }
+
+ return currentValue
})
- return eligible
-
- // epochStart := t.apiProvider.APIForEpoch(epoch).TimeProvider().EpochStart(epoch)
- // registeredStore := t.registeredValidatorsFunc(epochStart)
- // eligible := ds.NewSet[iotago.AccountID]()
- // registeredStore.ForEach(func(accountID iotago.AccountID, a *prunable.RegisteredValidatorActivity) bool {
- // if a.Active {
- // eligible.Add(accountID)
- // }
- // return true
- // }
+ // if there was an error when computing the value,
+ // and it was the first entry for the given issuer, then remove the entry
+ if rollback {
+ t.nextEpochCommitteeCandidates.Delete(block.ProtocolBlock().IssuerID)
+ }
+
+}
+
+func (t *Tracker) EligibleValidatorCandidates(epoch iotago.EpochIndex) (ds.Set[iotago.AccountID], error) {
+ t.mutex.RLock()
+ defer t.mutex.RUnlock()
+
+ return t.getValidatorCandidates(epoch)
}
// ValidatorCandidates returns the registered validator candidates for the given epoch.
-func (t *Tracker) ValidatorCandidates(_ iotago.EpochIndex) ds.Set[iotago.AccountID] {
- // TODO: we should choose candidates we tracked performance for no matter if they were active
+func (t *Tracker) ValidatorCandidates(epoch iotago.EpochIndex) (ds.Set[iotago.AccountID], error) {
+ t.mutex.RLock()
+ defer t.mutex.RUnlock()
- return ds.NewSet[iotago.AccountID]()
+ return t.getValidatorCandidates(epoch)
+}
+
+func (t *Tracker) getValidatorCandidates(epoch iotago.EpochIndex) (ds.Set[iotago.AccountID], error) {
+ // we store candidates in the store for the epoch of their activity, but the passed argument points to the target epoch,
+ // so it's necessary to subtract one epoch from the passed value
+ candidateStore, err := t.committeeCandidatesInEpochFunc(epoch - 1)
+ if err != nil {
+ return nil, ierrors.Wrapf(err, "error while retrieving candidates for epoch %d", epoch)
+ }
+
+ candidates := ds.NewSet[iotago.AccountID]()
+
+ var innerErr error
+ err = candidateStore.IterateKeys(kvstore.EmptyPrefix, func(key kvstore.Key) bool {
+ accountID, _, err := iotago.AccountIDFromBytes(key)
+ if err != nil {
+ innerErr = err
+
+ return false
+ }
+
+ candidates.Add(accountID)
+
+ return true
+ })
+
+ if innerErr != nil {
+ return nil, ierrors.Wrapf(innerErr, "error while iterating through candidates for epoch %d", epoch)
+ }
+
+ if err != nil {
+ return nil, ierrors.Wrapf(err, "error while retrieving candidates for epoch %d", epoch)
+ }
+
+ return candidates, nil
}
func (t *Tracker) LoadCommitteeForEpoch(epoch iotago.EpochIndex) (committee *account.Accounts, exists bool) {
@@ -116,6 +180,7 @@ func (t *Tracker) LoadCommitteeForEpoch(epoch iotago.EpochIndex) (committee *acc
if err != nil {
panic(ierrors.Wrapf(err, "failed to load committee for epoch %d", epoch))
}
+
if c == nil {
return nil, false
}
@@ -259,25 +324,31 @@ func (t *Tracker) trackCommitteeMemberPerformance(validationBlock *iotago.Valida
validatorPerformance, err := validatorPerformances.Load(block.ProtocolBlock().IssuerID)
if err != nil {
t.errHandler(ierrors.Errorf("failed to load performance factor for account %s", block.ProtocolBlock().IssuerID))
+
+ return
}
+
// key not found
if validatorPerformance == nil {
validatorPerformance = model.NewValidatorPerformance()
}
- // set bit at subslotIndex to 1 to indicate activity in that subslot
+ // set a bit at subslotIndex to 1 to indicate activity in that subslot
validatorPerformance.SlotActivityVector = validatorPerformance.SlotActivityVector | (1 << t.subslotIndex(block.ID().Slot(), block.ProtocolBlock().IssuingTime))
apiForSlot := t.apiProvider.APIForSlot(block.ID().Slot())
+
// we restrict the number up to ValidatorBlocksPerSlot + 1 to know later if the validator issued more blocks than allowed and be able to punish for it
// also it can fint into uint8
if validatorPerformance.BlockIssuedCount < apiForSlot.ProtocolParameters().ValidationBlocksPerSlot()+1 {
validatorPerformance.BlockIssuedCount++
}
+
validatorPerformance.HighestSupportedVersionAndHash = model.VersionAndHash{
Version: validationBlock.HighestSupportedVersion,
Hash: validationBlock.ProtocolParametersHash,
}
+
if err = validatorPerformances.Store(block.ProtocolBlock().IssuerID, validatorPerformance); err != nil {
t.errHandler(ierrors.Errorf("failed to store performance factor for account %s", block.ProtocolBlock().IssuerID))
}
diff --git a/pkg/protocol/sybilprotection/sybilprotectionv1/performance/testsuite_test.go b/pkg/protocol/sybilprotection/sybilprotectionv1/performance/testsuite_test.go
index 5d9c9ce5d..6615023d4 100644
--- a/pkg/protocol/sybilprotection/sybilprotectionv1/performance/testsuite_test.go
+++ b/pkg/protocol/sybilprotection/sybilprotectionv1/performance/testsuite_test.go
@@ -73,8 +73,18 @@ func (t *TestSuite) InitPerformanceTracker() {
rewardsStore := epochstore.NewEpochKVStore(kvstore.Realm{}, kvstore.Realm{}, mapdb.NewMapDB(), 0)
poolStatsStore := epochstore.NewStore(kvstore.Realm{}, kvstore.Realm{}, mapdb.NewMapDB(), 0, (*model.PoolsStats).Bytes, model.PoolsStatsFromBytes)
committeeStore := epochstore.NewStore(kvstore.Realm{}, kvstore.Realm{}, mapdb.NewMapDB(), 0, (*account.Accounts).Bytes, account.AccountsFromBytes)
-
- t.Instance = NewTracker(rewardsStore.GetEpoch, poolStatsStore, committeeStore, performanceFactorFunc, t.latestCommittedEpoch, api.SingleVersionProvider(t.api), func(err error) {})
+ committeeCandidatesStore := epochstore.NewEpochKVStore(kvstore.Realm{}, kvstore.Realm{}, mapdb.NewMapDB(), 0)
+
+ t.Instance = NewTracker(
+ rewardsStore.GetEpoch,
+ poolStatsStore,
+ committeeStore,
+ committeeCandidatesStore.GetEpoch,
+ performanceFactorFunc,
+ t.latestCommittedEpoch,
+ api.SingleVersionProvider(t.api),
+ func(err error) {},
+ )
}
func (t *TestSuite) Account(alias string, createIfNotExists bool) iotago.AccountID {
@@ -103,7 +113,8 @@ func (t *TestSuite) ApplyEpochActions(epoch iotago.EpochIndex, actions map[strin
})
}
- err := t.Instance.RegisterCommittee(epoch, committee)
+ // Store directly on the committee store, because in actual code the SeatManager is responsible for adding the storage entry.
+ err := t.Instance.committeeStore.Store(epoch, committee)
require.NoError(t.T, err)
for accIDAlias, action := range actions {
accID := t.Account(accIDAlias, false)
diff --git a/pkg/protocol/sybilprotection/sybilprotectionv1/performance/tracker_test.go b/pkg/protocol/sybilprotection/sybilprotectionv1/performance/tracker_test.go
index 46033f793..49a1a95bf 100644
--- a/pkg/protocol/sybilprotection/sybilprotectionv1/performance/tracker_test.go
+++ b/pkg/protocol/sybilprotection/sybilprotectionv1/performance/tracker_test.go
@@ -3,7 +3,14 @@ package performance
import (
"testing"
+ "github.com/stretchr/testify/require"
+
+ "github.com/iotaledger/hive.go/ds"
+ "github.com/iotaledger/hive.go/lo"
+ "github.com/iotaledger/iota-core/pkg/model"
+ "github.com/iotaledger/iota-core/pkg/protocol/engine/blocks"
iotago "github.com/iotaledger/iota.go/v4"
+ "github.com/iotaledger/iota.go/v4/tpkg"
)
func TestManager_Rewards(t *testing.T) {
@@ -80,5 +87,77 @@ func TestManager_Rewards(t *testing.T) {
ts.AssertRewardForDelegatorsOnly("D", epoch, epochActions)
// test the epoch after initial phase
+}
+
+func TestManager_Candidates(t *testing.T) {
+ ts := NewTestSuite(t)
+
+ issuer1 := tpkg.RandAccountID()
+ issuer2 := tpkg.RandAccountID()
+ issuer3 := tpkg.RandAccountID()
+ {
+ block1 := tpkg.RandProtocolBlock(tpkg.RandBasicBlock(ts.api, iotago.PayloadCandidacyAnnouncement), ts.api, 0)
+
+ block1.IssuingTime = ts.api.TimeProvider().SlotStartTime(1)
+ block1.IssuerID = issuer1
+
+ block2 := tpkg.RandProtocolBlock(tpkg.RandBasicBlock(ts.api, iotago.PayloadCandidacyAnnouncement), ts.api, 0)
+
+ block2.IssuingTime = ts.api.TimeProvider().SlotStartTime(2)
+ block2.IssuerID = issuer2
+
+ block3 := tpkg.RandProtocolBlock(tpkg.RandBasicBlock(ts.api, iotago.PayloadCandidacyAnnouncement), ts.api, 0)
+
+ block3.IssuingTime = ts.api.TimeProvider().SlotStartTime(3)
+ block3.IssuerID = issuer3
+
+ ts.Instance.TrackCandidateBlock(blocks.NewBlock(lo.PanicOnErr(model.BlockFromBlock(block1))))
+ ts.Instance.TrackCandidateBlock(blocks.NewBlock(lo.PanicOnErr(model.BlockFromBlock(block2))))
+ ts.Instance.TrackCandidateBlock(blocks.NewBlock(lo.PanicOnErr(model.BlockFromBlock(block3))))
+ }
+
+ {
+ block4 := tpkg.RandProtocolBlock(tpkg.RandBasicBlock(ts.api, iotago.PayloadCandidacyAnnouncement), ts.api, 0)
+ block4.IssuingTime = ts.api.TimeProvider().SlotStartTime(4)
+ block4.IssuerID = issuer1
+
+ block5 := tpkg.RandProtocolBlock(tpkg.RandBasicBlock(ts.api, iotago.PayloadCandidacyAnnouncement), ts.api, 0)
+
+ block5.IssuingTime = ts.api.TimeProvider().SlotStartTime(5)
+ block5.IssuerID = issuer2
+
+ block6 := tpkg.RandProtocolBlock(tpkg.RandBasicBlock(ts.api, iotago.PayloadCandidacyAnnouncement), ts.api, 0)
+
+ block6.IssuingTime = ts.api.TimeProvider().SlotStartTime(6)
+ block6.IssuerID = issuer3
+
+ ts.Instance.TrackCandidateBlock(blocks.NewBlock(lo.PanicOnErr(model.BlockFromBlock(block4))))
+ ts.Instance.TrackCandidateBlock(blocks.NewBlock(lo.PanicOnErr(model.BlockFromBlock(block5))))
+ ts.Instance.TrackCandidateBlock(blocks.NewBlock(lo.PanicOnErr(model.BlockFromBlock(block6))))
+ }
+
+ require.True(t, lo.PanicOnErr(ts.Instance.EligibleValidatorCandidates(1)).HasAll(ds.NewReadableSet(issuer1, issuer2, issuer3)))
+ require.True(t, lo.PanicOnErr(ts.Instance.ValidatorCandidates(1)).HasAll(ds.NewReadableSet(issuer1, issuer2, issuer3)))
+ require.True(t, lo.PanicOnErr(ts.Instance.EligibleValidatorCandidates(2)).IsEmpty())
+ require.True(t, lo.PanicOnErr(ts.Instance.ValidatorCandidates(2)).IsEmpty())
+
+ // retrieve epoch candidates for epoch 0, because we candidates prefixed with epoch in which they candidated
+ candidatesStore, err := ts.Instance.committeeCandidatesInEpochFunc(0)
+ require.NoError(t, err)
+
+ candidacySlotIssuer1, err := candidatesStore.Get(issuer1[:])
+ require.NoError(t, err)
+ require.Equal(t, iotago.SlotIndex(1).MustBytes(), candidacySlotIssuer1)
+
+ candidacySlotIssuer2, err := candidatesStore.Get(issuer2[:])
+ require.NoError(t, err)
+ require.Equal(t, iotago.SlotIndex(2).MustBytes(), candidacySlotIssuer2)
+
+ candidacySlotIssuer3, err := candidatesStore.Get(issuer3[:])
+ require.NoError(t, err)
+ require.Equal(t, iotago.SlotIndex(3).MustBytes(), candidacySlotIssuer3)
+
+ ts.Instance.ClearCandidates()
+ require.True(t, ts.Instance.nextEpochCommitteeCandidates.IsEmpty())
}
diff --git a/pkg/protocol/sybilprotection/sybilprotectionv1/sybilprotection.go b/pkg/protocol/sybilprotection/sybilprotectionv1/sybilprotection.go
index 01ac56ae7..1e15ec4e2 100644
--- a/pkg/protocol/sybilprotection/sybilprotectionv1/sybilprotection.go
+++ b/pkg/protocol/sybilprotection/sybilprotectionv1/sybilprotection.go
@@ -37,7 +37,7 @@ type SybilProtection struct {
errHandler func(error)
- optsInitialCommittee *account.Accounts
+ optsInitialCommittee accounts.AccountsData
optsSeatManagerProvider module.Provider[*engine.Engine, seatmanager.SeatManager]
mutex syncutils.Mutex
@@ -62,34 +62,25 @@ func NewProvider(opts ...options.Option[SybilProtection]) module.Provider[*engin
latestCommittedSlot := e.Storage.Settings().LatestCommitment().Slot()
latestCommittedEpoch := o.apiProvider.APIForSlot(latestCommittedSlot).TimeProvider().EpochFromSlot(latestCommittedSlot)
- o.performanceTracker = performance.NewTracker(e.Storage.RewardsForEpoch, e.Storage.PoolStats(), e.Storage.Committee(), e.Storage.ValidatorPerformances, latestCommittedEpoch, e, o.errHandler)
+ o.performanceTracker = performance.NewTracker(e.Storage.RewardsForEpoch, e.Storage.PoolStats(), e.Storage.Committee(), e.Storage.CommitteeCandidates, e.Storage.ValidatorPerformances, latestCommittedEpoch, e, o.errHandler)
o.lastCommittedSlot = latestCommittedSlot
if o.optsInitialCommittee != nil {
- if err := o.performanceTracker.RegisterCommittee(0, o.optsInitialCommittee); err != nil {
+ if _, err := o.seatManager.RotateCommittee(0, o.optsInitialCommittee); err != nil {
panic(ierrors.Wrap(err, "error while registering initial committee for epoch 0"))
}
}
+
o.TriggerConstructed()
// When the engine is triggered initialized, snapshot has been read or database has been initialized properly,
// so the committee should be available in the performance manager.
e.HookInitialized(func() {
- // Make sure that the sybil protection knows about the committee of the current epoch
- // (according to the latest committed slot), and potentially the next selected
- // committee if we have one.
-
- currentEpoch := e.LatestAPI().TimeProvider().EpochFromSlot(e.Storage.Settings().LatestCommitment().Slot())
-
- committee, exists := o.performanceTracker.LoadCommitteeForEpoch(currentEpoch)
- if !exists {
- panic("failed to load committee for last finalized slot to initialize sybil protection")
- }
- o.seatManager.ImportCommittee(currentEpoch, committee)
- fmt.Println("committee import", committee.TotalStake(), currentEpoch)
- if nextCommittee, nextCommitteeExists := o.performanceTracker.LoadCommitteeForEpoch(currentEpoch + 1); nextCommitteeExists {
- o.seatManager.ImportCommittee(currentEpoch+1, nextCommittee)
- fmt.Println("next committee", nextCommittee.TotalStake(), currentEpoch+1)
+ // Mark the committee for the last committed slot as active.
+ currentEpoch := e.CommittedAPI().TimeProvider().EpochFromSlot(e.Storage.Settings().LatestCommitment().Slot())
+ err := o.seatManager.InitializeCommittee(currentEpoch, e.Clock.Accepted().RelativeTime())
+ if err != nil {
+ panic(ierrors.Wrap(err, "error while initializing committee"))
}
o.TriggerInitialized()
@@ -108,8 +99,43 @@ func (o *SybilProtection) Shutdown() {
o.TriggerStopped()
}
-func (o *SybilProtection) TrackValidationBlock(block *blocks.Block) {
- o.performanceTracker.TrackValidationBlock(block)
+func (o *SybilProtection) TrackBlock(block *blocks.Block) {
+ if _, isValidationBlock := block.ValidationBlock(); isValidationBlock {
+ o.performanceTracker.TrackValidationBlock(block)
+
+ return
+ }
+
+ accountData, exists, err := o.ledger.Account(block.ProtocolBlock().IssuerID, block.SlotCommitmentID().Slot())
+ if err != nil {
+ o.errHandler(ierrors.Wrapf(err, "error while retrieving account from account %s in slot %d from accounts ledger", block.ProtocolBlock().IssuerID, block.SlotCommitmentID().Slot()))
+
+ return
+ }
+
+ if !exists {
+ return
+ }
+
+ blockEpoch := o.apiProvider.APIForSlot(block.ID().Slot()).TimeProvider().EpochFromSlot(block.ID().Slot())
+
+ // if the block is issued before the stake end epoch, then it's not a valid validator or candidate block
+ if accountData.StakeEndEpoch < blockEpoch {
+ return
+ }
+
+ // if a candidate block is issued in the stake end epoch,
+ // or if block is issued after EpochEndSlot - EpochNearingThreshold, because candidates can register only until that point.
+ // then don't consider it because the validator can't be part of the committee in the next epoch
+ if accountData.StakeEndEpoch == blockEpoch ||
+ block.ID().Slot()+o.apiProvider.APIForSlot(block.ID().Slot()).ProtocolParameters().EpochNearingThreshold() > o.apiProvider.APIForSlot(block.ID().Slot()).TimeProvider().EpochEnd(blockEpoch) {
+
+ return
+ }
+
+ if block.Payload().PayloadType() == iotago.PayloadCandidacyAnnouncement {
+ o.performanceTracker.TrackCandidateBlock(block)
+ }
}
func (o *SybilProtection) CommitSlot(slot iotago.SlotIndex) (committeeRoot, rewardsRoot iotago.Identifier, err error) {
@@ -126,23 +152,23 @@ func (o *SybilProtection) CommitSlot(slot iotago.SlotIndex) (committeeRoot, rewa
// If the committed slot is `maxCommittableSlot`
// away from the end of the epoch, then register a committee for the next epoch.
if timeProvider.EpochEnd(currentEpoch) == slot+maxCommittableAge {
- if _, committeeExists := o.performanceTracker.LoadCommitteeForEpoch(nextEpoch); !committeeExists {
+ if _, committeeExists := o.seatManager.CommitteeInEpoch(nextEpoch); !committeeExists {
// If the committee for the epoch wasn't set before due to finalization of a slot,
// we promote the current committee to also serve in the next epoch.
- committee, exists := o.performanceTracker.LoadCommitteeForEpoch(currentEpoch)
+ committee, exists := o.seatManager.CommitteeInEpoch(currentEpoch)
if !exists {
// that should never happen as it is already the fallback strategy
panic(fmt.Sprintf("committee for current epoch %d not found", currentEpoch))
}
- committee.SetReused()
- o.seatManager.SetCommittee(nextEpoch, committee)
-
- o.events.CommitteeSelected.Trigger(committee, nextEpoch)
-
- if err = o.performanceTracker.RegisterCommittee(nextEpoch, committee); err != nil {
- return iotago.Identifier{}, iotago.Identifier{}, ierrors.Wrapf(err, "failed to register committee for epoch %d", nextEpoch)
+ committeeAccounts := committee.Accounts()
+ committeeAccounts.SetReused()
+ if err = o.seatManager.SetCommittee(nextEpoch, committeeAccounts); err != nil {
+ return iotago.Identifier{}, iotago.Identifier{}, ierrors.Wrapf(err, "failed to set committee for epoch %d", nextEpoch)
}
+ o.performanceTracker.ClearCandidates()
+
+ o.events.CommitteeSelected.Trigger(committeeAccounts, nextEpoch)
}
}
@@ -191,7 +217,7 @@ func (o *SybilProtection) CommitSlot(slot iotago.SlotIndex) (committeeRoot, rewa
func (o *SybilProtection) committeeRoot(targetCommitteeEpoch iotago.EpochIndex) (committeeRoot iotago.Identifier, err error) {
committee, exists := o.performanceTracker.LoadCommitteeForEpoch(targetCommitteeEpoch)
if !exists {
- return iotago.Identifier{}, ierrors.Wrapf(err, "committee for a finished epoch %d not found", targetCommitteeEpoch)
+ return iotago.Identifier{}, ierrors.Wrapf(err, "committee for an epoch %d not found", targetCommitteeEpoch)
}
committeeTree := ads.NewSet[iotago.Identifier](
@@ -251,23 +277,30 @@ func (o *SybilProtection) slotFinalized(slot iotago.SlotIndex) {
if slot+apiForSlot.ProtocolParameters().EpochNearingThreshold() == epochEndSlot &&
epochEndSlot > o.lastCommittedSlot+apiForSlot.ProtocolParameters().MaxCommittableAge() {
newCommittee := o.selectNewCommittee(slot)
- fmt.Println("new committee selection finalization", epoch, newCommittee.TotalStake(), newCommittee.TotalValidatorStake())
o.events.CommitteeSelected.Trigger(newCommittee, epoch+1)
}
}
// IsCandidateActive returns true if the given validator is currently active.
-func (o *SybilProtection) IsCandidateActive(validatorID iotago.AccountID, epoch iotago.EpochIndex) bool {
- activeCandidates := o.performanceTracker.EligibleValidatorCandidates(epoch)
- return activeCandidates.Has(validatorID)
+func (o *SybilProtection) IsCandidateActive(validatorID iotago.AccountID, epoch iotago.EpochIndex) (bool, error) {
+ activeCandidates, err := o.performanceTracker.EligibleValidatorCandidates(epoch)
+ if err != nil {
+ return false, ierrors.Wrapf(err, "failed to retrieve eligible candidates")
+ }
+
+ return activeCandidates.Has(validatorID), nil
}
// EligibleValidators returns the currently known list of recently active validator candidates for the given epoch.
func (o *SybilProtection) EligibleValidators(epoch iotago.EpochIndex) (accounts.AccountsData, error) {
- candidates := o.performanceTracker.EligibleValidatorCandidates(epoch)
+ candidates, err := o.performanceTracker.EligibleValidatorCandidates(epoch)
+ if err != nil {
+ return nil, ierrors.Wrapf(err, "failed to retrieve eligible validator candidates for epoch %d", epoch)
+ }
+
validators := make(accounts.AccountsData, 0)
- if err := candidates.ForEach(func(candidate iotago.AccountID) error {
+ if err = candidates.ForEach(func(candidate iotago.AccountID) error {
accountData, exists, err := o.ledger.Account(candidate, o.lastCommittedSlot)
if err != nil {
return ierrors.Wrapf(err, "failed to load account data for candidate %s", candidate)
@@ -291,8 +324,15 @@ func (o *SybilProtection) EligibleValidators(epoch iotago.EpochIndex) (accounts.
// OrderedRegisteredCandidateValidatorsList returns the currently known list of registered validator candidates for the given epoch.
func (o *SybilProtection) OrderedRegisteredCandidateValidatorsList(epoch iotago.EpochIndex) ([]*apimodels.ValidatorResponse, error) {
- candidates := o.performanceTracker.ValidatorCandidates(epoch)
- activeCandidates := o.performanceTracker.EligibleValidatorCandidates(epoch)
+ candidates, err := o.performanceTracker.ValidatorCandidates(epoch)
+ if err != nil {
+ return nil, ierrors.Wrapf(err, "failed to retrieve candidates")
+ }
+
+ activeCandidates, err := o.performanceTracker.EligibleValidatorCandidates(epoch)
+ if err != nil {
+ return nil, ierrors.Wrapf(err, "failed to retrieve eligible candidates")
+ }
validatorResp := make([]*apimodels.ValidatorResponse, 0, candidates.Size())
if err := candidates.ForEach(func(candidate iotago.AccountID) error {
@@ -335,43 +375,41 @@ func (o *SybilProtection) selectNewCommittee(slot iotago.SlotIndex) *account.Acc
timeProvider := o.apiProvider.APIForSlot(slot).TimeProvider()
currentEpoch := timeProvider.EpochFromSlot(slot)
nextEpoch := currentEpoch + 1
- candidates := o.performanceTracker.EligibleValidatorCandidates(nextEpoch)
+ candidates, err := o.performanceTracker.EligibleValidatorCandidates(nextEpoch)
+ if err != nil {
+ panic(ierrors.Wrapf(err, "failed to retrieve candidates for epoch %d", nextEpoch))
+ }
- weightedCandidates := account.NewAccounts()
+ candidateAccounts := make(accounts.AccountsData, 0)
if err := candidates.ForEach(func(candidate iotago.AccountID) error {
- a, exists, err := o.ledger.Account(candidate, slot)
+ accountData, exists, err := o.ledger.Account(candidate, slot)
if err != nil {
return err
}
if !exists {
- o.errHandler(ierrors.Errorf("account of committee candidate does not exist: %s", candidate))
+ return ierrors.Errorf("account of committee candidate %s does not exist in slot %d", candidate, slot)
}
- weightedCandidates.Set(candidate, &account.Pool{
- PoolStake: a.ValidatorStake + a.DelegationStake,
- ValidatorStake: a.ValidatorStake,
- FixedCost: a.FixedCost,
- })
+ candidateAccounts = append(candidateAccounts, accountData)
return nil
}); err != nil {
- o.errHandler(err)
+ panic(ierrors.Wrap(err, "failed to iterate through candidates"))
}
- newCommittee := o.seatManager.RotateCommittee(nextEpoch, weightedCandidates)
- weightedCommittee := newCommittee.Accounts()
-
- err := o.performanceTracker.RegisterCommittee(nextEpoch, weightedCommittee)
+ newCommittee, err := o.seatManager.RotateCommittee(nextEpoch, candidateAccounts)
if err != nil {
- o.errHandler(ierrors.Wrap(err, "failed to register committee for epoch"))
+ panic(ierrors.Wrap(err, "failed to rotate committee"))
}
- return weightedCommittee
+ o.performanceTracker.ClearCandidates()
+
+ return newCommittee.Accounts()
}
// WithInitialCommittee registers the passed committee on a given slot.
// This is needed to generate Genesis snapshot with some initial committee.
-func WithInitialCommittee(committee *account.Accounts) options.Option[SybilProtection] {
+func WithInitialCommittee(committee accounts.AccountsData) options.Option[SybilProtection] {
return func(o *SybilProtection) {
o.optsInitialCommittee = committee
}
diff --git a/pkg/retainer/retainer/errors.go b/pkg/retainer/retainer/errors.go
index 9ef4908a0..88d9594f6 100644
--- a/pkg/retainer/retainer/errors.go
+++ b/pkg/retainer/retainer/errors.go
@@ -62,6 +62,14 @@ var txErrorsFailureReasonMap = map[error]apimodels.TransactionFailureReason{
iotago.ErrInputOutputManaMismatch: apimodels.TxFailureManaAmountInvalid,
iotago.ErrManaAmountInvalid: apimodels.TxFailureManaAmountInvalid,
iotago.ErrInputCreationAfterTxCreation: apimodels.TxFailureInputCreationAfterTxCreation,
+
+ // tx capabilities errors
+ iotago.ErrTxCapabilitiesNativeTokenBurningNotAllowed: apimodels.TxFailureCapabilitiesNativeTokenBurningNotAllowed,
+ iotago.ErrTxCapabilitiesManaBurningNotAllowed: apimodels.TxFailureCapabilitiesManaBurningNotAllowed,
+ iotago.ErrTxCapabilitiesAccountDestructionNotAllowed: apimodels.TxFailureCapabilitiesAccountDestructionNotAllowed,
+ iotago.ErrTxCapabilitiesAnchorDestructionNotAllowed: apimodels.TxFailureCapabilitiesAnchorDestructionNotAllowed,
+ iotago.ErrTxCapabilitiesFoundryDestructionNotAllowed: apimodels.TxFailureCapabilitiesFoundryDestructionNotAllowed,
+ iotago.ErrTxCapabilitiesNFTDestructionNotAllowed: apimodels.TxFailureCapabilitiesNFTDestructionNotAllowed,
}
func determineTxFailureReason(err error) apimodels.TransactionFailureReason {
diff --git a/pkg/storage/prunable/prunable.go b/pkg/storage/prunable/prunable.go
index 1fc392877..9e4396539 100644
--- a/pkg/storage/prunable/prunable.go
+++ b/pkg/storage/prunable/prunable.go
@@ -155,6 +155,10 @@ func (p *Prunable) Rollback(targetSlot iotago.SlotIndex) error {
return ierrors.Wrapf(err, "error while rolling back slots in a bucket for epoch %d", targetSlotEpoch)
}
+ if err := p.rollbackCommitteesCandidates(targetSlotEpoch, targetSlot); err != nil {
+ return ierrors.Wrapf(err, "error while rolling back committee for epoch %d", targetSlotEpoch)
+ }
+
// Shut down the prunableSlotStore in order to flush and get consistent state on disk after reopening.
p.prunableSlotStore.Shutdown()
@@ -218,3 +222,49 @@ func (p *Prunable) shouldRollbackCommittee(epoch iotago.EpochIndex, targetSlot i
return true, nil
}
+
+func (p *Prunable) rollbackCommitteesCandidates(targetSlotEpoch iotago.EpochIndex, targetSlot iotago.SlotIndex) error {
+ candidatesToRollback := make([]iotago.AccountID, 0)
+
+ candidates, err := p.CommitteeCandidates(targetSlotEpoch)
+ if err != nil {
+ return ierrors.Wrap(err, "failed to get candidates store")
+ }
+
+ var innerErr error
+ if err = candidates.Iterate(kvstore.EmptyPrefix, func(key kvstore.Key, value kvstore.Value) bool {
+ accountID, _, err := iotago.AccountIDFromBytes(key)
+ if err != nil {
+ innerErr = err
+
+ return false
+ }
+
+ candidacySlot, _, err := iotago.SlotIndexFromBytes(value)
+ if err != nil {
+ innerErr = err
+
+ return false
+ }
+
+ if candidacySlot < targetSlot {
+ candidatesToRollback = append(candidatesToRollback, accountID)
+ }
+
+ return true
+ }); err != nil {
+ return ierrors.Wrap(err, "failed to collect candidates to rollback")
+ }
+
+ if innerErr != nil {
+ return ierrors.Wrap(innerErr, "failed to iterate through candidates")
+ }
+
+ for _, candidateToRollback := range candidatesToRollback {
+ if err = candidates.Delete(candidateToRollback[:]); err != nil {
+ return ierrors.Wrapf(innerErr, "failed to rollback candidate %s", candidateToRollback)
+ }
+ }
+
+ return nil
+}
diff --git a/pkg/storage/prunable/prunable_slot.go b/pkg/storage/prunable/prunable_slot.go
index a7632ca3c..0549278b8 100644
--- a/pkg/storage/prunable/prunable_slot.go
+++ b/pkg/storage/prunable/prunable_slot.go
@@ -21,6 +21,7 @@ const (
slotPrefixUpgradeSignals
slotPrefixRoots
slotPrefixRetainer
+ epochPrefixCommitteeCandidates
)
func (p *Prunable) getKVStoreFromSlot(slot iotago.SlotIndex, prefix kvstore.Realm) (kvstore.KVStore, error) {
@@ -52,6 +53,12 @@ func (p *Prunable) RootBlocks(slot iotago.SlotIndex) (*slotstore.Store[iotago.Bl
), nil
}
+func (p *Prunable) CommitteeCandidates(epoch iotago.EpochIndex) (kvstore.KVStore, error) {
+ // Use the first slot of an epoch to avoid random clashes with other keys.
+ // Candidates belong to an epoch, but we store them here so that they're pruned more quickly and easily without unnecessary key iteration.
+ return p.prunableSlotStore.Get(epoch, byteutils.ConcatBytes(p.apiProvider.APIForEpoch(epoch).TimeProvider().EpochStart(epoch).MustBytes(), kvstore.Realm{epochPrefixCommitteeCandidates}))
+}
+
func (p *Prunable) Mutations(slot iotago.SlotIndex) (kvstore.KVStore, error) {
return p.getKVStoreFromSlot(slot, kvstore.Realm{slotPrefixMutations})
}
diff --git a/pkg/storage/storage_prunable.go b/pkg/storage/storage_prunable.go
index a65cd9406..5403484d9 100644
--- a/pkg/storage/storage_prunable.go
+++ b/pkg/storage/storage_prunable.go
@@ -29,6 +29,10 @@ func (s *Storage) Committee() *epochstore.Store[*account.Accounts] {
return s.prunable.Committee()
}
+func (s *Storage) CommitteeCandidates(epoch iotago.EpochIndex) (kvstore.KVStore, error) {
+ return s.prunable.CommitteeCandidates(epoch)
+}
+
func (s *Storage) Blocks(slot iotago.SlotIndex) (*slotstore.Blocks, error) {
return s.prunable.Blocks(slot)
}
diff --git a/pkg/tests/committee_rotation_test.go b/pkg/tests/committee_rotation_test.go
new file mode 100644
index 000000000..56b4f397d
--- /dev/null
+++ b/pkg/tests/committee_rotation_test.go
@@ -0,0 +1,115 @@
+package tests
+
+import (
+ "testing"
+
+ "github.com/iotaledger/hive.go/runtime/options"
+ "github.com/iotaledger/iota-core/pkg/protocol"
+ "github.com/iotaledger/iota-core/pkg/protocol/snapshotcreator"
+ "github.com/iotaledger/iota-core/pkg/protocol/sybilprotection/seatmanager/topstakers"
+ "github.com/iotaledger/iota-core/pkg/protocol/sybilprotection/sybilprotectionv1"
+ "github.com/iotaledger/iota-core/pkg/testsuite"
+ iotago "github.com/iotaledger/iota.go/v4"
+)
+
+func Test_TopStakersRotation(t *testing.T) {
+ ts := testsuite.NewTestSuite(t,
+ testsuite.WithProtocolParametersOptions(
+ iotago.WithTimeProviderOptions(
+ testsuite.GenesisTimeWithOffsetBySlots(1000, testsuite.DefaultSlotDurationInSeconds),
+ testsuite.DefaultSlotDurationInSeconds,
+ 4,
+ ),
+ iotago.WithLivenessOptions(
+ 10,
+ 10,
+ 3,
+ 4,
+ 5,
+ ),
+ ),
+ testsuite.WithSnapshotOptions(
+ snapshotcreator.WithSeatManagerProvider(
+ topstakers.NewProvider(
+ topstakers.WithSeatCount(3),
+ ),
+ ),
+ ),
+ )
+ defer ts.Shutdown()
+
+ ts.AddValidatorNode("node1", 1_000_006)
+ ts.AddValidatorNode("node2", 1_000_005)
+ ts.AddValidatorNode("node3", 1_000_004)
+ ts.AddValidatorNode("node4", 1_000_003)
+ ts.AddValidatorNode("node5", 1_000_002)
+ ts.AddValidatorNode("node6", 1_000_001)
+
+ nodeOptions := make(map[string][]options.Option[protocol.Protocol])
+
+ for _, node := range ts.Nodes() {
+ nodeOptions[node.Name] = []options.Option[protocol.Protocol]{protocol.WithSybilProtectionProvider(
+ sybilprotectionv1.NewProvider(
+ sybilprotectionv1.WithSeatManagerProvider(
+ topstakers.NewProvider(
+ topstakers.WithSeatCount(3),
+ ),
+ ),
+ ),
+ )}
+ }
+ ts.Run(true, nodeOptions)
+
+ for _, node := range ts.Nodes() {
+ nodeOptions[node.Name] = []options.Option[protocol.Protocol]{protocol.WithSybilProtectionProvider(
+ sybilprotectionv1.NewProvider(
+ sybilprotectionv1.WithSeatManagerProvider(
+ topstakers.NewProvider(topstakers.WithSeatCount(3)),
+ ),
+ ),
+ )}
+ }
+ ts.AssertSybilProtectionCommittee(0, []iotago.AccountID{
+ ts.Node("node1").Validator.AccountID,
+ ts.Node("node2").Validator.AccountID,
+ ts.Node("node3").Validator.AccountID,
+ }, ts.Nodes()...)
+
+ ts.IssueBlocksAtSlots("wave-1:", []iotago.SlotIndex{1, 2, 3, 4}, 4, "Genesis", ts.Nodes(), true, nil)
+
+ ts.IssueCandidacyAnnouncementInSlot("node1-candidacy:1", 4, "wave-1:4.3", ts.Node("node1"))
+ ts.IssueCandidacyAnnouncementInSlot("node4-candidacy:1", 5, "node1-candidacy:1", ts.Node("node4"))
+
+ ts.IssueBlocksAtSlots("wave-2:", []iotago.SlotIndex{5, 6, 7, 8, 9}, 4, "node4-candidacy:1", ts.Nodes(), true, nil)
+
+ ts.IssueCandidacyAnnouncementInSlot("node4-candidacy:2", 9, "wave-2:9.3", ts.Node("node4"))
+ ts.IssueCandidacyAnnouncementInSlot("node5-candidacy:1", 9, "node4-candidacy:2", ts.Node("node5"))
+
+ // This candidacy should be considered as it's announced at the last possible slot.
+ ts.IssueCandidacyAnnouncementInSlot("node6-candidacy:1", 10, "node5-candidacy:1", ts.Node("node6"))
+
+ ts.IssueBlocksAtSlots("wave-3:", []iotago.SlotIndex{10}, 4, "node6-candidacy:1", ts.Nodes(), true, nil)
+
+ // Those candidacies should not be considered as they're issued after EpochNearingThreshold (slot 10).
+ ts.IssueCandidacyAnnouncementInSlot("node2-candidacy:1", 11, "wave-3:10.3", ts.Node("node2"))
+ ts.IssueCandidacyAnnouncementInSlot("node3-candidacy:1", 11, "node2-candidacy:1", ts.Node("node3"))
+ ts.IssueCandidacyAnnouncementInSlot("node4-candidacy:3", 11, "node3-candidacy:1", ts.Node("node3"))
+ ts.IssueCandidacyAnnouncementInSlot("node5-candidacy:2", 11, "node4-candidacy:3", ts.Node("node3"))
+
+ // Assert that only candidates that issued before slot 11 are considered.
+ ts.AssertSybilProtectionCandidates(1, []iotago.AccountID{
+ ts.Node("node1").Validator.AccountID,
+ ts.Node("node4").Validator.AccountID,
+ ts.Node("node5").Validator.AccountID,
+ ts.Node("node6").Validator.AccountID,
+ }, ts.Nodes()...)
+
+ ts.IssueBlocksAtSlots("wave-4:", []iotago.SlotIndex{11, 12, 13, 14, 15, 16, 17}, 4, "node5-candidacy:2", ts.Nodes(), true, nil)
+
+ ts.AssertLatestFinalizedSlot(13, ts.Nodes()...)
+ ts.AssertSybilProtectionCommittee(1, []iotago.AccountID{
+ ts.Node("node1").Validator.AccountID,
+ ts.Node("node4").Validator.AccountID,
+ ts.Node("node5").Validator.AccountID,
+ }, ts.Nodes()...)
+}
diff --git a/pkg/tests/confirmation_state_test.go b/pkg/tests/confirmation_state_test.go
index 4a0285d08..c874dd400 100644
--- a/pkg/tests/confirmation_state_test.go
+++ b/pkg/tests/confirmation_state_test.go
@@ -108,7 +108,7 @@ func TestConfirmationFlags(t *testing.T) {
testsuite.WithChainID(genesisCommitment.MustID()),
testsuite.WithStorageCommitments([]*iotago.Commitment{genesisCommitment}),
testsuite.WithSybilProtectionCommittee(0, expectedCommittee),
- testsuite.WithSybilProtectionOnlineCommittee(lo.Return1(nodeA.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(nodeA.Validator.AccountID))),
+ testsuite.WithSybilProtectionOnlineCommittee(lo.Return1(lo.Return1(nodeA.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(nodeA.Validator.AccountID))),
testsuite.WithEvictedSlot(0),
testsuite.WithActiveRootBlocks(ts.Blocks("Genesis")),
testsuite.WithStorageRootBlocks(ts.Blocks("Genesis")),
@@ -163,10 +163,10 @@ func TestConfirmationFlags(t *testing.T) {
testsuite.WithLatestFinalizedSlot(0),
testsuite.WithLatestCommitmentSlotIndex(2),
testsuite.WithEqualStoredCommitmentAtIndex(2),
- testsuite.WithSybilProtectionCommittee(slot2CommittableSlot, expectedCommittee),
+ testsuite.WithSybilProtectionCommittee(ts.API.TimeProvider().EpochFromSlot(slot2CommittableSlot), expectedCommittee),
testsuite.WithSybilProtectionOnlineCommittee(
- lo.Return1(nodeA.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(nodeA.Validator.AccountID)),
- lo.Return1(nodeA.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(nodeB.Validator.AccountID)),
+ lo.Return1(lo.Return1(nodeA.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(nodeA.Validator.AccountID)),
+ lo.Return1(lo.Return1(nodeA.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(nodeB.Validator.AccountID)),
),
testsuite.WithEvictedSlot(2),
)
@@ -203,11 +203,11 @@ func TestConfirmationFlags(t *testing.T) {
testsuite.WithLatestFinalizedSlot(0),
testsuite.WithLatestCommitmentSlotIndex(2),
testsuite.WithEqualStoredCommitmentAtIndex(2),
- testsuite.WithSybilProtectionCommittee(slot3CommittableSlot, expectedCommittee),
+ testsuite.WithSybilProtectionCommittee(ts.API.TimeProvider().EpochFromSlot(slot3CommittableSlot), expectedCommittee),
testsuite.WithSybilProtectionOnlineCommittee(
- lo.Return1(nodeA.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(nodeA.Validator.AccountID)),
- lo.Return1(nodeA.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(nodeB.Validator.AccountID)),
- lo.Return1(nodeA.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(nodeC.Validator.AccountID)),
+ lo.Return1(lo.Return1(nodeA.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(nodeA.Validator.AccountID)),
+ lo.Return1(lo.Return1(nodeA.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(nodeB.Validator.AccountID)),
+ lo.Return1(lo.Return1(nodeA.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(nodeC.Validator.AccountID)),
),
testsuite.WithEvictedSlot(2),
)
@@ -258,11 +258,11 @@ func TestConfirmationFlags(t *testing.T) {
testsuite.WithLatestFinalizedSlot(1),
testsuite.WithLatestCommitmentSlotIndex(3),
testsuite.WithEqualStoredCommitmentAtIndex(3),
- testsuite.WithSybilProtectionCommittee(slot4CommittableSlot, expectedCommittee),
+ testsuite.WithSybilProtectionCommittee(ts.API.TimeProvider().EpochFromSlot(slot4CommittableSlot), expectedCommittee),
testsuite.WithSybilProtectionOnlineCommittee(
- lo.Return1(nodeA.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(nodeA.Validator.AccountID)),
- lo.Return1(nodeA.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(nodeB.Validator.AccountID)),
- lo.Return1(nodeA.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(nodeC.Validator.AccountID)),
+ lo.Return1(lo.Return1(nodeA.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(nodeA.Validator.AccountID)),
+ lo.Return1(lo.Return1(nodeA.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(nodeB.Validator.AccountID)),
+ lo.Return1(lo.Return1(nodeA.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(nodeC.Validator.AccountID)),
),
testsuite.WithEvictedSlot(3),
)
diff --git a/pkg/tests/protocol_engine_rollback_test.go b/pkg/tests/protocol_engine_rollback_test.go
index 1ab75a4ed..e99339b19 100644
--- a/pkg/tests/protocol_engine_rollback_test.go
+++ b/pkg/tests/protocol_engine_rollback_test.go
@@ -65,7 +65,6 @@ func TestProtocol_EngineRollbackFinalization(t *testing.T) {
return poa
})
}
-
nodeOptions := make(map[string][]options.Option[protocol.Protocol])
for _, node := range ts.Nodes() {
nodeOptions[node.Name] = []options.Option[protocol.Protocol]{
@@ -105,10 +104,10 @@ func TestProtocol_EngineRollbackFinalization(t *testing.T) {
node3.Validator.AccountID,
}
expectedOnlineCommitteeFull := []account.SeatIndex{
- lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(node0.Validator.AccountID)),
- lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(node1.Validator.AccountID)),
- lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(node2.Validator.AccountID)),
- lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(node3.Validator.AccountID)),
+ lo.Return1(lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node0.Validator.AccountID)),
+ lo.Return1(lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node1.Validator.AccountID)),
+ lo.Return1(lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node2.Validator.AccountID)),
+ lo.Return1(lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node3.Validator.AccountID)),
}
for _, node := range ts.Nodes() {
@@ -144,7 +143,7 @@ func TestProtocol_EngineRollbackFinalization(t *testing.T) {
testsuite.WithLatestCommitmentSlotIndex(9),
testsuite.WithEqualStoredCommitmentAtIndex(9),
testsuite.WithLatestCommitmentCumulativeWeight(28), // 7 for each slot starting from 4
- testsuite.WithSybilProtectionCommittee(9, expectedCommittee),
+ testsuite.WithSybilProtectionCommittee(ts.API.TimeProvider().EpochFromSlot(9), expectedCommittee),
testsuite.WithSybilProtectionOnlineCommittee(expectedOnlineCommitteeFull...),
testsuite.WithEvictedSlot(9),
)
@@ -170,7 +169,7 @@ func TestProtocol_EngineRollbackFinalization(t *testing.T) {
testsuite.WithLatestCommitmentSlotIndex(14),
testsuite.WithEqualStoredCommitmentAtIndex(14),
testsuite.WithLatestCommitmentCumulativeWeight(48), // 7 for each slot starting from 4
- testsuite.WithSybilProtectionCommittee(14, expectedCommittee),
+ testsuite.WithSybilProtectionCommittee(ts.API.TimeProvider().EpochFromSlot(14), expectedCommittee),
testsuite.WithSybilProtectionOnlineCommittee(expectedOnlineCommitteeFull...),
testsuite.WithEvictedSlot(14),
)
@@ -292,15 +291,15 @@ func TestProtocol_EngineRollbackNoFinalization(t *testing.T) {
node3.Validator.AccountID,
}
expectedOnlineCommitteeFull := []account.SeatIndex{
- lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(node0.Validator.AccountID)),
- lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(node1.Validator.AccountID)),
- lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(node2.Validator.AccountID)),
- lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(node3.Validator.AccountID)),
+ lo.Return1(lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node0.Validator.AccountID)),
+ lo.Return1(lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node1.Validator.AccountID)),
+ lo.Return1(lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node2.Validator.AccountID)),
+ lo.Return1(lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node3.Validator.AccountID)),
}
expectedOnlineCommitteeHalf := []account.SeatIndex{
- lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(node0.Validator.AccountID)),
- lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(node1.Validator.AccountID)),
+ lo.Return1(lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node0.Validator.AccountID)),
+ lo.Return1(lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node1.Validator.AccountID)),
}
for _, node := range ts.Nodes() {
@@ -327,7 +326,7 @@ func TestProtocol_EngineRollbackNoFinalization(t *testing.T) {
}
// Issue up to slot 11 - just before committee selection for the next epoch.
- // Committee will be reused at slot 10 is finalized or slot 12 is committed, whichever happens first.
+ // Committee will be reused when slot 10 is finalized or slot 12 is committed, whichever happens first.
{
ts.IssueBlocksAtSlots("P0:", []iotago.SlotIndex{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 4, "Genesis", ts.Nodes(), true, nil)
@@ -336,7 +335,7 @@ func TestProtocol_EngineRollbackNoFinalization(t *testing.T) {
testsuite.WithLatestCommitmentSlotIndex(9),
testsuite.WithEqualStoredCommitmentAtIndex(9),
testsuite.WithLatestCommitmentCumulativeWeight(28), // 7 for each slot starting from 4
- testsuite.WithSybilProtectionCommittee(9, expectedCommittee),
+ testsuite.WithSybilProtectionCommittee(ts.API.TimeProvider().EpochFromSlot(9), expectedCommittee),
testsuite.WithSybilProtectionOnlineCommittee(expectedOnlineCommitteeFull...),
testsuite.WithEvictedSlot(9),
)
@@ -369,7 +368,7 @@ func TestProtocol_EngineRollbackNoFinalization(t *testing.T) {
testsuite.WithLatestCommitmentSlotIndex(14),
testsuite.WithEqualStoredCommitmentAtIndex(14),
testsuite.WithLatestCommitmentCumulativeWeight(44), // 7 for each slot starting from 4
- testsuite.WithSybilProtectionCommittee(14, expectedCommittee),
+ testsuite.WithSybilProtectionCommittee(ts.API.TimeProvider().EpochFromSlot(14), expectedCommittee),
testsuite.WithSybilProtectionOnlineCommittee(expectedOnlineCommitteeHalf...),
testsuite.WithEvictedSlot(14),
)
@@ -491,15 +490,15 @@ func TestProtocol_EngineRollbackNoFinalizationLastSlot(t *testing.T) {
node3.Validator.AccountID,
}
expectedOnlineCommitteeFull := []account.SeatIndex{
- lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(node0.Validator.AccountID)),
- lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(node1.Validator.AccountID)),
- lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(node2.Validator.AccountID)),
- lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(node3.Validator.AccountID)),
+ lo.Return1(lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node0.Validator.AccountID)),
+ lo.Return1(lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node1.Validator.AccountID)),
+ lo.Return1(lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node2.Validator.AccountID)),
+ lo.Return1(lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node3.Validator.AccountID)),
}
expectedOnlineCommitteeHalf := []account.SeatIndex{
- lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(node0.Validator.AccountID)),
- lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(node1.Validator.AccountID)),
+ lo.Return1(lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node0.Validator.AccountID)),
+ lo.Return1(lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node1.Validator.AccountID)),
}
for _, node := range ts.Nodes() {
@@ -535,7 +534,7 @@ func TestProtocol_EngineRollbackNoFinalizationLastSlot(t *testing.T) {
testsuite.WithLatestCommitmentSlotIndex(9),
testsuite.WithEqualStoredCommitmentAtIndex(9),
testsuite.WithLatestCommitmentCumulativeWeight(28), // 7 for each slot starting from 4
- testsuite.WithSybilProtectionCommittee(9, expectedCommittee),
+ testsuite.WithSybilProtectionCommittee(ts.API.TimeProvider().EpochFromSlot(9), expectedCommittee),
testsuite.WithSybilProtectionOnlineCommittee(expectedOnlineCommitteeFull...),
testsuite.WithEvictedSlot(9),
)
@@ -568,7 +567,7 @@ func TestProtocol_EngineRollbackNoFinalizationLastSlot(t *testing.T) {
testsuite.WithLatestCommitmentSlotIndex(17),
testsuite.WithEqualStoredCommitmentAtIndex(17),
testsuite.WithLatestCommitmentCumulativeWeight(50), // 7 for each slot starting from 4
- testsuite.WithSybilProtectionCommittee(17, expectedCommittee),
+ testsuite.WithSybilProtectionCommittee(ts.API.TimeProvider().EpochFromSlot(17), expectedCommittee),
testsuite.WithSybilProtectionOnlineCommittee(expectedOnlineCommitteeHalf...),
testsuite.WithEvictedSlot(17),
)
@@ -690,15 +689,15 @@ func TestProtocol_EngineRollbackNoFinalizationBeforePointOfNoReturn(t *testing.T
node3.Validator.AccountID,
}
expectedOnlineCommitteeFull := []account.SeatIndex{
- lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(node0.Validator.AccountID)),
- lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(node1.Validator.AccountID)),
- lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(node2.Validator.AccountID)),
- lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(node3.Validator.AccountID)),
+ lo.Return1(lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node0.Validator.AccountID)),
+ lo.Return1(lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node1.Validator.AccountID)),
+ lo.Return1(lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node2.Validator.AccountID)),
+ lo.Return1(lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node3.Validator.AccountID)),
}
expectedOnlineCommitteeHalf := []account.SeatIndex{
- lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(node0.Validator.AccountID)),
- lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(node1.Validator.AccountID)),
+ lo.Return1(lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node0.Validator.AccountID)),
+ lo.Return1(lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node1.Validator.AccountID)),
}
for _, node := range ts.Nodes() {
@@ -734,7 +733,7 @@ func TestProtocol_EngineRollbackNoFinalizationBeforePointOfNoReturn(t *testing.T
testsuite.WithLatestCommitmentSlotIndex(9),
testsuite.WithEqualStoredCommitmentAtIndex(9),
testsuite.WithLatestCommitmentCumulativeWeight(28), // 7 for each slot starting from 4
- testsuite.WithSybilProtectionCommittee(9, expectedCommittee),
+ testsuite.WithSybilProtectionCommittee(ts.API.TimeProvider().EpochFromSlot(9), expectedCommittee),
testsuite.WithSybilProtectionOnlineCommittee(expectedOnlineCommitteeFull...),
testsuite.WithEvictedSlot(9),
)
@@ -767,7 +766,7 @@ func TestProtocol_EngineRollbackNoFinalizationBeforePointOfNoReturn(t *testing.T
testsuite.WithLatestCommitmentSlotIndex(13),
testsuite.WithEqualStoredCommitmentAtIndex(13),
testsuite.WithLatestCommitmentCumulativeWeight(42), // 7 for each slot starting from 4
- testsuite.WithSybilProtectionCommittee(13, expectedCommittee),
+ testsuite.WithSybilProtectionCommittee(ts.API.TimeProvider().EpochFromSlot(13), expectedCommittee),
testsuite.WithSybilProtectionOnlineCommittee(expectedOnlineCommitteeHalf...),
testsuite.WithEvictedSlot(13),
)
diff --git a/pkg/tests/protocol_engine_switching_test.go b/pkg/tests/protocol_engine_switching_test.go
index 43290eac5..e5a61698f 100644
--- a/pkg/tests/protocol_engine_switching_test.go
+++ b/pkg/tests/protocol_engine_switching_test.go
@@ -124,15 +124,15 @@ func TestProtocol_EngineSwitching(t *testing.T) {
node7.Validator.AccountID,
}
expectedP1OnlineCommittee := []account.SeatIndex{
- lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(node0.Validator.AccountID)),
- lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(node1.Validator.AccountID)),
- lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(node2.Validator.AccountID)),
- lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(node3.Validator.AccountID)),
- lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(node4.Validator.AccountID)),
+ lo.Return1(lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node0.Validator.AccountID)),
+ lo.Return1(lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node1.Validator.AccountID)),
+ lo.Return1(lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node2.Validator.AccountID)),
+ lo.Return1(lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node3.Validator.AccountID)),
+ lo.Return1(lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node4.Validator.AccountID)),
}
expectedP2OnlineCommittee := []account.SeatIndex{
- lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(node6.Validator.AccountID)),
- lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(node7.Validator.AccountID)),
+ lo.Return1(lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node6.Validator.AccountID)),
+ lo.Return1(lo.Return1(node0.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node7.Validator.AccountID)),
}
expectedOnlineCommittee := append(expectedP1OnlineCommittee, expectedP2OnlineCommittee...)
@@ -170,7 +170,7 @@ func TestProtocol_EngineSwitching(t *testing.T) {
testsuite.WithLatestCommitmentSlotIndex(11),
testsuite.WithEqualStoredCommitmentAtIndex(11),
testsuite.WithLatestCommitmentCumulativeWeight(56), // 7 for each slot starting from 4
- testsuite.WithSybilProtectionCommittee(11, expectedCommittee),
+ testsuite.WithSybilProtectionCommittee(ts.API.TimeProvider().EpochFromSlot(11), expectedCommittee),
testsuite.WithSybilProtectionOnlineCommittee(expectedOnlineCommittee...),
testsuite.WithEvictedSlot(11),
)
@@ -225,7 +225,7 @@ func TestProtocol_EngineSwitching(t *testing.T) {
testsuite.WithLatestCommitmentSlotIndex(18),
testsuite.WithEqualStoredCommitmentAtIndex(18),
testsuite.WithLatestCommitmentCumulativeWeight(99), // 56 + slot 12-15=7 + 5 for each slot starting from 16
- testsuite.WithSybilProtectionCommittee(18, expectedCommittee),
+ testsuite.WithSybilProtectionCommittee(ts.API.TimeProvider().EpochFromSlot(18), expectedCommittee),
testsuite.WithSybilProtectionOnlineCommittee(expectedP1OnlineCommittee...),
testsuite.WithEvictedSlot(18),
)
@@ -279,7 +279,7 @@ func TestProtocol_EngineSwitching(t *testing.T) {
testsuite.WithLatestCommitmentSlotIndex(18),
testsuite.WithEqualStoredCommitmentAtIndex(18),
testsuite.WithLatestCommitmentCumulativeWeight(90), // 56 + slot 12-15=7 + 2 for each slot starting from 16
- testsuite.WithSybilProtectionCommittee(18, expectedCommittee),
+ testsuite.WithSybilProtectionCommittee(ts.API.TimeProvider().EpochFromSlot(18), expectedCommittee),
testsuite.WithSybilProtectionOnlineCommittee(expectedP2OnlineCommittee...),
testsuite.WithEvictedSlot(18),
)
diff --git a/pkg/tests/protocol_startup_test.go b/pkg/tests/protocol_startup_test.go
index c76fd6bc1..5ba4f4960 100644
--- a/pkg/tests/protocol_startup_test.go
+++ b/pkg/tests/protocol_startup_test.go
@@ -58,7 +58,7 @@ func Test_BookInCommittedSlot(t *testing.T) {
}
expectedOnlineCommittee := []account.SeatIndex{
- lo.Return1(nodeA.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(nodeA.Validator.AccountID)),
+ lo.Return1(lo.Return1(nodeA.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(nodeA.Validator.AccountID)),
}
// Verify that nodes have the expected states.
@@ -168,8 +168,8 @@ func Test_StartNodeFromSnapshotAndDisk(t *testing.T) {
}
expectedOnlineCommittee := []account.SeatIndex{
- lo.Return1(nodeA.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(nodeA.Validator.AccountID)),
- lo.Return1(nodeA.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(1).GetSeat(nodeB.Validator.AccountID)),
+ lo.Return1(lo.Return1(nodeA.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(nodeA.Validator.AccountID)),
+ lo.Return1(lo.Return1(nodeA.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(nodeB.Validator.AccountID)),
}
// Verify that nodes have the expected states.
@@ -222,7 +222,7 @@ func Test_StartNodeFromSnapshotAndDisk(t *testing.T) {
testsuite.WithLatestCommitmentSlotIndex(5),
testsuite.WithEqualStoredCommitmentAtIndex(5),
testsuite.WithLatestCommitmentCumulativeWeight(4), // 2 for each slot starting from 4
- testsuite.WithSybilProtectionCommittee(5, expectedCommittee),
+ testsuite.WithSybilProtectionCommittee(ts.API.TimeProvider().EpochFromSlot(5), expectedCommittee),
testsuite.WithSybilProtectionOnlineCommittee(expectedOnlineCommittee...),
testsuite.WithEvictedSlot(5),
testsuite.WithActiveRootBlocks(expectedActiveRootBlocks),
@@ -270,7 +270,7 @@ func Test_StartNodeFromSnapshotAndDisk(t *testing.T) {
testsuite.WithLatestCommitmentSlotIndex(11),
testsuite.WithEqualStoredCommitmentAtIndex(11),
testsuite.WithLatestCommitmentCumulativeWeight(16), // 2 for each slot starting from 4
- testsuite.WithSybilProtectionCommittee(11, expectedCommittee),
+ testsuite.WithSybilProtectionCommittee(ts.API.TimeProvider().EpochFromSlot(11), expectedCommittee),
testsuite.WithSybilProtectionOnlineCommittee(expectedOnlineCommittee...),
testsuite.WithEvictedSlot(11),
testsuite.WithActiveRootBlocks(expectedActiveRootBlocks),
@@ -354,7 +354,7 @@ func Test_StartNodeFromSnapshotAndDisk(t *testing.T) {
testsuite.WithLatestCommitmentSlotIndex(11),
testsuite.WithEqualStoredCommitmentAtIndex(11),
testsuite.WithLatestCommitmentCumulativeWeight(16), // 2 for each slot starting from 4
- testsuite.WithSybilProtectionCommittee(11, expectedCommittee),
+ testsuite.WithSybilProtectionCommittee(ts.API.TimeProvider().EpochFromSlot(11), expectedCommittee),
testsuite.WithSybilProtectionOnlineCommittee(expectedOnlineCommittee...),
testsuite.WithEvictedSlot(11),
testsuite.WithActiveRootBlocks(expectedActiveRootBlocks),
@@ -410,7 +410,7 @@ func Test_StartNodeFromSnapshotAndDisk(t *testing.T) {
testsuite.WithLatestCommitmentSlotIndex(37),
testsuite.WithEqualStoredCommitmentAtIndex(37),
testsuite.WithLatestCommitmentCumulativeWeight(68), // 2 for each slot starting from 4
- testsuite.WithSybilProtectionCommittee(37, expectedCommittee),
+ testsuite.WithSybilProtectionCommittee(ts.API.TimeProvider().EpochFromSlot(37), expectedCommittee),
testsuite.WithSybilProtectionOnlineCommittee(expectedOnlineCommittee...),
testsuite.WithEvictedSlot(37),
testsuite.WithActiveRootBlocks(expectedActiveRootBlocks),
diff --git a/pkg/testsuite/mock/blockissuer.go b/pkg/testsuite/mock/blockissuer.go
index db6e532ba..b29b015ba 100644
--- a/pkg/testsuite/mock/blockissuer.go
+++ b/pkg/testsuite/mock/blockissuer.go
@@ -155,7 +155,7 @@ func (i *BlockIssuer) CreateValidationBlock(ctx context.Context, alias string, i
blockBuilder.IssuingTime(*blockParams.BlockHeader.IssuingTime)
strongParents, exists := blockParams.BlockHeader.References[iotago.StrongParentType]
- require.True(i.Testing, exists && len(strongParents) > 0)
+ require.True(i.Testing, exists && len(strongParents) > 0, "block should have strong parents (exists: %t, parents: %s)", exists, strongParents)
blockBuilder.StrongParents(strongParents)
if weakParents, exists := blockParams.BlockHeader.References[iotago.WeakParentType]; exists {
diff --git a/pkg/testsuite/mock/node.go b/pkg/testsuite/mock/node.go
index ae3d69847..36112ad13 100644
--- a/pkg/testsuite/mock/node.go
+++ b/pkg/testsuite/mock/node.go
@@ -380,6 +380,10 @@ func (n *Node) attachEngineLogsWithName(failOnBlockFiltered bool, instance *engi
fmt.Printf("%s > [%s] SybilProtection.OnlineCommitteeSeatRemoved: %d\n", n.Name, engineName, seat)
})
+ events.SybilProtection.CommitteeSelected.Hook(func(committee *account.Accounts, epoch iotago.EpochIndex) {
+ fmt.Printf("%s > [%s] SybilProtection.CommitteeSelected: epoch %d - %s\n", n.Name, engineName, epoch, committee.IDs())
+ })
+
events.ConflictDAG.ConflictCreated.Hook(func(conflictID iotago.TransactionID) {
fmt.Printf("%s > [%s] ConflictDAG.ConflictCreated: %s\n", n.Name, engineName, conflictID)
})
diff --git a/pkg/testsuite/node_state.go b/pkg/testsuite/node_state.go
index 88b7ee2b4..65b60c728 100644
--- a/pkg/testsuite/node_state.go
+++ b/pkg/testsuite/node_state.go
@@ -32,8 +32,8 @@ func (t *TestSuite) AssertNodeState(nodes []*mock.Node, opts ...options.Option[N
if state.chainID != nil {
t.AssertChainID(*state.chainID, nodes...)
}
- if state.sybilProtectionCommitteeSlot != nil && state.sybilProtectionCommittee != nil {
- t.AssertSybilProtectionCommittee(*state.sybilProtectionCommitteeSlot, *state.sybilProtectionCommittee, nodes...)
+ if state.sybilProtectionCommitteeEpoch != nil && state.sybilProtectionCommittee != nil {
+ t.AssertSybilProtectionCommittee(*state.sybilProtectionCommitteeEpoch, *state.sybilProtectionCommittee, nodes...)
}
if state.sybilProtectionOnlineCommittee != nil {
t.AssertSybilProtectionOnlineCommittee(*state.sybilProtectionOnlineCommittee, nodes...)
@@ -67,7 +67,7 @@ type NodeState struct {
latestFinalizedSlot *iotago.SlotIndex
chainID *iotago.CommitmentID
- sybilProtectionCommitteeSlot *iotago.SlotIndex
+ sybilProtectionCommitteeEpoch *iotago.EpochIndex
sybilProtectionCommittee *[]iotago.AccountID
sybilProtectionOnlineCommittee *[]account.SeatIndex
@@ -130,9 +130,9 @@ func WithChainID(chainID iotago.CommitmentID) options.Option[NodeState] {
}
}
-func WithSybilProtectionCommittee(slot iotago.SlotIndex, committee []iotago.AccountID) options.Option[NodeState] {
+func WithSybilProtectionCommittee(epoch iotago.EpochIndex, committee []iotago.AccountID) options.Option[NodeState] {
return func(state *NodeState) {
- state.sybilProtectionCommitteeSlot = &slot
+ state.sybilProtectionCommitteeEpoch = &epoch
state.sybilProtectionCommittee = &committee
}
}
diff --git a/pkg/testsuite/snapshotcreator/options.go b/pkg/testsuite/snapshotcreator/options.go
index f1da75f43..74d986fe4 100644
--- a/pkg/testsuite/snapshotcreator/options.go
+++ b/pkg/testsuite/snapshotcreator/options.go
@@ -6,6 +6,8 @@ import (
"github.com/iotaledger/iota-core/pkg/protocol/engine"
"github.com/iotaledger/iota-core/pkg/protocol/engine/ledger"
ledger1 "github.com/iotaledger/iota-core/pkg/protocol/engine/ledger/ledger"
+ "github.com/iotaledger/iota-core/pkg/protocol/sybilprotection/seatmanager"
+ "github.com/iotaledger/iota-core/pkg/protocol/sybilprotection/seatmanager/poa"
iotago "github.com/iotaledger/iota.go/v4"
)
@@ -29,19 +31,27 @@ type Options struct {
// BasicOutput defines the basic outputs that are created in the ledger as part of the Genesis.
BasicOutputs []BasicOutputDetails
- DataBaseVersion byte
- LedgerProvider func() module.Provider[*engine.Engine, ledger.Ledger]
+ DataBaseVersion byte
+ LedgerProvider module.Provider[*engine.Engine, ledger.Ledger]
+ SeatManagerProvider module.Provider[*engine.Engine, seatmanager.SeatManager]
}
func NewOptions(opts ...options.Option[Options]) *Options {
return options.Apply(&Options{
- FilePath: "snapshot.bin",
- DataBaseVersion: 1,
- LedgerProvider: ledger1.NewProvider,
+ FilePath: "snapshot.bin",
+ DataBaseVersion: 1,
+ LedgerProvider: ledger1.NewProvider(),
+ SeatManagerProvider: poa.NewProvider(),
}, opts)
}
-func WithLedgerProvider(ledgerProvider func() module.Provider[*engine.Engine, ledger.Ledger]) options.Option[Options] {
+func WithSeatManagerProvider(seatManagerProvider module.Provider[*engine.Engine, seatmanager.SeatManager]) options.Option[Options] {
+ return func(m *Options) {
+ m.SeatManagerProvider = seatManagerProvider
+ }
+}
+
+func WithLedgerProvider(ledgerProvider module.Provider[*engine.Engine, ledger.Ledger]) options.Option[Options] {
return func(m *Options) {
m.LedgerProvider = ledgerProvider
}
diff --git a/pkg/testsuite/snapshotcreator/snapshotcreator.go b/pkg/testsuite/snapshotcreator/snapshotcreator.go
index 8acc56027..f2040d79d 100644
--- a/pkg/testsuite/snapshotcreator/snapshotcreator.go
+++ b/pkg/testsuite/snapshotcreator/snapshotcreator.go
@@ -9,9 +9,9 @@ import (
"github.com/iotaledger/hive.go/lo"
"github.com/iotaledger/hive.go/runtime/options"
"github.com/iotaledger/hive.go/runtime/workerpool"
- "github.com/iotaledger/iota-core/pkg/core/account"
"github.com/iotaledger/iota-core/pkg/model"
"github.com/iotaledger/iota-core/pkg/protocol/engine"
+ "github.com/iotaledger/iota-core/pkg/protocol/engine/accounts"
"github.com/iotaledger/iota-core/pkg/protocol/engine/attestation/slotattestation"
"github.com/iotaledger/iota-core/pkg/protocol/engine/blockdag/inmemoryblockdag"
"github.com/iotaledger/iota-core/pkg/protocol/engine/booker/inmemorybooker"
@@ -70,20 +70,27 @@ func CreateSnapshot(opts ...options.Option[Options]) error {
return ierrors.Wrap(err, "failed to store empty commitment")
}
- accounts := account.NewAccounts()
- for _, accountData := range opt.Accounts {
+ committeeAccountsData := make(accounts.AccountsData, 0)
+ for _, snapshotAccountDetails := range opt.Accounts {
// Only add genesis validators if an account has both - StakedAmount and StakingEndEpoch - specified.
- if accountData.StakedAmount > 0 && accountData.StakingEpochEnd > 0 {
- blockIssuerKeyEd25519, ok := accountData.IssuerKey.(*iotago.Ed25519PublicKeyBlockIssuerKey)
+ if snapshotAccountDetails.StakedAmount > 0 && snapshotAccountDetails.StakingEpochEnd > 0 {
+ blockIssuerKeyEd25519, ok := snapshotAccountDetails.IssuerKey.(*iotago.Ed25519PublicKeyBlockIssuerKey)
if !ok {
panic("block issuer key must be of type ed25519")
}
ed25519PubKey := blockIssuerKeyEd25519.ToEd25519PublicKey()
accountID := blake2b.Sum256(ed25519PubKey[:])
- accounts.Set(accountID, &account.Pool{
- PoolStake: accountData.StakedAmount,
- ValidatorStake: accountData.StakedAmount,
- FixedCost: accountData.FixedCost,
+ committeeAccountsData = append(committeeAccountsData, &accounts.AccountData{
+ ID: accountID,
+ Credits: &accounts.BlockIssuanceCredits{Value: snapshotAccountDetails.BlockIssuanceCredits, UpdateTime: 0},
+ ExpirySlot: snapshotAccountDetails.ExpirySlot,
+ OutputID: iotago.OutputID{},
+ BlockIssuerKeys: iotago.BlockIssuerKeys{snapshotAccountDetails.IssuerKey},
+ ValidatorStake: snapshotAccountDetails.StakedAmount,
+ DelegationStake: 0,
+ FixedCost: snapshotAccountDetails.FixedCost,
+ StakeEndEpoch: snapshotAccountDetails.StakingEpochEnd,
+ LatestSupportedProtocolVersionAndHash: model.VersionAndHash{},
})
}
}
@@ -98,10 +105,11 @@ func CreateSnapshot(opts ...options.Option[Options]) error {
blocktime.NewProvider(),
thresholdblockgadget.NewProvider(),
totalweightslotgadget.NewProvider(),
- sybilprotectionv1.NewProvider(sybilprotectionv1.WithInitialCommittee(accounts)),
+ sybilprotectionv1.NewProvider(sybilprotectionv1.WithInitialCommittee(committeeAccountsData),
+ sybilprotectionv1.WithSeatManagerProvider(opt.SeatManagerProvider)),
slotnotarization.NewProvider(),
slotattestation.NewProvider(),
- opt.LedgerProvider(),
+ opt.LedgerProvider,
passthrough.NewProvider(),
tipmanagerv1.NewProvider(),
tipselectionv1.NewProvider(),
diff --git a/pkg/testsuite/sybilprotection.go b/pkg/testsuite/sybilprotection.go
index edbfe0feb..3eac2c524 100644
--- a/pkg/testsuite/sybilprotection.go
+++ b/pkg/testsuite/sybilprotection.go
@@ -2,19 +2,22 @@ package testsuite
import (
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
"github.com/iotaledger/hive.go/ierrors"
+ "github.com/iotaledger/hive.go/lo"
"github.com/iotaledger/iota-core/pkg/core/account"
+ "github.com/iotaledger/iota-core/pkg/protocol/engine/accounts"
"github.com/iotaledger/iota-core/pkg/testsuite/mock"
iotago "github.com/iotaledger/iota.go/v4"
)
-func (t *TestSuite) AssertSybilProtectionCommittee(slot iotago.SlotIndex, expectedAccounts []iotago.AccountID, nodes ...*mock.Node) {
+func (t *TestSuite) AssertSybilProtectionCommittee(epoch iotago.EpochIndex, expectedAccounts []iotago.AccountID, nodes ...*mock.Node) {
mustNodes(nodes)
for _, node := range nodes {
t.Eventually(func() error {
- accounts := node.Protocol.MainEngineInstance().SybilProtection.SeatManager().Committee(slot).Accounts().IDs()
+ accounts := lo.Return1(node.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInEpoch(epoch)).Accounts().IDs()
if !assert.ElementsMatch(t.fakeTesting, expectedAccounts, accounts) {
return ierrors.Errorf("AssertSybilProtectionCommittee: %s: expected %s, got %s", node.Name, expectedAccounts, accounts)
}
@@ -28,6 +31,30 @@ func (t *TestSuite) AssertSybilProtectionCommittee(slot iotago.SlotIndex, expect
}
}
+func (t *TestSuite) AssertSybilProtectionCandidates(epoch iotago.EpochIndex, expectedAccounts []iotago.AccountID, nodes ...*mock.Node) {
+ mustNodes(nodes)
+
+ for _, node := range nodes {
+ t.Eventually(func() error {
+ candidates, err := node.Protocol.MainEngineInstance().SybilProtection.EligibleValidators(epoch)
+ candidateIDs := lo.Map(candidates, func(candidate *accounts.AccountData) iotago.AccountID {
+ return candidate.ID
+ })
+ require.NoError(t.Testing, err)
+
+ if !assert.ElementsMatch(t.fakeTesting, expectedAccounts, candidateIDs) {
+ return ierrors.Errorf("AssertSybilProtectionCandidates: %s: expected %s, got %s", node.Name, expectedAccounts, candidateIDs)
+ }
+
+ if len(expectedAccounts) != len(candidates) {
+ return ierrors.Errorf("AssertSybilProtectionCandidates: %s: expected %v, got %v", node.Name, len(expectedAccounts), len(candidateIDs))
+ }
+
+ return nil
+ })
+ }
+}
+
func (t *TestSuite) AssertSybilProtectionOnlineCommittee(expectedSeats []account.SeatIndex, nodes ...*mock.Node) {
mustNodes(nodes)
diff --git a/pkg/testsuite/testsuite_issue_blocks.go b/pkg/testsuite/testsuite_issue_blocks.go
index 051de5159..38ce7ed98 100644
--- a/pkg/testsuite/testsuite_issue_blocks.go
+++ b/pkg/testsuite/testsuite_issue_blocks.go
@@ -158,6 +158,22 @@ func (t *TestSuite) IssueValidationBlock(blockName string, node *mock.Node, bloc
return block
}
+func (t *TestSuite) IssueCandidacyAnnouncementInSlot(alias string, slot iotago.SlotIndex, parentsPrefixAlias string, node *mock.Node, issuingOptions ...options.Option[mock.BlockHeaderParams]) *blocks.Block {
+ timeProvider := t.API.TimeProvider()
+ issuingTime := timeProvider.SlotStartTime(slot).Add(time.Duration(t.uniqueBlockTimeCounter.Add(1)))
+ require.Truef(t.Testing, issuingTime.Before(time.Now()), "node: %s: issued block (%s, slot: %d) is in the current (%s, slot: %d) or future slot", node.Name, issuingTime, slot, time.Now(), timeProvider.SlotFromTime(time.Now()))
+
+ return t.IssuePayloadWithOptions(
+ alias,
+ node.Validator,
+ node,
+ &iotago.CandidacyAnnouncement{},
+ append(issuingOptions,
+ mock.WithStrongParents(t.BlockIDsWithPrefix(parentsPrefixAlias)...),
+ mock.WithIssuingTime(issuingTime),
+ )...,
+ )
+}
func (t *TestSuite) IssueBlockRowInSlot(prefix string, slot iotago.SlotIndex, row int, parentsPrefix string, nodes []*mock.Node, issuingOptions map[string][]options.Option[mock.BlockHeaderParams]) []*blocks.Block {
blocksIssued := make([]*blocks.Block, 0, len(nodes))
@@ -174,7 +190,8 @@ func (t *TestSuite) IssueBlockRowInSlot(prefix string, slot iotago.SlotIndex, ro
require.Truef(t.Testing, issuingTime.Before(time.Now()), "node: %s: issued block (%s, slot: %d) is in the current (%s, slot: %d) or future slot", node.Name, issuingTime, slot, time.Now(), timeProvider.SlotFromTime(time.Now()))
var b *blocks.Block
- if node.Validator != nil {
+ // Only issue validator blocks if account has staking feature and is part of committee.
+ if node.Validator != nil && lo.Return1(node.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(slot)).HasAccount(node.Validator.AccountID) {
blockHeaderOptions := append(issuingOptionsCopy[node.Name], mock.WithIssuingTime(issuingTime))
t.assertParentsCommitmentExistFromBlockOptions(blockHeaderOptions, node)
t.assertParentsExistFromBlockOptions(blockHeaderOptions, node)
@@ -284,17 +301,31 @@ func (t *TestSuite) CommitUntilSlot(slot iotago.SlotIndex, parent *blocks.Block)
// preacceptance of nextBlockSlot
for _, node := range activeValidators {
require.True(t.Testing, node.IsValidator(), "node: %s: is not a validator node", node.Name)
- blockName := fmt.Sprintf("chain-%s-%d-%s", parent.ID().Alias(), chainIndex, node.Name)
- tip = t.IssueValidationBlockAtSlot(blockName, nextBlockSlot, node.Protocol.MainEngineInstance().Storage.Settings().LatestCommitment().Commitment(), node, tip.ID())
+ committeeAtBlockSlot, exists := node.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(nextBlockSlot)
+ require.True(t.Testing, exists, "node: %s: does not have committee selected for slot %d", node.Name, nextBlockSlot)
+ if committeeAtBlockSlot.HasAccount(node.Validator.AccountID) {
+ blockName := fmt.Sprintf("chain-%s-%d-%s", parent.ID().Alias(), chainIndex, node.Name)
+ tip = t.IssueValidationBlockAtSlot(blockName, nextBlockSlot, node.Protocol.MainEngineInstance().Storage.Settings().LatestCommitment().Commitment(), node, tip.ID())
+ }
}
// acceptance of nextBlockSlot
for _, node := range activeValidators {
- blockName := fmt.Sprintf("chain-%s-%d-%s", parent.ID().Alias(), chainIndex+1, node.Name)
- tip = t.IssueValidationBlockAtSlot(blockName, nextBlockSlot, node.Protocol.MainEngineInstance().Storage.Settings().LatestCommitment().Commitment(), node, tip.ID())
+ committeeAtBlockSlot, exists := node.Protocol.MainEngineInstance().SybilProtection.SeatManager().CommitteeInSlot(nextBlockSlot)
+ require.True(t.Testing, exists, "node: %s: does not have committee selected for slot %d", node.Name, nextBlockSlot)
+ if committeeAtBlockSlot.HasAccount(node.Validator.AccountID) {
+ blockName := fmt.Sprintf("chain-%s-%d-%s", parent.ID().Alias(), chainIndex+1, node.Name)
+ tip = t.IssueValidationBlockAtSlot(blockName, nextBlockSlot, node.Protocol.MainEngineInstance().Storage.Settings().LatestCommitment().Commitment(), node, tip.ID())
+ }
+ }
+
+ for _, node := range activeValidators {
+ t.AssertLatestCommitmentSlotIndex(nextBlockSlot-t.API.ProtocolParameters().MinCommittableAge(), node)
}
+
if nextBlockSlot == slot+t.API.ProtocolParameters().MinCommittableAge() {
break
}
+
nextBlockSlot = lo.Min(slot+t.API.ProtocolParameters().MinCommittableAge(), nextBlockSlot+t.API.ProtocolParameters().MinCommittableAge())
chainIndex += 2
}
diff --git a/pkg/toolset/ed25519.go b/pkg/toolset/ed25519.go
new file mode 100644
index 000000000..bae379fbf
--- /dev/null
+++ b/pkg/toolset/ed25519.go
@@ -0,0 +1,172 @@
+package toolset
+
+import (
+ "crypto/ed25519"
+ "encoding/hex"
+ "fmt"
+ "os"
+
+ flag "github.com/spf13/pflag"
+ "github.com/wollac/iota-crypto-demo/pkg/bip32path"
+ "github.com/wollac/iota-crypto-demo/pkg/bip39"
+ "github.com/wollac/iota-crypto-demo/pkg/slip10"
+ "github.com/wollac/iota-crypto-demo/pkg/slip10/eddsa"
+
+ "github.com/iotaledger/hive.go/app/configuration"
+ "github.com/iotaledger/hive.go/crypto"
+ iotago "github.com/iotaledger/iota.go/v4"
+)
+
+func printEd25519Info(mnemonic bip39.Mnemonic, path bip32path.Path, prvKey ed25519.PrivateKey, pubKey ed25519.PublicKey, hrp iotago.NetworkPrefix, outputJSON bool) error {
+ addr := iotago.Ed25519AddressFromPubKey(pubKey)
+
+ type keys struct {
+ BIP39 string `json:"mnemonic,omitempty"`
+ BIP32 string `json:"path,omitempty"`
+ PrivateKey string `json:"privateKey,omitempty"`
+ PublicKey string `json:"publicKey"`
+ Ed25519Address string `json:"ed25519"`
+ Bech32Address string `json:"bech32"`
+ }
+
+ k := keys{
+ PublicKey: hex.EncodeToString(pubKey),
+ Ed25519Address: hex.EncodeToString(addr[:]),
+ Bech32Address: addr.Bech32(hrp),
+ }
+
+ if prvKey != nil {
+ k.PrivateKey = hex.EncodeToString(prvKey)
+ }
+
+ if mnemonic != nil {
+ k.BIP39 = mnemonic.String()
+ k.BIP32 = path.String()
+ }
+
+ if outputJSON {
+ return printJSON(k)
+ }
+
+ if len(k.BIP39) > 0 {
+ fmt.Println("Your seed BIP39 mnemonic: ", k.BIP39)
+ fmt.Println()
+ fmt.Println("Your BIP32 path: ", k.BIP32)
+ }
+
+ if k.PrivateKey != "" {
+ fmt.Println("Your ed25519 private key: ", k.PrivateKey)
+ }
+
+ fmt.Println("Your ed25519 public key: ", k.PublicKey)
+ fmt.Println("Your ed25519 address: ", k.Ed25519Address)
+ fmt.Println("Your bech32 address: ", k.Bech32Address)
+
+ return nil
+}
+
+func generateEd25519Key(args []string) error {
+ fs := configuration.NewUnsortedFlagSet("", flag.ContinueOnError)
+ hrpFlag := fs.String(FlagToolHRP, string(iotago.PrefixTestnet), "the HRP which should be used for the Bech32 address")
+ bip32Path := fs.String(FlagToolBIP32Path, "m/44'/4218'/0'/0'/0'", "the BIP32 path that should be used to derive keys from seed")
+ mnemonicFlag := fs.String(FlagToolMnemonic, "", "the BIP-39 mnemonic sentence that should be used to derive the seed from (optional)")
+ outputJSONFlag := fs.Bool(FlagToolOutputJSON, false, FlagToolDescriptionOutputJSON)
+
+ fs.Usage = func() {
+ _, _ = fmt.Fprintf(os.Stderr, "Usage of %s:\n", ToolEd25519Key)
+ fs.PrintDefaults()
+ println(fmt.Sprintf("\nexample: %s --%s %s",
+ ToolEd25519Key,
+ FlagToolHRP,
+ string(iotago.PrefixTestnet)))
+ }
+
+ if err := parseFlagSet(fs, args); err != nil {
+ return err
+ }
+
+ if len(*hrpFlag) == 0 {
+ return fmt.Errorf("'%s' not specified", FlagToolHRP)
+ }
+
+ if len(*bip32Path) == 0 {
+ return fmt.Errorf("'%s' not specified", FlagToolBIP32Path)
+ }
+
+ var mnemonicSentence bip39.Mnemonic
+ if len(*mnemonicFlag) == 0 {
+ // Generate random entropy by using ed25519 key generation and using the private key seed (32 bytes)
+ _, random, err := ed25519.GenerateKey(nil)
+ if err != nil {
+ return err
+ }
+ entropy := random.Seed()
+
+ mnemonicSentence, err = bip39.EntropyToMnemonic(entropy)
+ if err != nil {
+ return err
+ }
+ } else {
+ mnemonicSentence = bip39.ParseMnemonic(*mnemonicFlag)
+ if len(mnemonicSentence) != 24 {
+ return fmt.Errorf("'%s' contains an invalid sentence length. Mnemonic should be 24 words", FlagToolMnemonic)
+ }
+ }
+
+ path, err := bip32path.ParsePath(*bip32Path)
+ if err != nil {
+ return err
+ }
+
+ seed, err := bip39.MnemonicToSeed(mnemonicSentence, "")
+ if err != nil {
+ return err
+ }
+
+ key, err := slip10.DeriveKeyFromPath(seed, eddsa.Ed25519(), path)
+ if err != nil {
+ return err
+ }
+ pubKey, prvKey := key.Key.(eddsa.Seed).Ed25519Key()
+
+ return printEd25519Info(mnemonicSentence, path, ed25519.PrivateKey(prvKey), ed25519.PublicKey(pubKey), iotago.NetworkPrefix(*hrpFlag), *outputJSONFlag)
+}
+
+func generateEd25519Address(args []string) error {
+ fs := configuration.NewUnsortedFlagSet("", flag.ContinueOnError)
+ hrpFlag := fs.String(FlagToolHRP, string(iotago.PrefixTestnet), "the HRP which should be used for the Bech32 address")
+ publicKeyFlag := fs.String(FlagToolPublicKey, "", "an ed25519 public key")
+ outputJSONFlag := fs.Bool(FlagToolOutputJSON, false, FlagToolDescriptionOutputJSON)
+
+ fs.Usage = func() {
+ _, _ = fmt.Fprintf(os.Stderr, "Usage of %s:\n", ToolEd25519Addr)
+ fs.PrintDefaults()
+ println(fmt.Sprintf("\nexample: %s --%s %s --%s %s",
+ ToolEd25519Addr,
+ FlagToolHRP,
+ string(iotago.PrefixTestnet),
+ FlagToolPublicKey,
+ "[PUB_KEY]",
+ ))
+ }
+
+ if err := parseFlagSet(fs, args); err != nil {
+ return err
+ }
+
+ if len(*hrpFlag) == 0 {
+ return fmt.Errorf("'%s' not specified", FlagToolHRP)
+ }
+
+ if len(*publicKeyFlag) == 0 {
+ return fmt.Errorf("'%s' not specified", FlagToolPublicKey)
+ }
+
+ // parse pubkey
+ pubKey, err := crypto.ParseEd25519PublicKeyFromString(*publicKeyFlag)
+ if err != nil {
+ return fmt.Errorf("can't decode '%s': %w", FlagToolPublicKey, err)
+ }
+
+ return printEd25519Info(nil, nil, nil, pubKey, iotago.NetworkPrefix(*hrpFlag), *outputJSONFlag)
+}
diff --git a/pkg/toolset/jwt.go b/pkg/toolset/jwt.go
new file mode 100644
index 000000000..9585eee8c
--- /dev/null
+++ b/pkg/toolset/jwt.go
@@ -0,0 +1,109 @@
+package toolset
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+
+ "github.com/libp2p/go-libp2p/core/peer"
+ flag "github.com/spf13/pflag"
+
+ "github.com/iotaledger/hive.go/app/configuration"
+ hivep2p "github.com/iotaledger/hive.go/crypto/p2p"
+ "github.com/iotaledger/hive.go/crypto/pem"
+ "github.com/iotaledger/iota-core/components/p2p"
+ "github.com/iotaledger/iota-core/pkg/jwt"
+)
+
+func generateJWTApiToken(args []string) error {
+
+ fs := configuration.NewUnsortedFlagSet("", flag.ContinueOnError)
+ databasePathFlag := fs.String(FlagToolDatabasePath, DefaultValueP2PDatabasePath, "the path to the p2p database folder")
+ apiJWTSaltFlag := fs.String(FlagToolSalt, DefaultValueAPIJWTTokenSalt, "salt used inside the JWT tokens for the REST API")
+ outputJSONFlag := fs.Bool(FlagToolOutputJSON, false, FlagToolDescriptionOutputJSON)
+
+ fs.Usage = func() {
+ _, _ = fmt.Fprintf(os.Stderr, "Usage of %s:\n", ToolJWTApi)
+ fs.PrintDefaults()
+ println(fmt.Sprintf("\nexample: %s --%s %s --%s %s",
+ ToolJWTApi,
+ FlagToolDatabasePath,
+ DefaultValueP2PDatabasePath,
+ FlagToolSalt,
+ DefaultValueAPIJWTTokenSalt))
+ }
+
+ if err := parseFlagSet(fs, args); err != nil {
+ return err
+ }
+
+ if len(*databasePathFlag) == 0 {
+ return fmt.Errorf("'%s' not specified", FlagToolDatabasePath)
+ }
+ if len(*apiJWTSaltFlag) == 0 {
+ return fmt.Errorf("'%s' not specified", FlagToolSalt)
+ }
+
+ databasePath := *databasePathFlag
+ privKeyFilePath := filepath.Join(databasePath, p2p.IdentityPrivateKeyFileName)
+
+ salt := *apiJWTSaltFlag
+
+ _, err := os.Stat(privKeyFilePath)
+ switch {
+ case os.IsNotExist(err):
+ // private key does not exist
+ return fmt.Errorf("private key file (%s) does not exist", privKeyFilePath)
+
+ case err == nil || os.IsExist(err):
+ // private key file exists
+
+ default:
+ return fmt.Errorf("unable to check private key file (%s): %w", privKeyFilePath, err)
+ }
+
+ privKey, err := pem.ReadEd25519PrivateKeyFromPEMFile(privKeyFilePath)
+ if err != nil {
+ return fmt.Errorf("reading private key file for peer identity failed: %w", err)
+ }
+
+ libp2pPrivKey, err := hivep2p.Ed25519PrivateKeyToLibp2pPrivateKey(privKey)
+ if err != nil {
+ return fmt.Errorf("reading private key file for peer identity failed: %w", err)
+ }
+
+ peerID, err := peer.IDFromPublicKey(libp2pPrivKey.GetPublic())
+ if err != nil {
+ return fmt.Errorf("unable to get peer identity from public key: %w", err)
+ }
+
+ // API tokens do not expire.
+ jwtAuth, err := jwt.NewAuth(salt,
+ 0,
+ peerID.String(),
+ libp2pPrivKey,
+ )
+ if err != nil {
+ return fmt.Errorf("JWT auth initialization failed: %w", err)
+ }
+
+ jwtToken, err := jwtAuth.IssueJWT()
+ if err != nil {
+ return fmt.Errorf("issuing JWT token failed: %w", err)
+ }
+
+ if *outputJSONFlag {
+
+ result := struct {
+ JWT string `json:"jwt"`
+ }{
+ JWT: jwtToken,
+ }
+
+ return printJSON(result)
+ }
+
+ fmt.Println("Your API JWT token: ", jwtToken)
+
+ return nil
+}
diff --git a/pkg/toolset/node_info.go b/pkg/toolset/node_info.go
new file mode 100644
index 000000000..058a3e6d6
--- /dev/null
+++ b/pkg/toolset/node_info.go
@@ -0,0 +1,50 @@
+package toolset
+
+import (
+ "context"
+ "fmt"
+ "os"
+
+ flag "github.com/spf13/pflag"
+
+ "github.com/iotaledger/hive.go/app/configuration"
+ "github.com/iotaledger/iota.go/v4/nodeclient"
+)
+
+func nodeInfo(args []string) error {
+ fs := configuration.NewUnsortedFlagSet("", flag.ContinueOnError)
+ nodeURLFlag := fs.String(FlagToolNodeURL, "http://localhost:14265", "URL of the node (optional)")
+ outputJSONFlag := fs.Bool(FlagToolOutputJSON, false, FlagToolDescriptionOutputJSON)
+
+ fs.Usage = func() {
+ _, _ = fmt.Fprintf(os.Stderr, "Usage of %s:\n", ToolNodeInfo)
+ fs.PrintDefaults()
+ println(fmt.Sprintf("\nexample: %s --%s %s",
+ ToolNodeInfo,
+ FlagToolNodeURL,
+ "http://192.168.1.221:14265",
+ ))
+ }
+
+ if err := parseFlagSet(fs, args); err != nil {
+ return err
+ }
+
+ client, err := nodeclient.New(*nodeURLFlag)
+ if err != nil {
+ return err
+ }
+
+ info, err := client.Info(context.Background())
+ if err != nil {
+ return err
+ }
+
+ if *outputJSONFlag {
+ return printJSON(info)
+ }
+
+ fmt.Printf("Name: %s\nVersion: %s\nLatestAcceptedBlockSlot: %d\nLatestConfirmedBlockSlot: %d\nIsHealthy: %s\n", info.Name, info.Version, info.Status.LatestAcceptedBlockSlot, info.Status.LatestConfirmedBlockSlot, yesOrNo(info.Status.IsHealthy))
+
+ return nil
+}
diff --git a/pkg/toolset/p2p_identity_extract.go b/pkg/toolset/p2p_identity_extract.go
new file mode 100644
index 000000000..45a364d10
--- /dev/null
+++ b/pkg/toolset/p2p_identity_extract.go
@@ -0,0 +1,66 @@
+package toolset
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+
+ flag "github.com/spf13/pflag"
+
+ "github.com/iotaledger/hive.go/app/configuration"
+ hivep2p "github.com/iotaledger/hive.go/crypto/p2p"
+ "github.com/iotaledger/hive.go/crypto/pem"
+ "github.com/iotaledger/iota-core/components/p2p"
+)
+
+func extractP2PIdentity(args []string) error {
+
+ fs := configuration.NewUnsortedFlagSet("", flag.ContinueOnError)
+ databasePathFlag := fs.String(FlagToolDatabasePath, DefaultValueP2PDatabasePath, "the path to the p2p database folder")
+ outputJSONFlag := fs.Bool(FlagToolOutputJSON, false, FlagToolDescriptionOutputJSON)
+
+ fs.Usage = func() {
+ _, _ = fmt.Fprintf(os.Stderr, "Usage of %s:\n", ToolP2PExtractIdentity)
+ fs.PrintDefaults()
+ println(fmt.Sprintf("\nexample: %s --%s %s",
+ ToolP2PExtractIdentity,
+ FlagToolDatabasePath,
+ DefaultValueP2PDatabasePath))
+ }
+
+ if err := parseFlagSet(fs, args); err != nil {
+ return err
+ }
+
+ if len(*databasePathFlag) == 0 {
+ return fmt.Errorf("'%s' not specified", FlagToolDatabasePath)
+ }
+
+ databasePath := *databasePathFlag
+ privKeyFilePath := filepath.Join(databasePath, p2p.IdentityPrivateKeyFileName)
+
+ _, err := os.Stat(privKeyFilePath)
+ switch {
+ case os.IsNotExist(err):
+ // private key does not exist
+ return fmt.Errorf("private key file (%s) does not exist", privKeyFilePath)
+
+ case err == nil || os.IsExist(err):
+ // private key file exists
+
+ default:
+ return fmt.Errorf("unable to check private key file (%s): %w", privKeyFilePath, err)
+ }
+
+ privKey, err := pem.ReadEd25519PrivateKeyFromPEMFile(privKeyFilePath)
+ if err != nil {
+ return fmt.Errorf("reading private key file for peer identity failed: %w", err)
+ }
+
+ libp2pPrivKey, err := hivep2p.Ed25519PrivateKeyToLibp2pPrivateKey(privKey)
+ if err != nil {
+ return err
+ }
+
+ return printP2PIdentity(libp2pPrivKey, libp2pPrivKey.GetPublic(), *outputJSONFlag)
+}
diff --git a/pkg/toolset/p2p_identity_gen.go b/pkg/toolset/p2p_identity_gen.go
new file mode 100644
index 000000000..b376fd5e8
--- /dev/null
+++ b/pkg/toolset/p2p_identity_gen.go
@@ -0,0 +1,136 @@
+package toolset
+
+import (
+ "crypto/ed25519"
+ "encoding/hex"
+ "fmt"
+ "os"
+ "path/filepath"
+
+ "github.com/libp2p/go-libp2p/core/crypto"
+ "github.com/libp2p/go-libp2p/core/peer"
+ "github.com/mr-tron/base58"
+ flag "github.com/spf13/pflag"
+
+ "github.com/iotaledger/hive.go/app/configuration"
+ hivecrypto "github.com/iotaledger/hive.go/crypto"
+ "github.com/iotaledger/hive.go/crypto/pem"
+ "github.com/iotaledger/iota-core/components/p2p"
+ "github.com/iotaledger/iota.go/v4/hexutil"
+)
+
+func generateP2PIdentity(args []string) error {
+
+ fs := configuration.NewUnsortedFlagSet("", flag.ContinueOnError)
+ databasePathFlag := fs.String(FlagToolOutputPath, DefaultValueP2PDatabasePath, "the path to the output folder")
+ privateKeyFlag := fs.String(FlagToolPrivateKey, "", "the p2p private key")
+ outputJSONFlag := fs.Bool(FlagToolOutputJSON, false, FlagToolDescriptionOutputJSON)
+
+ fs.Usage = func() {
+ fmt.Fprintf(os.Stderr, "Usage of %s:\n", ToolP2PIdentityGen)
+ fs.PrintDefaults()
+ println(fmt.Sprintf("\nexample: %s --%s %s --%s %s",
+ ToolP2PIdentityGen,
+ FlagToolDatabasePath,
+ DefaultValueP2PDatabasePath,
+ FlagToolPrivateKey,
+ "[PRIVATE_KEY]",
+ ))
+ }
+
+ if err := parseFlagSet(fs, args); err != nil {
+ return err
+ }
+
+ if len(*databasePathFlag) == 0 {
+ return fmt.Errorf("'%s' not specified", FlagToolDatabasePath)
+ }
+
+ databasePath := *databasePathFlag
+ privKeyFilePath := filepath.Join(databasePath, p2p.IdentityPrivateKeyFileName)
+
+ if err := os.MkdirAll(databasePath, 0700); err != nil {
+ return fmt.Errorf("could not create peer store database dir '%s': %w", databasePath, err)
+ }
+
+ _, err := os.Stat(privKeyFilePath)
+ switch {
+ case err == nil || os.IsExist(err):
+ // private key file already exists
+ return fmt.Errorf("private key file (%s) already exists", privKeyFilePath)
+
+ case os.IsNotExist(err):
+ // private key file does not exist, create a new one
+
+ default:
+ return fmt.Errorf("unable to check private key file (%s): %w", privKeyFilePath, err)
+ }
+
+ var privKey ed25519.PrivateKey
+ if privateKeyFlag != nil && len(*privateKeyFlag) > 0 {
+ privKey, err = hivecrypto.ParseEd25519PrivateKeyFromString(*privateKeyFlag)
+ if err != nil {
+ return fmt.Errorf("invalid private key given '%s': %w", *privateKeyFlag, err)
+ }
+ } else {
+ // create identity
+ _, privKey, err = ed25519.GenerateKey(nil)
+ if err != nil {
+ return fmt.Errorf("unable to generate Ed25519 private key for peer identity: %w", err)
+ }
+ }
+
+ libp2pPrivKey, libp2pPubKey, err := crypto.KeyPairFromStdKey(&privKey)
+ if err != nil {
+ return fmt.Errorf("unable to convert given private key '%s': %w", hexutil.EncodeHex(privKey), err)
+ }
+
+ if err := pem.WriteEd25519PrivateKeyToPEMFile(privKeyFilePath, privKey); err != nil {
+ return fmt.Errorf("writing private key file for peer identity failed: %w", err)
+ }
+
+ return printP2PIdentity(libp2pPrivKey, libp2pPubKey, *outputJSONFlag)
+}
+
+func printP2PIdentity(libp2pPrivKey crypto.PrivKey, libp2pPubKey crypto.PubKey, outputJSON bool) error {
+
+ type P2PIdentity struct {
+ PrivateKey string `json:"privateKey"`
+ PublicKey string `json:"publicKey"`
+ PublicKeyBase58 string `json:"publicKeyBase58"`
+ PeerID string `json:"peerId"`
+ }
+
+ privKeyBytes, err := libp2pPrivKey.Raw()
+ if err != nil {
+ return fmt.Errorf("unable to get raw private key bytes: %w", err)
+ }
+
+ pubKeyBytes, err := libp2pPubKey.Raw()
+ if err != nil {
+ return fmt.Errorf("unable to get raw public key bytes: %w", err)
+ }
+
+ peerID, err := peer.IDFromPublicKey(libp2pPubKey)
+ if err != nil {
+ return fmt.Errorf("unable to get peer identity from public key: %w", err)
+ }
+
+ identity := P2PIdentity{
+ PrivateKey: hex.EncodeToString(privKeyBytes),
+ PublicKey: hex.EncodeToString(pubKeyBytes),
+ PublicKeyBase58: base58.Encode(pubKeyBytes),
+ PeerID: peerID.String(),
+ }
+
+ if outputJSON {
+ return printJSON(identity)
+ }
+
+ fmt.Println("Your p2p private key (hex): ", identity.PrivateKey)
+ fmt.Println("Your p2p public key (hex): ", identity.PublicKey)
+ fmt.Println("Your p2p public key (base58): ", identity.PublicKeyBase58)
+ fmt.Println("Your p2p PeerID: ", identity.PeerID)
+
+ return nil
+}
diff --git a/pkg/toolset/toolset.go b/pkg/toolset/toolset.go
new file mode 100644
index 000000000..cad18b14e
--- /dev/null
+++ b/pkg/toolset/toolset.go
@@ -0,0 +1,157 @@
+package toolset
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+ "strings"
+
+ flag "github.com/spf13/pflag"
+
+ "github.com/iotaledger/hive.go/app/configuration"
+ "github.com/iotaledger/hive.go/ierrors"
+)
+
+const (
+ FlagToolDatabasePath = "databasePath"
+
+ FlagToolOutputPath = "outputPath"
+
+ FlagToolPrivateKey = "privateKey"
+ FlagToolPublicKey = "publicKey"
+
+ FlagToolHRP = "hrp"
+ FlagToolBIP32Path = "bip32Path"
+ FlagToolMnemonic = "mnemonic"
+ FlagToolSalt = "salt"
+
+ FlagToolNodeURL = "nodeURL"
+
+ FlagToolOutputJSON = "json"
+ FlagToolDescriptionOutputJSON = "format output as JSON"
+)
+
+const (
+ ToolP2PIdentityGen = "p2pidentity-gen"
+ ToolP2PExtractIdentity = "p2pidentity-extract"
+ ToolEd25519Key = "ed25519-key"
+ ToolEd25519Addr = "ed25519-addr"
+ ToolJWTApi = "jwt-api"
+ ToolNodeInfo = "node-info"
+)
+
+const (
+ DefaultValueAPIJWTTokenSalt = "IOTA"
+ DefaultValueP2PDatabasePath = "testnet/p2pstore"
+)
+
+// ShouldHandleTools checks if tools were requested.
+func ShouldHandleTools() bool {
+ args := os.Args[1:]
+
+ for _, arg := range args {
+ if strings.ToLower(arg) == "tool" || strings.ToLower(arg) == "tools" {
+ return true
+ }
+ }
+
+ return false
+}
+
+// HandleTools handles available tools.
+func HandleTools() {
+
+ args := os.Args[1:]
+ if len(args) == 1 {
+ listTools()
+ os.Exit(1)
+ }
+
+ tools := map[string]func([]string) error{
+ ToolP2PIdentityGen: generateP2PIdentity,
+ ToolP2PExtractIdentity: extractP2PIdentity,
+ ToolEd25519Key: generateEd25519Key,
+ ToolEd25519Addr: generateEd25519Address,
+ ToolJWTApi: generateJWTApiToken,
+ ToolNodeInfo: nodeInfo,
+ }
+
+ tool, exists := tools[strings.ToLower(args[1])]
+ if !exists {
+ fmt.Print("tool not found.\n\n")
+ listTools()
+ os.Exit(1)
+ }
+
+ if err := tool(args[2:]); err != nil {
+ if ierrors.Is(err, flag.ErrHelp) {
+ // help text was requested
+ os.Exit(0)
+ }
+
+ fmt.Printf("\nerror: %s\n", err)
+ os.Exit(1)
+ }
+
+ os.Exit(0)
+}
+
+func listTools() {
+ fmt.Printf("%-20s generates a p2p identity private key file\n", fmt.Sprintf("%s:", ToolP2PIdentityGen))
+ fmt.Printf("%-20s extracts the p2p identity from the private key file\n", fmt.Sprintf("%s:", ToolP2PExtractIdentity))
+ fmt.Printf("%-20s generates an ed25519 key pair\n", fmt.Sprintf("%s:", ToolEd25519Key))
+ fmt.Printf("%-20s generates an ed25519 address from a public key\n", fmt.Sprintf("%s:", ToolEd25519Addr))
+ fmt.Printf("%-20s generates a JWT token for REST-API access\n", fmt.Sprintf("%s:", ToolJWTApi))
+ fmt.Printf("%-20s queries the info endpoint of a node\n", fmt.Sprintf("%s:", ToolNodeInfo))
+}
+
+func yesOrNo(value bool) string {
+ if value {
+ return "YES"
+ }
+
+ return "NO"
+}
+
+func parseFlagSet(fs *flag.FlagSet, args []string) error {
+
+ if err := fs.Parse(args); err != nil {
+ return err
+ }
+
+ // Check if all parameters were parsed
+ if fs.NArg() != 0 {
+ return ierrors.New("too much arguments")
+ }
+
+ return nil
+}
+
+func printJSON(obj interface{}) error {
+ output, err := json.MarshalIndent(obj, "", " ")
+ if err != nil {
+ return err
+ }
+
+ fmt.Println(string(output))
+
+ return nil
+}
+
+//nolint:unused // we will need it at a later point in time
+func loadConfigFile(filePath string, parameters map[string]any) error {
+ config := configuration.New()
+ flagset := configuration.NewUnsortedFlagSet("", flag.ContinueOnError)
+
+ for namespace, pointerToStruct := range parameters {
+ config.BindParameters(flagset, namespace, pointerToStruct)
+ }
+
+ if err := config.LoadFile(filePath); err != nil {
+ return fmt.Errorf("loading config file failed: %w", err)
+ }
+
+ config.UpdateBoundParameters()
+
+ return nil
+}
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/docker-network/.env b/tools/docker-network/.env
index ce7903fb0..0a81fe337 100644
--- a/tools/docker-network/.env
+++ b/tools/docker-network/.env
@@ -5,16 +5,13 @@ COMMON_CONFIG="
--logger.disableStacktrace=false
--logger.encoding=console
--logger.outputPaths=stdout
-
---database.path=/app/data/database
-
--p2p.db.path=/app/data/peerdb
-
---profiling.bindAddress=0.0.0.0:6061
--profiling.enabled=true
-
+--profiling.bindAddress=0.0.0.0:6061
+--database.path=/app/data/database
--protocol.snapshot.path=/app/data/snapshot.bin
"
+
MANUALPEERING_CONFIG="
---p2p.peers=/dns/validator-1/tcp/14666/p2p/12D3KooWRVt4Engu27jHnF2RjfX48EqiAqJbgLfFdHNt3Vn6BtJK\
+--p2p.peers=/dns/node-1-validator/tcp/14666/p2p/12D3KooWRVt4Engu27jHnF2RjfX48EqiAqJbgLfFdHNt3Vn6BtJK\
"
diff --git a/tools/docker-network/docker-compose.yml b/tools/docker-network/docker-compose.yml
index 92ae8e023..22d21b2dc 100644
--- a/tools/docker-network/docker-compose.yml
+++ b/tools/docker-network/docker-compose.yml
@@ -4,9 +4,29 @@ x-build-iota-core: &iota-core_build
context: ${DOCKER_BUILD_CONTEXT:-../../}
dockerfile: ${DOCKERFILE_PATH:-./Dockerfile}
services:
- validator-1:
+
+###################
+# IOTA-CORE Nodes #
+###################
+
+ node-1-validator:
build: *iota-core_build
stop_grace_period: 1m
+ restart: unless-stopped
+ ulimits:
+ nofile:
+ soft: 16384
+ hard: 16384
+ networks:
+ - iota-core
+ ports:
+ - "8080:14265/tcp" # REST-API
+ - "8081:8081/tcp" # Dashboard
+ - "6081:6061/tcp" # pprof
+ - "9089:9029/tcp" # INX
+ volumes:
+ - ./docker-network.snapshot:/app/data/snapshot.bin
+ - ./config.json:/app/config.json:ro
command: >
${COMMON_CONFIG}
${MANUALPEERING_CONFIG}
@@ -17,20 +37,25 @@ services:
--validator.privateKey=443a988ea61797651217de1f4662d4d6da11fd78e67f94511453bf6576045a05293dc170d9a59474e6d81cfba7f7d924c09b25d7166bcfba606e53114d0a758b
--inx.enabled=true
--inx.bindAddress=0.0.0.0:9029
+
+ node-2-validator:
+ image: docker-network-node-1-validator:latest
+ stop_grace_period: 1m
+ restart: unless-stopped
+ ulimits:
+ nofile:
+ soft: 16384
+ hard: 16384
+ networks:
+ - iota-core
+ ports:
+ - "8070:14265/tcp" # REST-API
+ - "8071:8081/tcp" # Dashboard
+ - "6071:6061/tcp" # pprof
+ - "9029:9029/tcp" # INX
volumes:
- ./docker-network.snapshot:/app/data/snapshot.bin
- ./config.json:/app/config.json:ro
- ports:
- - "8080:8080/tcp" # web API
- - "8081:8081/tcp" # dashboard
- - "6081:6061/tcp" # pprof
- - "9089:9029/tcp" # inx
- networks:
- - iota-core
-
- validator-2:
- image: docker-network-validator-1:latest
- stop_grace_period: 1m
command: >
${COMMON_CONFIG}
${MANUALPEERING_CONFIG}
@@ -40,20 +65,25 @@ services:
--validator.privateKey=3a5d39f8b60367a17fd54dac2a32c172c8e1fd6cf74ce65f1e13edba565f281705c1de274451db8de8182d64c6ee0dca3ae0c9077e0b4330c976976171d79064
--inx.enabled=true
--inx.bindAddress=0.0.0.0:9029
+
+ node-3-validator:
+ image: docker-network-node-1-validator:latest
+ stop_grace_period: 1m
+ restart: unless-stopped
+ ulimits:
+ nofile:
+ soft: 16384
+ hard: 16384
+ networks:
+ - iota-core
+ ports:
+ - "8090:14265/tcp" # REST-API
+ - "8091:8081/tcp" # Dashboard
+ - "6091:6061/tcp" # pprof
+ - "9099:9029/tcp" # INX
volumes:
- ./docker-network.snapshot:/app/data/snapshot.bin
- ./config.json:/app/config.json:ro
- ports:
- - "8070:8080/tcp" # web API
- - "8071:8081/tcp" # dashboard
- - "6071:6061/tcp" # pprof
- - "9029:9029/tcp" # inx
- networks:
- - iota-core
-
- validator-3:
- image: docker-network-validator-1:latest
- stop_grace_period: 1m
command: >
${COMMON_CONFIG}
${MANUALPEERING_CONFIG}
@@ -63,146 +93,167 @@ services:
--validator.privateKey=db39d2fde6301d313b108dc9db1ee724d0f405f6fde966bd776365bc5f4a5fb31e4b21eb51dcddf65c20db1065e1f1514658b23a3ddbf48d30c0efc926a9a648
--inx.enabled=true
--inx.bindAddress=0.0.0.0:9029
+
+ node-4:
+ image: docker-network-node-1-validator:latest
+ stop_grace_period: 1m
+ restart: unless-stopped
+ ulimits:
+ nofile:
+ soft: 16384
+ hard: 16384
+ networks:
+ - iota-core
+ ports:
+ - "8040:14265/tcp" # REST-API
+ - "8041:8081/tcp" # Dashboard
+ - "6041:6061/tcp" # pprof
+ - "9049:9029/tcp" # INX
volumes:
- ./docker-network.snapshot:/app/data/snapshot.bin
- ./config.json:/app/config.json:ro
- ports:
- - "8090:8080/tcp" # web API
- - "8091:8081/tcp" # dashboard
- - "6091:6061/tcp" # pprof
- - "9099:9029/tcp" # inx
- networks:
- - iota-core
-
- node-1:
- image: docker-network-validator-1:latest
- stop_grace_period: 1m
command: >
${COMMON_CONFIG}
${MANUALPEERING_CONFIG}
--p2p.identityPrivateKey=03feb3bcd25e57f75697bb329e6e0100680431e4c45c85bc013da2aea9e9d0345e08a0c37407dc62369deebc64cb0fb3ea26127d19d141ee7fb8eaa6b92019d7
--inx.enabled=true
--inx.bindAddress=0.0.0.0:9029
+
+ node-5:
+ image: docker-network-node-1-validator:latest
+ stop_grace_period: 1m
+ restart: unless-stopped
+ ulimits:
+ nofile:
+ soft: 16384
+ hard: 16384
+ networks:
+ - iota-core
+ ports:
+ - "8030:14265/tcp" # REST-API
+ - "8031:8081/tcp" # Dashboard
+ - "6031:6061/tcp" # pprof
+ - "9039:9029/tcp" # INX
volumes:
- ./docker-network.snapshot:/app/data/snapshot.bin
- ./config.json:/app/config.json:ro
- ports:
- - "8040:8080/tcp" # web API
- - "8041:8081/tcp" # dashboard
- - "6041:6061/tcp" # pprof
- - "9049:9029/tcp" # inx
- networks:
- - iota-core
-
- node-2:
- image: docker-network-validator-1:latest
- stop_grace_period: 1m
command: >
${COMMON_CONFIG}
${MANUALPEERING_CONFIG}
--p2p.identityPrivateKey=7d1491df3ef334dee988d6cdfc4b430b996d520bd63375a01d6754f8cee979b855b200fbea8c936ea1937a27e6ad72a7c9a21c1b17c2bd3c11f1f6994d813446
--inx.enabled=true
--inx.bindAddress=0.0.0.0:9029
- volumes:
- - ./docker-network.snapshot:/app/data/snapshot.bin
- - ./config.json:/app/config.json:ro
+
+##################################################################
+# Monitoring #
+##################################################################
+
+ prometheus:
+ image: prom/prometheus:latest
+ stop_grace_period: 1m
+ restart: unless-stopped
+ depends_on:
+ node-1-validator:
+ condition: service_started
+ networks:
+ - iota-core
ports:
- - "8030:8080/tcp" # web API
- - "8031:8081/tcp" # dashboard
- - "6031:6061/tcp" # pprof
- - "9039:9029/tcp" # inx
+ - "9090:9090" # prometheus
+ secrets:
+ - prometheus.yml
+ command:
+ - "--config.file=/run/secrets/prometheus.yml"
+ profiles:
+ - monitoring
+
+ grafana:
+ image: grafana/grafana:9.5.6
+ restart: unless-stopped
networks:
- iota-core
+ ports:
+ - "3000:3000" # Grafana Dashboard
+ volumes:
+ - grafana_data:/var/lib/grafana
+ - ./grafana/:/etc/grafana/:ro
+ environment:
+ - GF_ALERTING_ENABLED=true
+ - GF_UNIFIED_ALERTING_ENABLED=false
+ - GF_DASHBOARDS_DEFAULT_HOME_DASHBOARD_PATH=/etc/grafana/provisioning/dashboards/local_dashboard.json
+ profiles:
+ - monitoring
+
+##################
+# INX Extensions #
+##################
inx-indexer:
image: iotaledger/inx-indexer:2.0-alpha
stop_grace_period: 1m
+ restart: unless-stopped
depends_on:
- validator-1:
- condition: service_started
- command:
- - "--inx.address=validator-1:9029"
- - "--restAPI.bindAddress=inx-indexer:9091"
+ node-1-validator:
+ condition: service_healthy
+ ulimits:
+ nofile:
+ soft: 16384
+ hard: 16384
networks:
- iota-core
+ command: >
+ --inx.address=node-1-validator:9029
+ --restAPI.bindAddress=inx-indexer:9091
inx-blockissuer:
image: iotaledger/inx-blockissuer:1.0-alpha
stop_grace_period: 1m
+ restart: unless-stopped
depends_on:
- validator-1:
- condition: service_started
+ node-1-validator:
+ condition: service_healthy
inx-indexer:
condition: service_started
- restart: on-failure
- environment:
- - "BLOCKISSUER_PRV_KEY=432c624ca3260f910df35008d5c740593b222f1e196e6cdb8cd1ad080f0d4e33997be92a22b1933f36e26fba5f721756f95811d6b4ae21564197c2bfa4f28270"
- command:
- - "--inx.address=validator-1:9029"
- - "--restAPI.bindAddress=inx-blockissuer:9086"
- - "--blockIssuer.accountAddress=rms1prkursay9fs2qjmfctamd6yxg9x8r3ry47786x0mvwek4qr9xd9d5c6gkun"
networks:
- iota-core
+ environment:
+ - "BLOCKISSUER_PRV_KEY=432c624ca3260f910df35008d5c740593b222f1e196e6cdb8cd1ad080f0d4e33997be92a22b1933f36e26fba5f721756f95811d6b4ae21564197c2bfa4f28270"
+ command: >
+ --inx.address=node-1-validator:9029
+ --restAPI.bindAddress=inx-blockissuer:9086
+ --blockIssuer.accountAddress=rms1prkursay9fs2qjmfctamd6yxg9x8r3ry47786x0mvwek4qr9xd9d5c6gkun
+ --blockIssuer.proofOfWork.targetTrailingZeros=5
inx-faucet:
image: iotaledger/inx-faucet:2.0-alpha
stop_grace_period: 1m
+ restart: unless-stopped
depends_on:
- validator-1:
- condition: service_started
+ node-1-validator:
+ condition: service_healthy
inx-indexer:
condition: service_started
inx-blockissuer:
condition: service_started
- restart: on-failure
- environment:
- - "FAUCET_PRV_KEY=de52b9964dda96564e9fab362ab16c2669c715c6a2a853bece8a25fc58c599755b938327ea463e0c323c0fd44f6fc1843ed94daecc6909c6043d06b7152e4737"
- command:
- - "--inx.address=validator-1:9029"
- - "--faucet.bindAddress=inx-faucet:8091"
- ports:
- - "8088:8091/tcp" # faucet frontend
networks:
- iota-core
-
- prometheus:
- image: prom/prometheus:latest
- profiles:
- - grafana
ports:
- - "9090:9090"
- command:
- - --config.file=/run/secrets/prometheus.yml
- secrets:
- - prometheus.yml
- networks:
- - iota-core
- depends_on:
- - validator-1
-
- grafana:
- image: grafana/grafana:9.5.6
- profiles:
- - grafana
+ - "8088:8091/tcp" # Faucet Frontend
environment:
- - GF_ALERTING_ENABLED=true
- - GF_UNIFIED_ALERTING_ENABLED=false
- - GF_DASHBOARDS_DEFAULT_HOME_DASHBOARD_PATH=/etc/grafana/provisioning/dashboards/local_dashboard.json
- networks:
- - iota-core
- ports:
- - "3000:3000"
- volumes:
- - grafana_data:/var/lib/grafana
- - ./grafana/:/etc/grafana/:ro
+ - "FAUCET_PRV_KEY=de52b9964dda96564e9fab362ab16c2669c715c6a2a853bece8a25fc58c599755b938327ea463e0c323c0fd44f6fc1843ed94daecc6909c6043d06b7152e4737"
+ command: >
+ --inx.address=node-1-validator:9029
+ --faucet.bindAddress=inx-faucet:8091
+ --faucet.rateLimit.enabled=false
# Create our own network
networks:
iota-core:
driver: bridge
+
# Named Docker volumes for data persistence
volumes:
grafana_data:
+
# read only files to load in the containers that may be shared across containers
secrets:
prometheus.yml:
diff --git a/tools/docker-network/grafana/provisioning/dashboards/local_dashboard.json b/tools/docker-network/grafana/provisioning/dashboards/local_dashboard.json
index f464af4c6..571712c82 100644
--- a/tools/docker-network/grafana/provisioning/dashboards/local_dashboard.json
+++ b/tools/docker-network/grafana/provisioning/dashboards/local_dashboard.json
@@ -1340,8 +1340,8 @@
{
"current": {
"selected": false,
- "text": "validator-3:9311",
- "value": "validator-3:9311"
+ "text": "node-3-validator:9311",
+ "value": "node-3-validator:9311"
},
"datasource": {
"type": "prometheus",
diff --git a/tools/docker-network/grafana/provisioning/dashboards/local_dashboard_old.json b/tools/docker-network/grafana/provisioning/dashboards/local_dashboard_old.json
index 71fb84826..320e8c1a6 100644
--- a/tools/docker-network/grafana/provisioning/dashboards/local_dashboard_old.json
+++ b/tools/docker-network/grafana/provisioning/dashboards/local_dashboard_old.json
@@ -4847,8 +4847,8 @@
{
"current": {
"selected": false,
- "text": "validator-3:9311",
- "value": "validator-3:9311"
+ "text": "node-3-validator:9311",
+ "value": "node-3-validator:9311"
},
"datasource": {
"type": "prometheus",
diff --git a/tools/docker-network/grafana/provisioning/dashboards/scheduler-metrics.json b/tools/docker-network/grafana/provisioning/dashboards/scheduler-metrics.json
index c3d8798c3..e76dd6b1c 100644
--- a/tools/docker-network/grafana/provisioning/dashboards/scheduler-metrics.json
+++ b/tools/docker-network/grafana/provisioning/dashboards/scheduler-metrics.json
@@ -772,8 +772,8 @@
{
"current": {
"selected": true,
- "text": "validator-2:9311",
- "value": "validator-2:9311"
+ "text": "node-2-validator:9311",
+ "value": "node-2-validator:9311"
},
"datasource": {
"type": "prometheus",
diff --git a/tools/docker-network/prometheus.yml b/tools/docker-network/prometheus.yml
index 28817e795..07728e80f 100644
--- a/tools/docker-network/prometheus.yml
+++ b/tools/docker-network/prometheus.yml
@@ -3,11 +3,11 @@ scrape_configs:
scrape_interval: 5s
static_configs:
- targets:
- - validator-1:9311
- - validator-2:9311
- - validator-3:9311
- - node-1:9311
- - node-2:9311
+ - node-1-validator:9311
+ - node-2-validator:9311
+ - node-3-validator:9311
+ - node-4:9311
+ - node-5:9311
dns_sd_configs:
- names:
- 'peer_replica'
diff --git a/tools/docker-network/run.sh b/tools/docker-network/run.sh
index e1021c8e1..b137a67a4 100755
--- a/tools/docker-network/run.sh
+++ b/tools/docker-network/run.sh
@@ -6,12 +6,12 @@ function join { local IFS="$1"; shift; echo "$*"; }
# All parameters can be optional now, just make sure we don't have too many
if [[ $# -gt 4 ]] ; then
- echo 'Call with ./run [replicas=1|2|3|...] [grafana=0|1] [feature=0|1]'
+ echo 'Call with ./run [replicas=1|2|3|...] [monitoring=0|1] [feature=0|1]'
exit 0
fi
REPLICAS=${1:-1}
-GRAFANA=${2:-0}
+MONITORING=${2:-0}
FEATURE=${3:-0}
DOCKER_COMPOSE_FILE=docker-compose.yml
@@ -67,9 +67,9 @@ export IOTA_CORE_PEER_REPLICAS=$REPLICAS
# Profiles is created to set which docker profiles to run
# https://docs.docker.com/compose/profiles/
PROFILES=()
-if [ $GRAFANA -ne 0 ]
+if [ $MONITORING -ne 0 ]
then
- PROFILES+=("grafana")
+ PROFILES+=("monitoring")
fi
export COMPOSE_PROFILES=$(join , ${PROFILES[@]})
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 05dbccf7f..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.genesisKeyManager.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
- genesisKeyManager *mock.KeyManager
-
- 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.NewKeyManager(genesisSeed, 0).Address(iotago.AddressEd25519)
-
- f := &faucet{
- clt: clt,
- account: blockhandler.AccountFromParams(faucetParams.faucetAccountID, faucetParams.faucetPrivateKey),
- genesisKeyManager: mock.NewKeyManager(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.genesisKeyManager.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.genesisKeyManager.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.genesisKeyManager.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.genesisKeyManager.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 327d6c150..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) {
- keyManager := mock.NewKeyManager(a.seed[:], a.latestUsedIndex+1)
- privKey, _ := keyManager.KeyPair()
- receiverAddr := keyManager.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.NewKeyManager(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.genesisKeyManager.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 349c97b97..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))
- keyManager := mock.NewKeyManager(w.seed[:], index)
- //nolint:forcetypeassert
- addr := keyManager.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.NewKeyManager(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.NewKeyManager(w.seed[:], index)
-
- return hdWallet.AddressSigner()
-}
-
-func (w *Wallet) KeyPair(index uint64) (ed25519.PrivateKey, ed25519.PublicKey) {
- w.RLock()
- defer w.RUnlock()
- hdWallet := mock.NewKeyManager(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 91ec4b3b6..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-20231023190719-1a9daaa83ca8
- 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 f66d3f1bb..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-20231023190719-1a9daaa83ca8 h1:81aWESXFC04iKI9I140eDrBb9zBWXfVoAUMp9berk0c=
-github.com/iotaledger/iota.go/v4 v4.0.0-20231023190719-1a9daaa83ca8/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 2be0e01c8..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/testsuite/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
-}
diff --git a/tools/gendoc/go.mod b/tools/gendoc/go.mod
index 65a416839..554b4ebab 100644
--- a/tools/gendoc/go.mod
+++ b/tools/gendoc/go.mod
@@ -71,9 +71,9 @@ require (
github.com/iotaledger/hive.go/runtime 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/iotaledger/inx-app v1.0.0-rc.3.0.20231023191159-38919c4705e0 // indirect
- github.com/iotaledger/inx/go v1.0.0-rc.2.0.20231023190837-6e7b2cdfd4fd // indirect
- github.com/iotaledger/iota.go/v4 v4.0.0-20231023190719-1a9daaa83ca8 // indirect
+ github.com/iotaledger/inx-app v1.0.0-rc.3.0.20231026154618-820b8eaed2cc // indirect
+ github.com/iotaledger/inx/go v1.0.0-rc.2.0.20231026154523-9ee0c47a283a // indirect
+ github.com/iotaledger/iota.go/v4 v4.0.0-20231026154111-efd63ff4f03d // indirect
github.com/ipfs/boxo v0.13.1 // indirect
github.com/ipfs/go-cid v0.4.1 // indirect
github.com/ipfs/go-datastore v0.6.0 // indirect
@@ -156,6 +156,7 @@ require (
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect
+ github.com/wollac/iota-crypto-demo v0.0.0-20221117162917-b10619eccb98 // indirect
github.com/zyedidia/generic v1.2.1 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/otel v1.19.0 // indirect
diff --git a/tools/gendoc/go.sum b/tools/gendoc/go.sum
index c13517eea..b5bc4c338 100644
--- a/tools/gendoc/go.sum
+++ b/tools/gendoc/go.sum
@@ -309,12 +309,12 @@ github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231020115340-13da292
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/inx-app v1.0.0-rc.3.0.20231023191159-38919c4705e0 h1:/8pbFXhTSroJvjJMfJqfHjzoT9N8B4LUY3SbKruD5MM=
-github.com/iotaledger/inx-app v1.0.0-rc.3.0.20231023191159-38919c4705e0/go.mod h1:My1SB4vZj42EgTDNJ/dgW8lUpLNmvtzu8f89J5y2kP0=
-github.com/iotaledger/inx/go v1.0.0-rc.2.0.20231023190837-6e7b2cdfd4fd h1:hh5mAnnaZHOYAi4CIqR9K/mv786ex9AQgpisbJ4ZMow=
-github.com/iotaledger/inx/go v1.0.0-rc.2.0.20231023190837-6e7b2cdfd4fd/go.mod h1:MK0SHfNicBmcaZb3qS3tA8NEJIWKNbcNtNNKuSDKqXY=
-github.com/iotaledger/iota.go/v4 v4.0.0-20231023190719-1a9daaa83ca8 h1:81aWESXFC04iKI9I140eDrBb9zBWXfVoAUMp9berk0c=
-github.com/iotaledger/iota.go/v4 v4.0.0-20231023190719-1a9daaa83ca8/go.mod h1:jqbLYq4a/FwuiPBqFfkAwwxU8vs3+kReRq2/tyX5qRA=
+github.com/iotaledger/inx-app v1.0.0-rc.3.0.20231026154618-820b8eaed2cc h1:Foz7Q1vNh0Ts+YTEODHO3LSKVGM/uNV3RqbBJS6u7yA=
+github.com/iotaledger/inx-app v1.0.0-rc.3.0.20231026154618-820b8eaed2cc/go.mod h1:gaQbe/L+wjjUeQj5N8+o/XdZnSosFmDQfxmfyrK05hc=
+github.com/iotaledger/inx/go v1.0.0-rc.2.0.20231026154523-9ee0c47a283a h1:8JbC44pNud1rT091fJA4bDC+35MozksapuCXf8M1kmg=
+github.com/iotaledger/inx/go v1.0.0-rc.2.0.20231026154523-9ee0c47a283a/go.mod h1:iXG/tO+GQZQzgIUyITnQDigb6Ny1wSueHFIYne4HBkc=
+github.com/iotaledger/iota.go/v4 v4.0.0-20231026154111-efd63ff4f03d h1:gcJz0J3xFELIPT7y4xqW+q25oOcK6QMlxNnrfFu8srA=
+github.com/iotaledger/iota.go/v4 v4.0.0-20231026154111-efd63ff4f03d/go.mod h1:jqbLYq4a/FwuiPBqFfkAwwxU8vs3+kReRq2/tyX5qRA=
github.com/ipfs/boxo v0.13.1 h1:nQ5oQzcMZR3oL41REJDcTbrvDvuZh3J9ckc9+ILeRQI=
github.com/ipfs/boxo v0.13.1/go.mod h1:btrtHy0lmO1ODMECbbEY1pxNtrLilvKSYLoGQt1yYCk=
github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s=
@@ -668,6 +668,8 @@ github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0 h1:GDDkbFiaK8jsSD
github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw=
github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k=
github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc=
+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=
diff --git a/tools/genesis-snapshot/go.mod b/tools/genesis-snapshot/go.mod
index 0743c2ff7..4a1a2b7ab 100644
--- a/tools/genesis-snapshot/go.mod
+++ b/tools/genesis-snapshot/go.mod
@@ -10,7 +10,7 @@ require (
github.com/iotaledger/hive.go/lo 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.go/v4 v4.0.0-20231023190719-1a9daaa83ca8
+ github.com/iotaledger/iota.go/v4 v4.0.0-20231026154111-efd63ff4f03d
github.com/mr-tron/base58 v1.2.0
github.com/spf13/pflag v1.0.5
golang.org/x/crypto v0.14.0
diff --git a/tools/genesis-snapshot/go.sum b/tools/genesis-snapshot/go.sum
index f50a589ed..1d01d2de8 100644
--- a/tools/genesis-snapshot/go.sum
+++ b/tools/genesis-snapshot/go.sum
@@ -52,8 +52,8 @@ github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231020115340-13da292
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-20231023190719-1a9daaa83ca8 h1:81aWESXFC04iKI9I140eDrBb9zBWXfVoAUMp9berk0c=
-github.com/iotaledger/iota.go/v4 v4.0.0-20231023190719-1a9daaa83ca8/go.mod h1:jqbLYq4a/FwuiPBqFfkAwwxU8vs3+kReRq2/tyX5qRA=
+github.com/iotaledger/iota.go/v4 v4.0.0-20231026154111-efd63ff4f03d h1:gcJz0J3xFELIPT7y4xqW+q25oOcK6QMlxNnrfFu8srA=
+github.com/iotaledger/iota.go/v4 v4.0.0-20231026154111-efd63ff4f03d/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/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
diff --git a/tools/genesis-snapshot/presets/presets.go b/tools/genesis-snapshot/presets/presets.go
index 67f67df43..373a52929 100644
--- a/tools/genesis-snapshot/presets/presets.go
+++ b/tools/genesis-snapshot/presets/presets.go
@@ -21,7 +21,7 @@ var Base = []options.Option[snapshotcreator.Options]{
snapshotcreator.WithProtocolParameters(
iotago.NewV3ProtocolParameters(
iotago.WithNetworkOptions("default", "rms"),
- iotago.WithSupplyOptions(10_000_000_000, 100, 1, 10, 100, 100, 100),
+ iotago.WithSupplyOptions(4_600_000_000_000_000, 100, 1, 10, 100, 100, 100),
iotago.WithTimeProviderOptions(1696841745, 10, 13),
iotago.WithLivenessOptions(30, 30, 7, 14, 30),
// increase/decrease threshold = fraction * slotDurationInSeconds * schedulerRate
@@ -36,25 +36,8 @@ var Base = []options.Option[snapshotcreator.Options]{
var Docker = []options.Option[snapshotcreator.Options]{
snapshotcreator.WithFilePath("docker-network.snapshot"),
- snapshotcreator.WithBasicOutputs(
- /*
- inx-faucet
-
- ed25519 private key: de52b9964dda96564e9fab362ab16c2669c715c6a2a853bece8a25fc58c599755b938327ea463e0c323c0fd44f6fc1843ed94daecc6909c6043d06b7152e4737
- ed25519 public key: 5b938327ea463e0c323c0fd44f6fc1843ed94daecc6909c6043d06b7152e4737
- ed25519 address: 2f64f9d179991f50542b01e034fa043b195403875b8677efaf196b41c88803d0
- bech32 address: rms1qqhkf7w30xv375z59vq7qd86qsa3j4qrsadcval04uvkkswg3qpaqf4hga2
-
- => restricted address with mana enabled: rms19qqz7e8e69uej86s2s4srcp5lgzrkx25qwr4hpnha7h3j66pezyq85qpqgqjjc5k
- */
- snapshotcreator.BasicOutputDetails{
- Address: lo.Return2(iotago.ParseBech32("rms19qqz7e8e69uej86s2s4srcp5lgzrkx25qwr4hpnha7h3j66pezyq85qpqgqjjc5k")),
- Amount: 5_000_000_000,
- Mana: 10_000_000,
- },
- ),
snapshotcreator.WithAccounts(
- snapshotcreator.AccountDetails{ // validator-1
+ snapshotcreator.AccountDetails{ // node-1-validator
AccountID: blake2b.Sum256(lo.PanicOnErr(hexutil.DecodeHex("0x293dc170d9a59474e6d81cfba7f7d924c09b25d7166bcfba606e53114d0a758b"))),
Address: iotago.Ed25519AddressFromPubKey(lo.PanicOnErr(hexutil.DecodeHex("0x293dc170d9a59474e6d81cfba7f7d924c09b25d7166bcfba606e53114d0a758b"))),
Amount: mock.MinValidatorAccountAmount,
@@ -66,7 +49,7 @@ var Docker = []options.Option[snapshotcreator.Options]{
StakedAmount: mock.MinValidatorAccountAmount,
Mana: iotago.Mana(mock.MinValidatorAccountAmount),
},
- snapshotcreator.AccountDetails{ // validator-2
+ snapshotcreator.AccountDetails{ // node-2-validator
AccountID: blake2b.Sum256(lo.PanicOnErr(hexutil.DecodeHex("0x05c1de274451db8de8182d64c6ee0dca3ae0c9077e0b4330c976976171d79064"))),
Address: iotago.Ed25519AddressFromPubKey(lo.PanicOnErr(hexutil.DecodeHex("0x05c1de274451db8de8182d64c6ee0dca3ae0c9077e0b4330c976976171d79064"))),
Amount: mock.MinValidatorAccountAmount,
@@ -78,7 +61,7 @@ var Docker = []options.Option[snapshotcreator.Options]{
StakedAmount: mock.MinValidatorAccountAmount,
Mana: iotago.Mana(mock.MinValidatorAccountAmount),
},
- snapshotcreator.AccountDetails{ // validator-3
+ snapshotcreator.AccountDetails{ // node-3-validator
AccountID: blake2b.Sum256(lo.PanicOnErr(hexutil.DecodeHex("0x1e4b21eb51dcddf65c20db1065e1f1514658b23a3ddbf48d30c0efc926a9a648"))),
Address: iotago.Ed25519AddressFromPubKey(lo.PanicOnErr(hexutil.DecodeHex("0x1e4b21eb51dcddf65c20db1065e1f1514658b23a3ddbf48d30c0efc926a9a648"))),
Amount: mock.MinValidatorAccountAmount,
@@ -108,10 +91,27 @@ var Docker = []options.Option[snapshotcreator.Options]{
Mana: iotago.Mana(mock.MinIssuerAccountAmount),
},
),
+ snapshotcreator.WithBasicOutputs(
+ /*
+ inx-faucet
+
+ ed25519 private key: de52b9964dda96564e9fab362ab16c2669c715c6a2a853bece8a25fc58c599755b938327ea463e0c323c0fd44f6fc1843ed94daecc6909c6043d06b7152e4737
+ ed25519 public key: 5b938327ea463e0c323c0fd44f6fc1843ed94daecc6909c6043d06b7152e4737
+ ed25519 address: 2f64f9d179991f50542b01e034fa043b195403875b8677efaf196b41c88803d0
+ bech32 address: rms1qqhkf7w30xv375z59vq7qd86qsa3j4qrsadcval04uvkkswg3qpaqf4hga2
+
+ => restricted address with mana enabled: rms1xqqz7e8e69uej86s2s4srcp5lgzrkx25qwr4hpnha7h3j66pezyq85qpqg55v3ur
+ */
+ snapshotcreator.BasicOutputDetails{
+ Address: lo.Return2(iotago.ParseBech32("rms1xqqz7e8e69uej86s2s4srcp5lgzrkx25qwr4hpnha7h3j66pezyq85qpqg55v3ur")),
+ Amount: 1_000_000_000_000_000,
+ Mana: 10_000_000,
+ },
+ ),
snapshotcreator.WithProtocolParameters(
iotago.NewV3ProtocolParameters(
iotago.WithNetworkOptions("docker", "rms"),
- iotago.WithSupplyOptions(10_000_000_000, 1, 1, 10, 100, 100, 100),
+ iotago.WithSupplyOptions(4_600_000_000_000_000, 1, 1, 10, 100, 100, 100),
iotago.WithTimeProviderOptions(time.Now().Unix(), 10, 13),
iotago.WithLivenessOptions(30, 30, 7, 14, 30),
// increase/decrease threshold = fraction * slotDurationInSeconds * schedulerRate
@@ -125,7 +125,7 @@ var Docker = []options.Option[snapshotcreator.Options]{
var Feature = []options.Option[snapshotcreator.Options]{
snapshotcreator.WithFilePath("docker-network.snapshot"),
snapshotcreator.WithAccounts(
- snapshotcreator.AccountDetails{
+ snapshotcreator.AccountDetails{ // node-01
AccountID: blake2b.Sum256(lo.PanicOnErr(hexutil.DecodeHex("0x01fb6b9db5d96240aef00bc950d1c67a6494513f6d7cf784e57b4972b96ab2fe"))),
Address: iotago.Ed25519AddressFromPubKey(lo.PanicOnErr(hexutil.DecodeHex("0x01fb6b9db5d96240aef00bc950d1c67a6494513f6d7cf784e57b4972b96ab2fe"))),
Amount: mock.MinValidatorAccountAmount,
@@ -137,7 +137,7 @@ var Feature = []options.Option[snapshotcreator.Options]{
StakedAmount: mock.MinValidatorAccountAmount,
Mana: iotago.Mana(mock.MinValidatorAccountAmount),
},
- snapshotcreator.AccountDetails{
+ snapshotcreator.AccountDetails{ // node-02
AccountID: blake2b.Sum256(lo.PanicOnErr(hexutil.DecodeHex("0x83e7f71a440afd48981a8b4684ddae24434b7182ce5c47cfb56ac528525fd4b6"))),
Address: iotago.Ed25519AddressFromPubKey(lo.PanicOnErr(hexutil.DecodeHex("0x83e7f71a440afd48981a8b4684ddae24434b7182ce5c47cfb56ac528525fd4b6"))),
Amount: mock.MinValidatorAccountAmount,
@@ -149,7 +149,7 @@ var Feature = []options.Option[snapshotcreator.Options]{
StakedAmount: mock.MinValidatorAccountAmount,
Mana: iotago.Mana(mock.MinValidatorAccountAmount),
},
- snapshotcreator.AccountDetails{
+ snapshotcreator.AccountDetails{ // node-03
AccountID: blake2b.Sum256(lo.PanicOnErr(hexutil.DecodeHex("0xac628986b2ef52a1679f2289fcd7b4198476976dea4c30ae34ff04ae52e14805"))),
Address: iotago.Ed25519AddressFromPubKey(lo.PanicOnErr(hexutil.DecodeHex("0xac628986b2ef52a1679f2289fcd7b4198476976dea4c30ae34ff04ae52e14805"))),
Amount: mock.MinValidatorAccountAmount,
@@ -161,11 +161,43 @@ var Feature = []options.Option[snapshotcreator.Options]{
StakedAmount: mock.MinValidatorAccountAmount,
Mana: iotago.Mana(mock.MinValidatorAccountAmount),
},
+ snapshotcreator.AccountDetails{
+ /*
+ inx-blockissuer
+
+ ed25519 public key: 670a1a20ddb02a6cec53ec3196bc7d5bd26df2f5a6ca90b5fffd71364f104b25
+ ed25519 address: 3b07e3e84c1276f0b9d35cf218b3763e0cbdadaa7ca38588de2170c31b38e9bb
+ bech32 address: rms1pqas0clgfsf8du9e6dw0yx9nwclqe0dd4f728pvgmcshpscm8r5mkddrrfc
+ */
+ AccountID: blake2b.Sum256(lo.PanicOnErr(hexutil.DecodeHex("0x670a1a20ddb02a6cec53ec3196bc7d5bd26df2f5a6ca90b5fffd71364f104b25"))),
+ Address: iotago.Ed25519AddressFromPubKey(lo.PanicOnErr(hexutil.DecodeHex("0x670a1a20ddb02a6cec53ec3196bc7d5bd26df2f5a6ca90b5fffd71364f104b25"))),
+ Amount: testsuite.MinIssuerAccountAmount,
+ IssuerKey: iotago.Ed25519PublicKeyBlockIssuerKeyFromPublicKey(ed25519.PublicKey(lo.PanicOnErr(hexutil.DecodeHex("0x670a1a20ddb02a6cec53ec3196bc7d5bd26df2f5a6ca90b5fffd71364f104b25")))),
+ ExpirySlot: iotago.MaxSlotIndex,
+ BlockIssuanceCredits: iotago.MaxBlockIssuanceCredits / 4,
+ Mana: iotago.Mana(testsuite.MinIssuerAccountAmount),
+ },
+ ),
+ snapshotcreator.WithBasicOutputs(
+ /*
+ inx-faucet
+
+ ed25519 public key: dcd760a51cfafe901f4ca0745d399af7146028af643e8a339c7bb82fbb1be7f9
+ ed25519 address: 48acd764f626523646d5ccf22f807e96d30b7ab0064f370b66fa811985985ec4
+ bech32 address: rms1qpy2e4my7cn9ydjx6hx0ytuq06tdxzm6kqry7dctvmagzxv9np0vg9c55n4
+
+ => restricted address with mana enabled: rms1xqqy3txhvnmzv53kgm2ueu30splfd5ct02cqvnehpdn04qgeskv9a3qpqgrhlhv3
+ */
+ snapshotcreator.BasicOutputDetails{
+ Address: lo.Return2(iotago.ParseBech32("rms1xqqy3txhvnmzv53kgm2ueu30splfd5ct02cqvnehpdn04qgeskv9a3qpqgrhlhv3")),
+ Amount: 1_000_000_000_000_000,
+ Mana: 10_000_000,
+ },
),
snapshotcreator.WithProtocolParameters(
iotago.NewV3ProtocolParameters(
iotago.WithNetworkOptions("feature", "rms"),
- iotago.WithSupplyOptions(10_000_000_000, 100, 1, 10, 100, 100, 100),
+ iotago.WithSupplyOptions(4_600_000_000_000_000, 100, 1, 10, 100, 100, 100),
iotago.WithTimeProviderOptions(1697631694, 10, 13),
iotago.WithLivenessOptions(30, 30, 10, 20, 30),
// increase/decrease threshold = fraction * slotDurationInSeconds * schedulerRate