diff --git a/.github/workflows/build_tools.yml b/.github/workflows/build_tools.yml index 89c14f9dc..18481f055 100644 --- a/.github/workflows/build_tools.yml +++ b/.github/workflows/build_tools.yml @@ -3,13 +3,12 @@ name: Build internal tools on: pull_request: paths: - - 'tools/evil-spammer/**' - 'tools/genesis-snapshot/**' jobs: build: name: Import Check - runs-on: ubuntu-latest + runs-on: self-hosted steps: - name: Checkout repository @@ -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/.github/workflows/docker-network-health.yml b/.github/workflows/docker-network-health.yml new file mode 100644 index 000000000..ee5b1b08b --- /dev/null +++ b/.github/workflows/docker-network-health.yml @@ -0,0 +1,66 @@ +name: Run Docker Network and Check Health + +on: + workflow_dispatch: + pull_request: + paths-ignore: + - 'documentation/**' + - 'scripts/**' + - 'tools/**' + +concurrency: + group: run-and-check-group + cancel-in-progress: false + +jobs: + run-and-check: + runs-on: self-hosted + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Run network, wait and check health + run: | + set -x + + # Run network + cd ./tools/docker-network + timeout 10m ./run.sh 0 0 & + RUN_PID=$! + + # Wait for node-4 to be created before querying it + timeout 10m bash -c 'until docker ps | grep docker-network-node-4; do sleep 5; done' & + + # Wait for any of the two processes to exit + wait -n || exit 1 + + # Additional 10 seconds wait to allow the API to come up + sleep 10 + + # Health check + SUCCESS=false + while true; do + OUTPUT=$(curl -o /dev/null -s -w "%{http_code}\n" http://localhost:8080/health) + if [[ $OUTPUT -eq 200 ]]; then + SUCCESS=true + kill -s SIGINT $RUN_PID + break + # curl will return a connection refused when the network is tear down from the timeout. + elif [[ $OUTPUT -eq 000 ]]; then + echo "Connection refused. Failing the action." + break + fi + sleep 5 + done + + if [[ ! $SUCCESS ]]; then + echo "Health check never returned 200. Failing the action." + exit 1 + fi + + - name: Cleanup + run: | + cd ./tools/docker-network + docker compose kill + docker compose down -v diff --git a/.github/workflows/feature-network-deploy.yml b/.github/workflows/feature-network-deploy.yml index c4c71e425..812411683 100644 --- a/.github/workflows/feature-network-deploy.yml +++ b/.github/workflows/feature-network-deploy.yml @@ -70,7 +70,7 @@ jobs: - name: Ansible deploy env: CUSTOM_SNAPSHOT_URL: '${{ github.event.inputs.snapshotUrl }}' - DEFAULT_SNAPSHOT_URL: 'https://0x0.st/HJXh.bin' + DEFAULT_SNAPSHOT_URL: 'https://0x0.st/HywH.bin' NETWORK_ENVIRONMENT: '${{ secrets.NETWORK_ENVIRONMENT }}' IOTA_CORE_DOCKER_IMAGE_REPO: 'iotaledger/iota-core' IOTA_CORE_DOCKER_IMAGE_TAG: 'feature' diff --git a/.gitignore b/.gitignore index b404776d6..56ae79587 100644 --- a/.gitignore +++ b/.gitignore @@ -15,8 +15,7 @@ iota-core # IDE related files .vscode/ .idea/ -go.work -go.work.sum +go.work* # dist packages dist/ @@ -30,6 +29,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..5f840eb4c 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" @@ -14,7 +17,7 @@ import ( "github.com/iotaledger/iota-core/components/protocol" "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 +31,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, @@ -38,7 +47,6 @@ func App() *app.App { debugapi.Component, metricstracker.Component, protocol.Component, - validator.Component, dashboardmetrics.Component, dashboard.Component, metrics.Component, @@ -63,5 +71,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/dashboard/component.go b/components/dashboard/component.go index 6fb614105..7932e12bd 100644 --- a/components/dashboard/component.go +++ b/components/dashboard/component.go @@ -155,7 +155,7 @@ func currentNodeStatus() *nodestatus { status.TangleTime = tangleTime{ Synced: syncStatus.NodeSynced, - Bootstrapped: deps.Protocol.Engines.Main.Get().SyncManager.IsBootstrapped(), + Bootstrapped: syncStatus.NodeBootstrapped, AcceptedBlockSlot: int64(syncStatus.LastAcceptedBlockSlot), ConfirmedBlockSlot: int64(syncStatus.LastConfirmedBlockSlot), CommittedSlot: int64(syncStatus.LatestCommitment.Slot()), diff --git a/components/dashboard/explorer_routes.go b/components/dashboard/explorer_routes.go index 692722f71..c9415bd9d 100644 --- a/components/dashboard/explorer_routes.go +++ b/components/dashboard/explorer_routes.go @@ -105,7 +105,7 @@ func createExplorerBlock(block *model.Block, cachedBlock *blocks.Block, metadata var payloadJSON []byte basicBlock, isBasic := block.BasicBlock() if isBasic { - payloadJSON, err = lo.PanicOnErr(deps.Protocol.APIForVersion(iotaBlk.ProtocolVersion)).JSONEncode(basicBlock.Payload) + payloadJSON, err = lo.PanicOnErr(deps.Protocol.APIForVersion(iotaBlk.Header.ProtocolVersion)).JSONEncode(basicBlock.Payload) if err != nil { return nil } @@ -113,16 +113,16 @@ func createExplorerBlock(block *model.Block, cachedBlock *blocks.Block, metadata t := &ExplorerBlock{ ID: block.ID().ToHex(), - NetworkID: iotaBlk.NetworkID, - ProtocolVersion: iotaBlk.ProtocolVersion, + NetworkID: iotaBlk.Header.NetworkID, + ProtocolVersion: iotaBlk.Header.ProtocolVersion, SolidificationTimestamp: 0, - IssuanceTimestamp: iotaBlk.IssuingTime.Unix(), + IssuanceTimestamp: iotaBlk.Header.IssuingTime.Unix(), SequenceNumber: 0, - IssuerID: iotaBlk.IssuerID.ToHex(), + IssuerID: iotaBlk.Header.IssuerID.ToHex(), Signature: hexutil.EncodeHex(sigBytes), - StrongParents: iotaBlk.Block.StrongParentIDs().ToHex(), - WeakParents: iotaBlk.Block.WeakParentIDs().ToHex(), - ShallowLikedParents: iotaBlk.Block.ShallowLikeParentIDs().ToHex(), + StrongParents: iotaBlk.Body.StrongParentIDs().ToHex(), + WeakParents: iotaBlk.Body.WeakParentIDs().ToHex(), + ShallowLikedParents: iotaBlk.Body.ShallowLikeParentIDs().ToHex(), PayloadType: func() iotago.PayloadType { if isBasic && basicBlock.Payload != nil { @@ -152,8 +152,8 @@ func createExplorerBlock(block *model.Block, cachedBlock *blocks.Block, metadata return "" }(), - CommitmentID: iotaBlk.SlotCommitmentID.ToHex(), - LatestConfirmedSlot: uint64(iotaBlk.LatestFinalizedSlot), + CommitmentID: iotaBlk.Header.SlotCommitmentID.ToHex(), + LatestConfirmedSlot: uint64(iotaBlk.Header.LatestFinalizedSlot), } if cachedBlock != nil { diff --git a/components/dashboard/visualizer.go b/components/dashboard/visualizer.go index 31eca650d..ad565cd41 100644 --- a/components/dashboard/visualizer.go +++ b/components/dashboard/visualizer.go @@ -48,9 +48,9 @@ func sendVertex(blk *blocks.Block, confirmed bool) { broadcastWsBlock(&wsblk{MsgTypeVertex, &vertex{ ID: blk.ID().ToHex(), - StrongParents: blk.ProtocolBlock().Block.StrongParentIDs().ToHex(), - WeakParents: blk.ProtocolBlock().Block.WeakParentIDs().ToHex(), - ShallowLikedParents: blk.ProtocolBlock().Block.ShallowLikeParentIDs().ToHex(), + StrongParents: blk.ProtocolBlock().Body.StrongParentIDs().ToHex(), + WeakParents: blk.ProtocolBlock().Body.WeakParentIDs().ToHex(), + ShallowLikedParents: blk.ProtocolBlock().Body.ShallowLikeParentIDs().ToHex(), IsConfirmed: confirmed, IsTx: isTx, IsTxAccepted: func() bool { diff --git a/components/debugapi/debug_models.go b/components/debugapi/debug_models.go index 3ec7eba94..8343b91af 100644 --- a/components/debugapi/debug_models.go +++ b/components/debugapi/debug_models.go @@ -72,8 +72,8 @@ func BlockMetadataResponseFromBlock(block *blocks.Block) *BlockMetadataResponse return &BlockMetadataResponse{ BlockID: block.ID().String(), StrongParents: lo.Map(block.StrongParents(), func(blockID iotago.BlockID) string { return blockID.String() }), - WeakParents: lo.Map(block.ProtocolBlock().Block.WeakParentIDs(), func(blockID iotago.BlockID) string { return blockID.String() }), - ShallowLikeParents: lo.Map(block.ProtocolBlock().Block.ShallowLikeParentIDs(), func(blockID iotago.BlockID) string { return blockID.String() }), + WeakParents: lo.Map(block.ProtocolBlock().Body.WeakParentIDs(), func(blockID iotago.BlockID) string { return blockID.String() }), + ShallowLikeParents: lo.Map(block.ProtocolBlock().Body.ShallowLikeParentIDs(), func(blockID iotago.BlockID) string { return blockID.String() }), Solid: block.IsSolid(), Invalid: block.IsInvalid(), Booked: block.IsBooked(), diff --git a/components/debugapi/node.go b/components/debugapi/node.go index 313b52e61..e39851aed 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.Engines.Main.Get().SybilProtection.SeatManager() latestSlotIndex := deps.Protocol.Engines.Main.Get().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/inx/server_accounts.go b/components/inx/server_accounts.go new file mode 100644 index 000000000..47795c49c --- /dev/null +++ b/components/inx/server_accounts.go @@ -0,0 +1,53 @@ +package inx + +import ( + "context" + + "github.com/iotaledger/hive.go/ierrors" + inx "github.com/iotaledger/inx/go" + iotago "github.com/iotaledger/iota.go/v4" +) + +func (s *Server) ReadIsValidatorAccount(_ context.Context, accountInfoRequest *inx.AccountInfoRequest) (*inx.BoolResponse, error) { + slot := iotago.SlotIndex(accountInfoRequest.GetAccountSlot()) + accountID, _, err := iotago.AccountIDFromBytes(accountInfoRequest.AccountId) + if err != nil { + return nil, ierrors.Wrap(err, "error when parsing account id") + } + + account, exists, err := deps.Protocol.Engines.Main.Get().Ledger.Account(accountID, slot) + if err != nil { + return nil, ierrors.Wrapf(err, "error when retrieving account data for %s", accountID) + } + + return inx.WrapBoolResponse(exists && account.StakeEndEpoch <= deps.Protocol.APIForSlot(slot).TimeProvider().EpochFromSlot(slot)), nil +} + +func (s *Server) ReadIsCommitteeMember(_ context.Context, accountInfoRequest *inx.AccountInfoRequest) (*inx.BoolResponse, error) { + slot := iotago.SlotIndex(accountInfoRequest.GetAccountSlot()) + accountID, _, err := iotago.AccountIDFromBytes(accountInfoRequest.AccountId) + if err != nil { + return nil, ierrors.Wrap(err, "error when parsing account id") + } + committee, exists := deps.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(slot) + if !exists { + return nil, ierrors.Errorf("committee does not exist for slot %d", slot) + } + + return inx.WrapBoolResponse(committee.HasAccount(accountID)), nil +} + +func (s *Server) ReadIsCandidate(_ context.Context, accountInfoRequest *inx.AccountInfoRequest) (*inx.BoolResponse, error) { + slot := iotago.SlotIndex(accountInfoRequest.GetAccountSlot()) + accountID, _, err := iotago.AccountIDFromBytes(accountInfoRequest.AccountId) + if err != nil { + return nil, ierrors.Wrap(err, "error when parsing account id") + } + + isCandidateActive, err := deps.Protocol.Engines.Main.Get().SybilProtection.IsCandidateActive(accountID, deps.Protocol.APIForSlot(slot).TimeProvider().EpochFromSlot(slot)) + if err != nil { + return nil, ierrors.Wrap(err, "error when checking if candidate is active") + } + + return inx.WrapBoolResponse(isCandidateActive), nil +} diff --git a/components/inx/server_blocks.go b/components/inx/server_blocks.go index c82f6fe10..754f6cee6 100644 --- a/components/inx/server_blocks.go +++ b/components/inx/server_blocks.go @@ -16,6 +16,12 @@ import ( iotago "github.com/iotaledger/iota.go/v4" ) +func (s *Server) ReadActiveRootBlocks(_ context.Context, _ *inx.NoParams) (*inx.RootBlocksResponse, error) { + activeRootBlocks := deps.Protocol.Engines.Main.Get().EvictionState.ActiveRootBlocks() + + return inx.WrapRootBlocks(activeRootBlocks), nil +} + func (s *Server) ReadBlock(_ context.Context, blockID *inx.BlockId) (*inx.RawBlock, error) { blkID := blockID.Unwrap() block, exists := deps.Protocol.Engines.Main.Get().Block(blkID) // block +1 @@ -126,7 +132,7 @@ func (s *Server) SubmitBlock(ctx context.Context, rawBlock *inx.RawBlock) (*inx. return s.attachBlock(ctx, block) } -func (s *Server) attachBlock(ctx context.Context, block *iotago.ProtocolBlock) (*inx.BlockId, error) { +func (s *Server) attachBlock(ctx context.Context, block *iotago.Block) (*inx.BlockId, error) { mergedCtx, mergedCtxCancel := contextutils.MergeContexts(ctx, Component.Daemon().ContextStopped()) defer mergedCtxCancel() @@ -154,10 +160,10 @@ func getINXBlockMetadata(blockID iotago.BlockID) (*inx.BlockMetadata, error) { } return &inx.BlockMetadata{ - BlockId: inx.NewBlockId(blockID), - BlockState: inx.WrapBlockState(blockMetadata.BlockState), - BlockFailureReason: inx.WrapBlockFailureReason(blockMetadata.BlockFailureReason), - TxState: inx.WrapTransactionState(blockMetadata.TxState), - TxFailureReason: inx.WrapTransactionFailureReason(blockMetadata.TxFailureReason), + BlockId: inx.NewBlockId(blockID), + BlockState: inx.WrapBlockState(blockMetadata.BlockState), + BlockFailureReason: inx.WrapBlockFailureReason(blockMetadata.BlockFailureReason), + TransactionState: inx.WrapTransactionState(blockMetadata.TransactionState), + TransactionFailureReason: inx.WrapTransactionFailureReason(blockMetadata.TransactionFailureReason), }, nil } diff --git a/components/inx/server_commitments.go b/components/inx/server_commitments.go index 6c3030142..5bf60ae47 100644 --- a/components/inx/server_commitments.go +++ b/components/inx/server_commitments.go @@ -22,6 +22,14 @@ func inxCommitment(commitment *model.Commitment) *inx.Commitment { } } +func (s *Server) ForceCommitUntil(_ context.Context, slot *inx.SlotIndex) (*inx.NoParams, error) { + err := deps.Protocol.Engines.Main.Get().Notarization.ForceCommitUntil(slot.Unwrap()) + if err != nil { + return nil, ierrors.Wrapf(err, "error while performing force commit until %d", slot.Index) + } + + return &inx.NoParams{}, nil +} func (s *Server) ReadCommitment(_ context.Context, req *inx.CommitmentRequest) (*inx.Commitment, error) { commitmentSlot := iotago.SlotIndex(req.GetCommitmentSlot()) diff --git a/components/inx/server_node.go b/components/inx/server_node.go index 87f3361b7..abfec03af 100644 --- a/components/inx/server_node.go +++ b/components/inx/server_node.go @@ -25,6 +25,7 @@ func inxNodeStatus(status *syncmanager.SyncStatus) *inx.NodeStatus { return &inx.NodeStatus{ IsHealthy: status.NodeSynced, + IsBootstrapped: status.NodeBootstrapped, LastAcceptedBlockSlot: uint32(status.LastAcceptedBlockSlot), LastConfirmedBlockSlot: uint32(status.LastConfirmedBlockSlot), LatestCommitment: inxCommitment(status.LatestCommitment), diff --git a/components/metrics/metrics_accounts.go b/components/metrics/metrics_accounts.go index a761ca800..dc2bfca3d 100644 --- a/components/metrics/metrics_accounts.go +++ b/components/metrics/metrics_accounts.go @@ -23,7 +23,7 @@ var AccountMetrics = collector.NewCollection(accountNamespace, collector.WithPruningDelay(10*time.Minute), collector.WithInitFunc(func() { deps.Protocol.Events.Engine.BlockGadget.BlockAccepted.Hook(func(block *blocks.Block) { - accountData, exists, _ := deps.Protocol.Engines.Main.Get().Ledger.Account(block.ProtocolBlock().IssuerID, deps.Protocol.Engines.Main.Get().SyncManager.LatestCommitment().Slot()) + accountData, exists, _ := deps.Protocol.Engines.Main.Get().Ledger.Account(block.ProtocolBlock().Header.IssuerID, deps.Protocol.Engines.Main.Get().SyncManager.LatestCommitment().Slot()) if exists { deps.Collector.Update(accountNamespace, credits, float64(accountData.Credits.Value), accountData.ID.String()) } diff --git a/components/metrics/metrics_scheduler.go b/components/metrics/metrics_scheduler.go index 5096ef433..c823b7379 100644 --- a/components/metrics/metrics_scheduler.go +++ b/components/metrics/metrics_scheduler.go @@ -38,23 +38,20 @@ var SchedulerMetrics = collector.NewCollection(schedulerNamespace, collector.WithHelp("Current size of each node's queue (in work units)."), collector.WithInitFunc(func() { deps.Protocol.Events.Engine.Scheduler.BlockEnqueued.Hook(func(block *blocks.Block) { - deps.Collector.Update(schedulerNamespace, queueSizePerNodeWork, float64(deps.Protocol.Engines.Main.Get().Scheduler.IssuerQueueWork(block.ProtocolBlock().IssuerID)), block.ProtocolBlock().IssuerID.String()) + deps.Collector.Update(schedulerNamespace, queueSizePerNodeWork, float64(deps.Protocol.Engines.Main.Get().Scheduler.IssuerQueueWork(block.ProtocolBlock().Header.IssuerID)), block.ProtocolBlock().Header.IssuerID.String()) }, event.WithWorkerPool(Component.WorkerPool)) deps.Protocol.Events.Engine.Scheduler.BlockSkipped.Hook(func(block *blocks.Block) { - deps.Collector.Update(schedulerNamespace, queueSizePerNodeWork, float64(deps.Protocol.Engines.Main.Get().Scheduler.IssuerQueueWork(block.ProtocolBlock().IssuerID)), block.ProtocolBlock().IssuerID.String()) - + deps.Collector.Update(schedulerNamespace, queueSizePerNodeWork, float64(deps.Protocol.Engines.Main.Get().Scheduler.IssuerQueueWork(block.ProtocolBlock().Header.IssuerID)), block.ProtocolBlock().Header.IssuerID.String()) }, event.WithWorkerPool(Component.WorkerPool)) deps.Protocol.Events.Engine.Scheduler.BlockDropped.Hook(func(block *blocks.Block, _ error) { - deps.Collector.Update(schedulerNamespace, queueSizePerNodeWork, float64(deps.Protocol.Engines.Main.Get().Scheduler.IssuerQueueWork(block.ProtocolBlock().IssuerID)), block.ProtocolBlock().IssuerID.String()) - + deps.Collector.Update(schedulerNamespace, queueSizePerNodeWork, float64(deps.Protocol.Engines.Main.Get().Scheduler.IssuerQueueWork(block.ProtocolBlock().Header.IssuerID)), block.ProtocolBlock().Header.IssuerID.String()) }, event.WithWorkerPool(Component.WorkerPool)) deps.Protocol.Events.Engine.Scheduler.BlockScheduled.Hook(func(block *blocks.Block) { - deps.Collector.Update(schedulerNamespace, queueSizePerNodeWork, float64(deps.Protocol.Engines.Main.Get().Scheduler.IssuerQueueWork(block.ProtocolBlock().IssuerID)), block.ProtocolBlock().IssuerID.String()) - + deps.Collector.Update(schedulerNamespace, queueSizePerNodeWork, float64(deps.Protocol.Engines.Main.Get().Scheduler.IssuerQueueWork(block.ProtocolBlock().Header.IssuerID)), block.ProtocolBlock().Header.IssuerID.String()) }, event.WithWorkerPool(Component.WorkerPool)) }), )), @@ -66,25 +63,25 @@ var SchedulerMetrics = collector.NewCollection(schedulerNamespace, collector.WithInitFunc(func() { deps.Protocol.Events.Engine.Scheduler.BlockEnqueued.Hook(func(block *blocks.Block) { if _, isBasic := block.BasicBlock(); isBasic { - deps.Collector.Update(schedulerNamespace, queueSizePerNodeCount, float64(deps.Protocol.Engines.Main.Get().Scheduler.IssuerQueueBlockCount(block.ProtocolBlock().IssuerID)), block.ProtocolBlock().IssuerID.String()) + deps.Collector.Update(schedulerNamespace, queueSizePerNodeCount, float64(deps.Protocol.Engines.Main.Get().Scheduler.IssuerQueueBlockCount(block.ProtocolBlock().Header.IssuerID)), block.ProtocolBlock().Header.IssuerID.String()) } }, event.WithWorkerPool(Component.WorkerPool)) deps.Protocol.Events.Engine.Scheduler.BlockSkipped.Hook(func(block *blocks.Block) { if _, isBasic := block.BasicBlock(); isBasic { - deps.Collector.Update(schedulerNamespace, queueSizePerNodeCount, float64(deps.Protocol.Engines.Main.Get().Scheduler.IssuerQueueBlockCount(block.ProtocolBlock().IssuerID)), block.ProtocolBlock().IssuerID.String()) + deps.Collector.Update(schedulerNamespace, queueSizePerNodeCount, float64(deps.Protocol.Engines.Main.Get().Scheduler.IssuerQueueBlockCount(block.ProtocolBlock().Header.IssuerID)), block.ProtocolBlock().Header.IssuerID.String()) } }, event.WithWorkerPool(Component.WorkerPool)) deps.Protocol.Events.Engine.Scheduler.BlockDropped.Hook(func(block *blocks.Block, _ error) { if _, isBasic := block.BasicBlock(); isBasic { - deps.Collector.Update(schedulerNamespace, queueSizePerNodeCount, float64(deps.Protocol.Engines.Main.Get().Scheduler.IssuerQueueBlockCount(block.ProtocolBlock().IssuerID)), block.ProtocolBlock().IssuerID.String()) + deps.Collector.Update(schedulerNamespace, queueSizePerNodeCount, float64(deps.Protocol.Engines.Main.Get().Scheduler.IssuerQueueBlockCount(block.ProtocolBlock().Header.IssuerID)), block.ProtocolBlock().Header.IssuerID.String()) } }, event.WithWorkerPool(Component.WorkerPool)) deps.Protocol.Events.Engine.Scheduler.BlockScheduled.Hook(func(block *blocks.Block) { if _, isBasic := block.BasicBlock(); isBasic { - deps.Collector.Update(schedulerNamespace, queueSizePerNodeCount, float64(deps.Protocol.Engines.Main.Get().Scheduler.IssuerQueueBlockCount(block.ProtocolBlock().IssuerID)), block.ProtocolBlock().IssuerID.String()) + deps.Collector.Update(schedulerNamespace, queueSizePerNodeCount, float64(deps.Protocol.Engines.Main.Get().Scheduler.IssuerQueueBlockCount(block.ProtocolBlock().Header.IssuerID)), block.ProtocolBlock().Header.IssuerID.String()) } }, event.WithWorkerPool(Component.WorkerPool)) }), @@ -97,25 +94,25 @@ var SchedulerMetrics = collector.NewCollection(schedulerNamespace, collector.WithInitFunc(func() { deps.Protocol.Events.Engine.Scheduler.BlockEnqueued.Hook(func(block *blocks.Block) { if _, isValidation := block.ValidationBlock(); isValidation { - deps.Collector.Update(schedulerNamespace, validatorQueueSizePerNodeCount, float64(deps.Protocol.Engines.Main.Get().Scheduler.ValidatorQueueBlockCount(block.ProtocolBlock().IssuerID)), block.ProtocolBlock().IssuerID.String()) + deps.Collector.Update(schedulerNamespace, validatorQueueSizePerNodeCount, float64(deps.Protocol.Engines.Main.Get().Scheduler.ValidatorQueueBlockCount(block.ProtocolBlock().Header.IssuerID)), block.ProtocolBlock().Header.IssuerID.String()) } }, event.WithWorkerPool(Component.WorkerPool)) deps.Protocol.Events.Engine.Scheduler.BlockSkipped.Hook(func(block *blocks.Block) { if _, isValidation := block.ValidationBlock(); isValidation { - deps.Collector.Update(schedulerNamespace, validatorQueueSizePerNodeCount, float64(deps.Protocol.Engines.Main.Get().Scheduler.ValidatorQueueBlockCount(block.ProtocolBlock().IssuerID)), block.ProtocolBlock().IssuerID.String()) + deps.Collector.Update(schedulerNamespace, validatorQueueSizePerNodeCount, float64(deps.Protocol.Engines.Main.Get().Scheduler.ValidatorQueueBlockCount(block.ProtocolBlock().Header.IssuerID)), block.ProtocolBlock().Header.IssuerID.String()) } }, event.WithWorkerPool(Component.WorkerPool)) deps.Protocol.Events.Engine.Scheduler.BlockDropped.Hook(func(block *blocks.Block, _ error) { if _, isValidation := block.ValidationBlock(); isValidation { - deps.Collector.Update(schedulerNamespace, validatorQueueSizePerNodeCount, float64(deps.Protocol.Engines.Main.Get().Scheduler.ValidatorQueueBlockCount(block.ProtocolBlock().IssuerID)), block.ProtocolBlock().IssuerID.String()) + deps.Collector.Update(schedulerNamespace, validatorQueueSizePerNodeCount, float64(deps.Protocol.Engines.Main.Get().Scheduler.ValidatorQueueBlockCount(block.ProtocolBlock().Header.IssuerID)), block.ProtocolBlock().Header.IssuerID.String()) } }, event.WithWorkerPool(Component.WorkerPool)) deps.Protocol.Events.Engine.Scheduler.BlockScheduled.Hook(func(block *blocks.Block) { if _, isValidation := block.ValidationBlock(); isValidation { - deps.Collector.Update(schedulerNamespace, validatorQueueSizePerNodeCount, float64(deps.Protocol.Engines.Main.Get().Scheduler.ValidatorQueueBlockCount(block.ProtocolBlock().IssuerID)), block.ProtocolBlock().IssuerID.String()) + deps.Collector.Update(schedulerNamespace, validatorQueueSizePerNodeCount, float64(deps.Protocol.Engines.Main.Get().Scheduler.ValidatorQueueBlockCount(block.ProtocolBlock().Header.IssuerID)), block.ProtocolBlock().Header.IssuerID.String()) } }, event.WithWorkerPool(Component.WorkerPool)) }), @@ -127,14 +124,14 @@ var SchedulerMetrics = collector.NewCollection(schedulerNamespace, collector.WithHelp("Current amount of mana of each issuer in the queue."), collector.WithInitFunc(func() { deps.Protocol.Events.Engine.Scheduler.BlockEnqueued.Hook(func(block *blocks.Block) { - mana, err := deps.Protocol.Engines.Main.Get().Ledger.ManaManager().GetManaOnAccount(block.ProtocolBlock().IssuerID, block.SlotCommitmentID().Slot()) + mana, err := deps.Protocol.Engines.Main.Get().Ledger.ManaManager().GetManaOnAccount(block.ProtocolBlock().Header.IssuerID, block.SlotCommitmentID().Slot()) if err != nil { - deps.Protocol.Engines.Main.Get().ErrorHandler("metrics")(ierrors.Wrapf(err, "failed to retrieve mana on account %s for slot %d", block.ProtocolBlock().IssuerID, block.SlotCommitmentID().Slot())) + deps.Protocol.Engines.Main.Get().ErrorHandler("metrics")(ierrors.Wrapf(err, "failed to retrieve mana on account %s for slot %d", block.ProtocolBlock().Header.IssuerID, block.SlotCommitmentID().Slot())) return } - deps.Collector.Update(schedulerNamespace, manaAmountPerNode, float64(mana), block.ProtocolBlock().IssuerID.String()) + deps.Collector.Update(schedulerNamespace, manaAmountPerNode, float64(mana), block.ProtocolBlock().Header.IssuerID.String()) }, event.WithWorkerPool(Component.WorkerPool)) }), )), 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 40e50261c..938617225 100644 --- a/components/protocol/component.go +++ b/components/protocol/component.go @@ -183,91 +183,91 @@ func configure() error { }) 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.Network.OnCommitmentRequestReceived(func(commitmentID iotago.CommitmentID, id peer.ID) { @@ -279,27 +279,27 @@ func configure() error { }) 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 207e68f07..a07998ecb 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.Engines.Main.Get().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.Engines.Main.Get().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.Engines.Main.Get().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.Engines.Main.Get().SyncManager.LatestCommitment().Slot() accountData, exists, err := deps.Protocol.Engines.Main.Get().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.Engines.Main.Get().SybilProtection.IsCandidateActive(accountID, nextEpoch) + + active, err := deps.Protocol.Engines.Main.Get().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.Engines.Main.Get().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.Engines.Main.Get().SybilProtection.SeatManager().Committee(slot) + seatedAccounts, exists := deps.Protocol.Engines.Main.Get().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{ @@ -212,7 +222,7 @@ func selectedCommittee(c echo.Context) *apimodels.CommitteeResponse { }) return &apimodels.CommitteeResponse{ - EpochIndex: epoch, + Epoch: epoch, Committee: committee, TotalStake: seatedAccounts.Accounts().TotalStake(), TotalValidatorStake: seatedAccounts.Accounts().TotalValidatorStake(), diff --git a/components/restapi/core/blocks.go b/components/restapi/core/blocks.go index 96ce3d9c4..acbf3bd7d 100644 --- a/components/restapi/core/blocks.go +++ b/components/restapi/core/blocks.go @@ -1,12 +1,9 @@ package core import ( - "io" - "github.com/labstack/echo/v4" "github.com/iotaledger/hive.go/ierrors" - "github.com/iotaledger/hive.go/serializer/v2/serix" "github.com/iotaledger/inx-app/pkg/httpserver" "github.com/iotaledger/iota-core/pkg/blockhandler" "github.com/iotaledger/iota-core/pkg/model" @@ -18,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.Engines.Main.Get().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 @@ -32,7 +29,7 @@ func blockByID(c echo.Context) (*model.Block, error) { func blockMetadataByBlockID(blockID iotago.BlockID) (*apimodels.BlockMetadataResponse, error) { blockMetadata, err := deps.Protocol.Engines.Main.Get().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 @@ -41,14 +38,14 @@ 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) } func blockIssuanceBySlot(slotIndex iotago.SlotIndex) (*apimodels.IssuanceBlockHeaderResponse, error) { - references := deps.Protocol.Engines.Main.Get().TipSelection.SelectTips(iotago.BlockMaxParents) + references := deps.Protocol.Engines.Main.Get().TipSelection.SelectTips(iotago.BasicBlockMaxParents) var slotCommitment *model.Commitment var err error @@ -58,7 +55,7 @@ func blockIssuanceBySlot(slotIndex iotago.SlotIndex) (*apimodels.IssuanceBlockHe } else { slotCommitment, err = deps.Protocol.Engines.Main.Get().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) } } @@ -78,37 +75,9 @@ func blockIssuanceBySlot(slotIndex iotago.SlotIndex) (*apimodels.IssuanceBlockHe } func sendBlock(c echo.Context) (*apimodels.BlockCreatedResponse, error) { - mimeType, err := httpserver.GetRequestContentType(c, httpserver.MIMEApplicationVendorIOTASerializerV2, echo.MIMEApplicationJSON) - if err != nil { - return nil, ierrors.Wrapf(httpserver.ErrInvalidParameter, "invalid block, error: %w", err) - } - - var iotaBlock *iotago.ProtocolBlock - - if c.Request().Body == nil { - // bad request - return nil, ierrors.Wrap(httpserver.ErrInvalidParameter, "invalid block, error: request body missing") - } - - bytes, err := io.ReadAll(c.Request().Body) + iotaBlock, err := httpserver.ParseRequestByHeader(c, deps.Protocol.CommittedAPI(), iotago.BlockFromBytes(deps.Protocol)) if err != nil { - return nil, ierrors.Wrapf(httpserver.ErrInvalidParameter, "invalid block, error: %w", err) - } - - switch mimeType { - case echo.MIMEApplicationJSON: - if err := deps.Protocol.CommittedAPI().JSONDecode(bytes, iotaBlock, serix.WithValidation()); err != nil { - return nil, ierrors.Wrapf(httpserver.ErrInvalidParameter, "invalid block, error: %w", err) - } - - case httpserver.MIMEApplicationVendorIOTASerializerV2: - iotaBlock, _, err = iotago.ProtocolBlockFromBytes(deps.Protocol)(bytes) - if err != nil { - return nil, ierrors.Wrapf(httpserver.ErrInvalidParameter, "invalid block, error: %w", err) - } - - default: - return nil, echo.ErrUnsupportedMediaType + 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 712e0378a..b91c2541f 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,16 +22,16 @@ func indexByCommitmentID(c echo.Context) (iotago.SlotIndex, error) { func getCommitmentDetails(index iotago.SlotIndex) (*iotago.Commitment, error) { commitment, err := deps.Protocol.Engines.Main.Get().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 } -func getUTXOChanges(index iotago.SlotIndex) (*apimodels.UTXOChangesResponse, error) { - diffs, err := deps.Protocol.Engines.Main.Get().Ledger.SlotDiffs(index) +func getUTXOChanges(slot iotago.SlotIndex) (*apimodels.UTXOChangesResponse, error) { + diffs, err := deps.Protocol.Engines.Main.Get().Ledger.SlotDiffs(slot) if err != nil { - return nil, ierrors.Wrapf(err, "failed to get slot diffs: %d", index) + return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to get slot diffs %d: %s", slot, err) } createdOutputs := make(iotago.OutputIDs, len(diffs.Outputs)) @@ -46,7 +46,7 @@ func getUTXOChanges(index iotago.SlotIndex) (*apimodels.UTXOChangesResponse, err } return &apimodels.UTXOChangesResponse{ - Index: index, + Slot: slot, CreatedOutputs: createdOutputs, ConsumedOutputs: consumedOutputs, }, nil diff --git a/components/restapi/core/component.go b/components/restapi/core/component.go index 0574bf620..39be74576 100644 --- a/components/restapi/core/component.go +++ b/components/restapi/core/component.go @@ -383,29 +383,6 @@ func checkNodeSynced() echo.MiddlewareFunc { } func responseByHeader(c echo.Context, obj any) error { - mimeType, err := httpserver.GetAcceptHeaderContentType(c, httpserver.MIMEApplicationVendorIOTASerializerV2, echo.MIMEApplicationJSON) - if err != nil && err != httpserver.ErrNotAcceptable { - return err - } - - switch mimeType { - case httpserver.MIMEApplicationVendorIOTASerializerV2: - // TODO: that should take the API that belongs to the object - b, err := deps.Protocol.CommittedAPI().Encode(obj) - if err != nil { - return err - } - - return c.Blob(http.StatusOK, httpserver.MIMEApplicationVendorIOTASerializerV2, b) - - // default to echo.MIMEApplicationJSON - default: - // TODO: that should take the API that belongs to the object - j, err := deps.Protocol.CommittedAPI().JSONEncode(obj) - if err != nil { - return err - } - - return c.Blob(http.StatusOK, echo.MIMEApplicationJSON, j) - } + // TODO: that should take the API that belongs to the object + return httpserver.SendResponseByHeader(c, deps.Protocol.CommittedAPI(), obj) } diff --git a/components/restapi/core/transaction.go b/components/restapi/core/transaction.go index 9dc1ea160..7876416c6 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.Engines.Main.Get().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.Engines.Main.Get().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 e5fec1eea..550b21650 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.Engines.Main.Get().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.Engines.Main.Get().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.Engines.Main.Get().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.Engines.Main.Get().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.Engines.Main.Get().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.Engines.Main.Get().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/management/pruning.go b/components/restapi/management/pruning.go index 6d613ddec..eb46223a3 100644 --- a/components/restapi/management/pruning.go +++ b/components/restapi/management/pruning.go @@ -20,17 +20,17 @@ func pruneDatabase(c echo.Context) (*apimodels.PruneDatabaseResponse, error) { } // only allow one type of pruning at a time - if (request.Index == 0 && request.Depth == 0 && request.TargetDatabaseSize == "") || - (request.Index != 0 && request.Depth != 0) || - (request.Index != 0 && request.TargetDatabaseSize != "") || + if (request.Epoch == 0 && request.Depth == 0 && request.TargetDatabaseSize == "") || + (request.Epoch != 0 && request.Depth != 0) || + (request.Epoch != 0 && request.TargetDatabaseSize != "") || (request.Depth != 0 && request.TargetDatabaseSize != "") { - return nil, ierrors.Wrapf(httpserver.ErrInvalidParameter, "either index, depth or size has to be specified") + return nil, ierrors.Wrapf(httpserver.ErrInvalidParameter, "either epoch, depth or size has to be specified") } var err error - if request.Index != 0 { - err = deps.Protocol.Engines.Main.Get().Storage.PruneByEpochIndex(request.Index) + if request.Epoch != 0 { + err = deps.Protocol.Engines.Main.Get().Storage.PruneByEpochIndex(request.Epoch) if err != nil { return nil, ierrors.Wrapf(echo.ErrInternalServerError, "pruning database failed: %s", err) } @@ -55,12 +55,12 @@ func pruneDatabase(c echo.Context) (*apimodels.PruneDatabaseResponse, error) { } } - targetIndex, hasPruned := deps.Protocol.Engines.Main.Get().Storage.LastPrunedEpoch() + targetEpoch, hasPruned := deps.Protocol.Engines.Main.Get().Storage.LastPrunedEpoch() if hasPruned { - targetIndex++ + targetEpoch++ } return &apimodels.PruneDatabaseResponse{ - Index: targetIndex, + Epoch: targetEpoch, }, nil } 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/component.go b/components/validator/component.go deleted file mode 100644 index bed6442ca..000000000 --- a/components/validator/component.go +++ /dev/null @@ -1,93 +0,0 @@ -package validator - -import ( - "context" - "sync/atomic" - "time" - - "go.uber.org/dig" - - "github.com/iotaledger/hive.go/app" - "github.com/iotaledger/hive.go/runtime/event" - "github.com/iotaledger/hive.go/runtime/timed" - "github.com/iotaledger/iota-core/pkg/blockhandler" - "github.com/iotaledger/iota-core/pkg/daemon" - "github.com/iotaledger/iota-core/pkg/protocol" - "github.com/iotaledger/iota-core/pkg/protocol/engine/notarization" - iotago "github.com/iotaledger/iota.go/v4" -) - -func init() { - Component = &app.Component{ - Name: "Validator", - DepsFunc: func(cDeps dependencies) { deps = cDeps }, - Params: params, - Run: run, - IsEnabled: func(_ *dig.Container) bool { - return ParamsValidator.Enabled - }, - } -} - -var ( - Component *app.Component - deps dependencies - - isValidator atomic.Bool - executor *timed.TaskExecutor[iotago.AccountID] - validatorAccount blockhandler.Account -) - -type dependencies struct { - dig.In - - Protocol *protocol.Protocol - BlockHandler *blockhandler.BlockHandler -} - -func run() error { - validatorAccount = blockhandler.AccountFromParams(ParamsValidator.Account, ParamsValidator.PrivateKey) - - executor = timed.NewTaskExecutor[iotago.AccountID](1) - - return Component.Daemon().BackgroundWorker(Component.Name, func(ctx context.Context) { - Component.LogInfof("Starting Validator with IssuerID: %s", validatorAccount.ID()) - - checkValidatorStatus(ctx) - - deps.Protocol.Events.Engine.Notarization.SlotCommitted.Hook(func(details *notarization.SlotCommittedDetails) { - checkValidatorStatus(ctx) - }, event.WithWorkerPool(Component.WorkerPool)) - - <-ctx.Done() - - executor.Shutdown() - - Component.LogInfo("Stopping Validator... done") - }, daemon.PriorityActivity) -} - -func checkValidatorStatus(ctx context.Context) { - account, exists, err := deps.Protocol.Engines.Main.Get().Ledger.Account(validatorAccount.ID(), deps.Protocol.Engines.Main.Get().Storage.Settings().LatestCommitment().Slot()) - if err != nil { - Component.LogErrorf("error when retrieving BlockIssuer account %s: %w", validatorAccount.ID(), err) - - return - } - - if !exists || account.StakeEndEpoch <= deps.Protocol.CommittedAPI().TimeProvider().EpochFromSlot(deps.Protocol.CommittedAPI().TimeProvider().SlotFromTime(time.Now())) { - if prevValue := isValidator.Swap(false); prevValue { - // If the account stops being a validator, don't issue any blocks. - Component.LogInfof("BlockIssuer account %s stopped being a validator", validatorAccount.ID()) - executor.Cancel(validatorAccount.ID()) - } - - return - } - - if prevValue := isValidator.Swap(true); !prevValue { - Component.LogInfof("BlockIssuer account %s became a validator", validatorAccount.ID()) - // If the account becomes a validator, start issue validator blocks. - executor.ExecuteAfter(validatorAccount.ID(), func() { issueValidatorBlock(ctx) }, ParamsValidator.CommitteeBroadcastInterval) - } -} diff --git a/components/validator/issuer.go b/components/validator/issuer.go deleted file mode 100644 index ff4456e0d..000000000 --- a/components/validator/issuer.go +++ /dev/null @@ -1,160 +0,0 @@ -package validator - -import ( - "context" - "time" - - "github.com/iotaledger/hive.go/ierrors" - "github.com/iotaledger/iota-core/pkg/model" - iotago "github.com/iotaledger/iota.go/v4" - "github.com/iotaledger/iota.go/v4/builder" -) - -var ErrBlockTooRecent = ierrors.New("block is too recent compared to latest commitment") - -func issueValidatorBlock(ctx context.Context) { - // Get the main engine instance in case it changes mid-execution. - engineInstance := deps.Protocol.Engines.Main.Get() - - blockIssuingTime := time.Now() - nextBroadcast := blockIssuingTime.Add(ParamsValidator.CommitteeBroadcastInterval) - - // Use 'defer' because nextBroadcast is updated during function execution, and the value at the end needs to be used. - defer func() { - executor.ExecuteAt(validatorAccount.ID(), func() { issueValidatorBlock(ctx) }, nextBroadcast) - }() - - if !ParamsValidator.IgnoreBootstrapped && !engineInstance.SyncManager.IsBootstrapped() { - Component.LogDebug("Not issuing validator block because node is not bootstrapped yet.") - - return - } - - protocolParametersHash, err := deps.Protocol.CommittedAPI().ProtocolParameters().Hash() - if err != nil { - Component.LogWarnf("failed to get protocol parameters hash: %s", err.Error()) - - return - } - - parents := engineInstance.TipSelection.SelectTips(iotago.BlockTypeValidationMaxParents) - - addressableCommitment, err := getAddressableCommitment(deps.Protocol.CommittedAPI().TimeProvider().SlotFromTime(blockIssuingTime)) - if err != nil && ierrors.Is(err, ErrBlockTooRecent) { - commitment, parentID, reviveChainErr := reviveChain(blockIssuingTime) - if reviveChainErr != nil { - Component.LogError("error reviving chain: %s", reviveChainErr.Error()) - return - } - - addressableCommitment = commitment - parents = make(model.ParentReferences) - parents[iotago.StrongParentType] = []iotago.BlockID{parentID} - } else if err != nil { - Component.LogWarnf("error getting commitment: %s", err.Error()) - - return - } - - // create the validation block here using the validation block builder from iota.go - validationBlock, err := builder.NewValidationBlockBuilder(deps.Protocol.CommittedAPI()). - IssuingTime(blockIssuingTime). - ProtocolParametersHash(protocolParametersHash). - SlotCommitmentID(addressableCommitment.MustID()). - HighestSupportedVersion(deps.Protocol.LatestAPI().Version()). - LatestFinalizedSlot(engineInstance.SyncManager.LatestFinalizedSlot()). - StrongParents(parents[iotago.StrongParentType]). - WeakParents(parents[iotago.WeakParentType]). - ShallowLikeParents(parents[iotago.ShallowLikeParentType]). - Sign(validatorAccount.ID(), validatorAccount.PrivateKey()). - Build() - if err != nil { - Component.LogWarnf("error creating validation block: %s", err.Error()) - - return - } - - modelBlock, err := model.BlockFromBlock(validationBlock) - if err != nil { - Component.LogWarnf("error creating model block from validation block: %s", err.Error()) - - return - } - - if !engineInstance.SybilProtection.SeatManager().Committee(deps.Protocol.CommittedAPI().TimeProvider().SlotFromTime(blockIssuingTime)).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) - } - - if err = deps.BlockHandler.SubmitBlock(modelBlock); err != nil { - Component.LogWarnf("error issuing validator block: %s", err.Error()) - - return - } - - Component.LogDebugf("Issued validator block: %s - commitment %s %d - latest finalized slot %d", modelBlock.ID(), modelBlock.ProtocolBlock().SlotCommitmentID, modelBlock.ProtocolBlock().SlotCommitmentID.Slot(), modelBlock.ProtocolBlock().LatestFinalizedSlot) -} - -func reviveChain(issuingTime time.Time) (*iotago.Commitment, iotago.BlockID, error) { - lastCommittedSlot := deps.Protocol.Engines.Main.Get().Storage.Settings().LatestCommitment().Slot() - apiForSlot := deps.Protocol.APIForSlot(lastCommittedSlot) - - // Get a rootblock as recent as possible for the parent. - parentBlockID := iotago.EmptyBlockID - for rootBlock := range deps.Protocol.Engines.Main.Get().EvictionState.ActiveRootBlocks() { - if rootBlock.Slot() > parentBlockID.Slot() { - parentBlockID = rootBlock - } - - // Exit the loop if we found a rootblock in the last committed slot (which is the highest we can get). - if parentBlockID.Slot() == lastCommittedSlot { - break - } - } - - issuingSlot := apiForSlot.TimeProvider().SlotFromTime(issuingTime) - - // Force commitments until minCommittableAge relative to the block's issuing time. We basically "pretend" that - // this block was already accepted at the time of issuing so that we have a commitment to reference. - if issuingSlot < apiForSlot.ProtocolParameters().MinCommittableAge() { // Should never happen as we're beyond maxCommittableAge which is > minCommittableAge. - return nil, iotago.EmptyBlockID, ierrors.Errorf("issuing slot %d is smaller than min committable age %d", issuingSlot, apiForSlot.ProtocolParameters().MinCommittableAge()) - } - commitUntilSlot := issuingSlot - apiForSlot.ProtocolParameters().MinCommittableAge() - - if err := deps.Protocol.Engines.Main.Get().Notarization.ForceCommitUntil(commitUntilSlot); err != nil { - return nil, iotago.EmptyBlockID, ierrors.Wrapf(err, "failed to force commit until slot %d", commitUntilSlot) - } - - commitment, err := deps.Protocol.Engines.Main.Get().Storage.Commitments().Load(commitUntilSlot) - if err != nil { - return nil, iotago.EmptyBlockID, ierrors.Wrapf(err, "failed to commit until slot %d to revive chain", commitUntilSlot) - } - - return commitment.Commitment(), parentBlockID, nil -} - -func getAddressableCommitment(blockSlot iotago.SlotIndex) (*iotago.Commitment, error) { - protoParams := deps.Protocol.CommittedAPI().ProtocolParameters() - commitment := deps.Protocol.Engines.Main.Get().Storage.Settings().LatestCommitment().Commitment() - - if blockSlot > commitment.Slot+protoParams.MaxCommittableAge() { - return nil, ierrors.Wrapf(ErrBlockTooRecent, "can't issue block: block slot %d is too far in the future, latest commitment is %d", blockSlot, commitment.Slot) - } - - if blockSlot < commitment.Slot+protoParams.MinCommittableAge() { - if blockSlot < protoParams.MinCommittableAge() || commitment.Slot < protoParams.MinCommittableAge() { - return commitment, nil - } - - commitmentSlot := commitment.Slot - protoParams.MinCommittableAge() - loadedCommitment, err := deps.Protocol.Engines.Main.Get().Storage.Commitments().Load(commitmentSlot) - if err != nil { - return nil, ierrors.Wrapf(err, "error loading valid commitment of slot %d according to minCommittableAge from storage", commitmentSlot) - } - - return loadedCommitment.Commitment(), nil - } - - return commitment, nil -} diff --git a/components/validator/params.go b/components/validator/params.go deleted file mode 100644 index 037498ab2..000000000 --- a/components/validator/params.go +++ /dev/null @@ -1,34 +0,0 @@ -package validator - -import ( - "time" - - "github.com/iotaledger/hive.go/app" -) - -// ParametersValidator contains the definition of the configuration parameters used by the Validator component. -type ParametersValidator struct { - // Enabled defines whether the Validator component is enabled. - Enabled bool `default:"false" usage:"whether the Validator component is enabled"` - // CommitteeBroadcastInterval the interval at which the node will broadcast its committee validator block. - CommitteeBroadcastInterval time.Duration `default:"500ms" usage:"the interval at which the node will broadcast its committee validator block"` - // CandidateBroadcastInterval the interval at which the node will broadcast its candidate validator block. - CandidateBroadcastInterval time.Duration `default:"30m" usage:"the interval at which the node will broadcast its candidate validator block"` - // ParentsCount is the number of parents that node will choose for its validator blocks. - ParentsCount int `default:"8" usage:"the number of parents that node will choose for its validator blocks"` - // IgnoreBootstrapped sets whether the Validator component should start issuing validator blocks before the main engine is bootstrapped. - IgnoreBootstrapped bool `default:"false" usage:"whether the Validator component should start issuing validator blocks before the main engine is bootstrapped"` - // Account the accountID of the account that will issue the blocks. - Account string `default:"" usage:"the accountID of the validator account that will issue the blocks"` - // PrivateKey the private key of the account that will issue the blocks. - PrivateKey string `default:"" usage:"the private key of the validator account that will issue the blocks"` -} - -// ParamsValidator contains the values of the configuration parameters used by the Activity component. -var ParamsValidator = &ParametersValidator{} - -var params = &app.ComponentParams{ - Params: map[string]any{ - "validator": ParamsValidator, - }, -} diff --git a/config_defaults.json b/config_defaults.json index 2dc55de16..f89cc121a 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": { @@ -116,15 +116,6 @@ "useMetricPrefix": false } }, - "validator": { - "enabled": false, - "committeeBroadcastInterval": "500ms", - "candidateBroadcastInterval": "30m", - "parentsCount": 8, - "ignoreBootstrapped": false, - "account": "", - "privateKey": "" - }, "dashboard": { "enabled": true, "bindAddress": "0.0.0.0:8081", diff --git a/deploy/ansible/hosts/feature.yml b/deploy/ansible/hosts/feature.yml index 52405d60a..66e7b10ee 100644 --- a/deploy/ansible/hosts/feature.yml +++ b/deploy/ansible/hosts/feature.yml @@ -7,23 +7,19 @@ cores: internal_nodes: hosts: node-01.feature.shimmer.iota.cafe: - validatorAccount: "{{ NODE_01_ACCOUNTID }}" - validatorPrivKey: "{{ NODE_01_VALIDATOR_PRIVKEY }}" - p2pIdentityPrivateKey: "{{ NODE_01_P2PIDENTITYPRIVATEKEY }}" + validatorAccountAddress: "{{ NODE_01_VALIDATOR_ACCOUNTADDRESS }}" + validatorPrvKey: "{{ NODE_01_VALIDATOR_PRIVKEY }}" + p2pIdentityPrvKey: "{{ NODE_01_P2PIDENTITY_PRIVKEY }}" node-02.feature.shimmer.iota.cafe: - validatorAccount: "{{ NODE_02_ACCOUNTID }}" - validatorPrivKey: "{{ NODE_02_VALIDATOR_PRIVKEY }}" - p2pIdentityPrivateKey: "{{ NODE_02_P2PIDENTITYPRIVATEKEY }}" + validatorAccountAddress: "{{ NODE_02_VALIDATOR_ACCOUNTADDRESS }}" + validatorPrvKey: "{{ NODE_02_VALIDATOR_PRIVKEY }}" + p2pIdentityPrvKey: "{{ NODE_02_P2PIDENTITY_PRIVKEY }}" node-03.feature.shimmer.iota.cafe: - validatorAccount: "{{ NODE_03_ACCOUNTID }}" - validatorPrivKey: "{{ NODE_03_VALIDATOR_PRIVKEY }}" - p2pIdentityPrivateKey: "{{ NODE_03_P2PIDENTITYPRIVATEKEY }}" + validatorAccountAddress: "{{ NODE_03_VALIDATOR_ACCOUNTADDRESS }}" + validatorPrvKey: "{{ NODE_03_VALIDATOR_PRIVKEY }}" + p2pIdentityPrvKey: "{{ NODE_03_P2PIDENTITY_PRIVKEY }}" node-04.feature.shimmer.iota.cafe: - validatorAccount: "{{ NODE_04_ACCOUNTID }}" - validatorPrivKey: "{{ NODE_04_VALIDATOR_PRIVKEY }}" - p2pIdentityPrivateKey: "{{ NODE_04_P2PIDENTITYPRIVATEKEY }}" + p2pIdentityPrvKey: "{{ NODE_04_P2PIDENTITY_PRIVKEY }}" node-05.feature.shimmer.iota.cafe: - validatorAccount: "{{ NODE_05_ACCOUNTID }}" - validatorPrivKey: "{{ NODE_05_VALIDATOR_PRIVKEY }}" - p2pIdentityPrivateKey: "{{ NODE_05_P2PIDENTITYPRIVATEKEY }}" + p2pIdentityPrvKey: "{{ NODE_05_P2PIDENTITY_PRIVKEY }}" 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..40beab818 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,129 @@ version: '3.3' services: - iota_core: + +################### +# 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 %} - --validator.ignoreBootstrapped=true - {% 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={{NODE_04_BLOCKISSUER_PRIVKEY}}" + command: > + --inx.address=iota-core:9029 + --restAPI.bindAddress=inx-blockissuer:9086 + --blockIssuer.accountAddress={{NODE_04_BLOCKISSUER_ACCOUNTADDRESS}} + --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 + ports: + - "8091:8091/tcp" # Faucet Frontend + environment: + - "FAUCET_PRV_KEY={{NODE_04_FAUCET_PRIVKEY}}" + command: > + --inx.address=iota-core:9029 + --faucet.bindAddress=0.0.0.0:8091 +{% endif %} + +{% if 'node-01' in inventory_hostname or 'node-02' in inventory_hostname or 'node-03' in inventory_hostname %} + inx-validator: + container_name: inx-validator + image: iotaledger/inx-validator:1.0-alpha + stop_grace_period: 1m + restart: unless-stopped + depends_on: + iota-core: + condition: service_started + environment: + - "VALIDATOR_PRV_KEY={{validatorPrvKey}}" + command: > + --inx.address=iota-core:9029 + {% if 'node-01' in inventory_hostname %} + --validator.ignoreBootstrapped=true + {% endif %} + --validator.accountAddress={{validatorAccountAddress}} +{% 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..62ee4840f 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_ACCOUNTADDRESS=$NODE_01_VALIDATOR_ACCOUNTADDRESS NODE_01_VALIDATOR_PRIVKEY=$NODE_01_VALIDATOR_PRIVKEY -NODE_01_P2PIDENTITYPRIVATEKEY=$NODE_01_P2PIDENTITYPRIVATEKEY -NODE_02_ACCOUNTID=$NODE_02_ACCOUNTID +NODE_01_P2PIDENTITY_PRIVKEY=$NODE_01_P2PIDENTITY_PRIVKEY + +NODE_02_VALIDATOR_ACCOUNTADDRESS=$NODE_02_VALIDATOR_ACCOUNTADDRESS NODE_02_VALIDATOR_PRIVKEY=$NODE_02_VALIDATOR_PRIVKEY -NODE_02_P2PIDENTITYPRIVATEKEY=$NODE_02_P2PIDENTITYPRIVATEKEY -NODE_03_ACCOUNTID=$NODE_03_ACCOUNTID +NODE_02_P2PIDENTITY_PRIVKEY=$NODE_02_P2PIDENTITY_PRIVKEY + +NODE_03_VALIDATOR_ACCOUNTADDRESS=$NODE_03_VALIDATOR_ACCOUNTADDRESS 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_05_P2PIDENTITYPRIVATEKEY=$NODE_05_P2PIDENTITYPRIVATEKEY -NODE_05_P2PPUBKEY=$NODE_05_P2PPUBKEY" \ +NODE_03_P2PIDENTITY_PRIVKEY=$NODE_03_P2PIDENTITY_PRIVKEY + +NODE_04_BLOCKISSUER_ACCOUNTADDRESS=$NODE_04_BLOCKISSUER_ACCOUNTADDRESS +NODE_04_BLOCKISSUER_PRIVKEY=$NODE_04_BLOCKISSUER_PRIVKEY +NODE_04_FAUCET_PRIVKEY=$NODE_04_FAUCET_PRIVKEY +NODE_04_P2PIDENTITY_PRIVKEY=$NODE_04_P2PIDENTITY_PRIVKEY + +NODE_05_P2PIDENTITY_PRIVKEY=$NODE_05_P2PIDENTITY_PRIVKEY" \ ${ARGS[@]:2} deploy/ansible/"${2:-deploy.yml}" diff --git a/documentation/docs/references/configuration.md b/documentation/docs/references/configuration.md index acf7bfa3f..b6b3d58e2 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 } } @@ -375,35 +375,7 @@ Example: } ``` -## 10. Validator - -| Name | Description | Type | Default value | -| -------------------------- | ------------------------------------------------------------------------------------------------------------ | ------- | ------------- | -| enabled | Whether the Validator component is enabled | boolean | false | -| committeeBroadcastInterval | The interval at which the node will broadcast its committee validator block | string | "500ms" | -| candidateBroadcastInterval | The interval at which the node will broadcast its candidate validator block | string | "30m" | -| parentsCount | The number of parents that node will choose for its validator blocks | int | 8 | -| ignoreBootstrapped | Whether the Validator component should start issuing validator blocks before the main engine is bootstrapped | boolean | false | -| account | The accountID of the validator account that will issue the blocks | string | "" | -| privateKey | The private key of the validator account that will issue the blocks | string | "" | - -Example: - -```json - { - "validator": { - "enabled": false, - "committeeBroadcastInterval": "500ms", - "candidateBroadcastInterval": "30m", - "parentsCount": 8, - "ignoreBootstrapped": false, - "account": "", - "privateKey": "" - } - } -``` - -## 11. Dashboard +## 10. Dashboard | Name | Description | Type | Default value | | --------------------------------- | --------------------------------------- | ------- | -------------- | @@ -445,7 +417,7 @@ Example: } ``` -## 12. Metrics +## 11. Metrics | Name | Description | Type | Default value | | --------------- | ---------------------------------------------------- | ------- | -------------- | @@ -469,7 +441,7 @@ Example: } ``` -## 13. Inx +## 12. Inx | Name | Description | Type | Default value | | ----------- | ------------------------------------------------------ | ------- | ---------------- | diff --git a/go.mod b/go.mod index 75f1f3d74..93103443f 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ replace github.com/goccy/go-graphviz => github.com/alexsporn/go-graphviz v0.0.0- require ( github.com/goccy/go-graphviz v0.1.1 github.com/golang-jwt/jwt v3.2.2+incompatible - github.com/google/uuid v1.3.1 + github.com/google/uuid v1.4.0 github.com/gorilla/websocket v1.5.0 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 github.com/iotaledger/hive.go/ads v0.0.0-20231102135205-0f3d1e75042a @@ -24,18 +24,20 @@ require ( github.com/iotaledger/hive.go/runtime v0.0.0-20231102135205-0f3d1e75042a github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231102135205-0f3d1e75042a github.com/iotaledger/hive.go/stringify v0.0.0-20231102135205-0f3d1e75042a - github.com/iotaledger/inx-app v1.0.0-rc.3.0.20231020152103-b6ea7ff7a4af - github.com/iotaledger/inx/go v1.0.0-rc.2.0.20231020151337-569450d5bf7d - github.com/iotaledger/iota.go/v4 v4.0.0-20231019174124-aa2290512bcd + github.com/iotaledger/inx-app v1.0.0-rc.3.0.20231031135002-4c79ea5193f5 + github.com/iotaledger/inx/go v1.0.0-rc.2.0.20231031134131-b6ad918dc1ac + github.com/iotaledger/iota.go/v4 v4.0.0-20231102113728-20b8d01e826e github.com/labstack/echo/v4 v4.11.2 github.com/labstack/gommon v0.4.0 - github.com/libp2p/go-libp2p v0.30.0 + github.com/libp2p/go-libp2p v0.32.0 github.com/libp2p/go-libp2p-kad-dht v0.25.1 - github.com/multiformats/go-multiaddr v0.11.0 + 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 @@ -68,37 +70,36 @@ require ( github.com/flynn/noise v1.0.0 // indirect github.com/fogleman/gg v1.3.0 // indirect github.com/francoispqt/gojay v1.2.13 // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/go-logr/logr v1.2.4 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/go-logr/logr v1.3.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect - github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-github v17.0.0+incompatible // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/gopacket v1.1.19 // indirect - github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f // indirect + github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-version v1.6.0 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/hashicorp/golang-lru v1.0.2 // indirect github.com/holiman/uint256 v1.2.3 // indirect github.com/huin/goupnp v1.3.0 // indirect github.com/iancoleman/orderedmap v0.3.0 // indirect github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7 // indirect - github.com/ipfs/boxo v0.10.0 // 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 github.com/ipfs/go-log v1.0.5 // indirect github.com/ipfs/go-log/v2 v2.5.1 // indirect - github.com/ipld/go-ipld-prime v0.20.0 // indirect + github.com/ipld/go-ipld-prime v0.21.0 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect github.com/jbenet/goprocess v0.1.4 // indirect - github.com/klauspost/compress v1.16.7 // indirect + github.com/klauspost/compress v1.17.2 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/knadh/koanf v1.5.0 // indirect github.com/koron/go-ssdp v0.0.4 // indirect @@ -109,7 +110,7 @@ require ( github.com/libp2p/go-libp2p-asn-util v0.3.0 // indirect github.com/libp2p/go-libp2p-kbucket v0.6.3 // indirect github.com/libp2p/go-libp2p-record v0.2.0 // indirect - github.com/libp2p/go-libp2p-routing-helpers v0.7.2 // indirect + github.com/libp2p/go-libp2p-routing-helpers v0.7.3 // indirect github.com/libp2p/go-msgio v0.3.0 // indirect github.com/libp2p/go-nat v0.2.0 // indirect github.com/libp2p/go-netroute v0.2.1 // indirect @@ -117,16 +118,15 @@ require ( github.com/libp2p/go-yamux/v4 v4.0.1 // indirect github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/miekg/dns v1.1.55 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect + github.com/miekg/dns v1.1.56 // indirect github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // 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/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 @@ -134,8 +134,8 @@ require ( 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-multistream v0.4.1 // indirect - github.com/onsi/ginkgo/v2 v2.12.0 // indirect + github.com/multiformats/go-multistream v0.5.0 // indirect + github.com/onsi/ginkgo/v2 v2.13.0 // indirect github.com/opencontainers/runtime-spec v1.1.0 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pasztorpisti/qs v0.0.0-20171216220353-8d6c33ee906c // indirect @@ -146,40 +146,40 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pokt-network/smt v0.6.1 // indirect github.com/polydawn/refmt v0.89.0 // indirect - github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect - github.com/prometheus/common v0.44.0 // indirect - github.com/prometheus/procfs v0.11.1 // indirect + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/common v0.45.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect - github.com/quic-go/qtls-go1-20 v0.3.3 // indirect - github.com/quic-go/quic-go v0.38.1 // indirect - github.com/quic-go/webtransport-go v0.5.3 // indirect + github.com/quic-go/qtls-go1-20 v0.4.1 // indirect + github.com/quic-go/quic-go v0.40.0 // indirect + github.com/quic-go/webtransport-go v0.6.0 // indirect github.com/raulk/go-watchdog v1.3.0 // 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/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 github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/otel v1.16.0 // indirect - go.opentelemetry.io/otel/metric v1.16.0 // indirect - go.opentelemetry.io/otel/trace v1.16.0 // indirect - go.uber.org/fx v1.20.0 // indirect + go.opentelemetry.io/otel v1.19.0 // indirect + go.opentelemetry.io/otel/metric v1.19.0 // indirect + go.opentelemetry.io/otel/trace v1.19.0 // indirect + go.uber.org/fx v1.20.1 // indirect + go.uber.org/mock v0.3.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect - golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect - golang.org/x/image v0.11.0 // indirect - golang.org/x/mod v0.12.0 // indirect + golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect + golang.org/x/image v0.13.0 // indirect + golang.org/x/mod v0.13.0 // 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/text v0.13.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.13.0 // indirect - gonum.org/v1/gonum v0.13.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b // indirect + golang.org/x/tools v0.14.0 // indirect + gonum.org/v1/gonum v0.14.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // 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/go.sum b/go.sum index 92dc401e9..8a89391f1 100644 --- a/go.sum +++ b/go.sum @@ -115,12 +115,12 @@ github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= -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/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 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/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= @@ -132,8 +132,8 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 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-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -160,8 +160,6 @@ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4er github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= 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= @@ -204,12 +202,12 @@ github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= -github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f h1:pDhu5sgp8yJlEF/g6osliIIpF9K4F5jvkULXa4daRDQ= -github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= +github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b h1:RMpPgZTSApbPf7xaVel+QkoGPRLFLrwFO89uDUHEGf0= +github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 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/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -254,8 +252,8 @@ github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mO github.com/hashicorp/go-version v1.6.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/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= +github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= 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= @@ -305,14 +303,14 @@ github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231102135205-0f3d1e7 github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231102135205-0f3d1e75042a/go.mod h1:SdK26z8/VhWtxaqCuQrufm80SELgowQPmu9T/8eUQ8g= github.com/iotaledger/hive.go/stringify v0.0.0-20231102135205-0f3d1e75042a h1:DNcRsEdvfnrfwh9Mb19lHfsz5Izf9BiMqJILYYW0SvU= github.com/iotaledger/hive.go/stringify v0.0.0-20231102135205-0f3d1e75042a/go.mod h1:FTo/UWzNYgnQ082GI9QVM9HFDERqf9rw9RivNpqrnTs= -github.com/iotaledger/inx-app v1.0.0-rc.3.0.20231020152103-b6ea7ff7a4af h1:3+heabXNGjv3Sx1sg0qG+QdqeEWIpf7jM3T5cPiVmSA= -github.com/iotaledger/inx-app v1.0.0-rc.3.0.20231020152103-b6ea7ff7a4af/go.mod h1:3BZ5r/MhswUZwXHDzMZacOhm6DWpUdGjefNRNshfwiM= -github.com/iotaledger/inx/go v1.0.0-rc.2.0.20231020151337-569450d5bf7d h1:PeJNFBkR28hdQCxUyC/cbOlDjFm3D1U7O5bxi+qsOvQ= -github.com/iotaledger/inx/go v1.0.0-rc.2.0.20231020151337-569450d5bf7d/go.mod h1:/LK3WJ2UhPPAbKTdvRasz0xaQ9qaMI2isYAX9d75WNU= -github.com/iotaledger/iota.go/v4 v4.0.0-20231019174124-aa2290512bcd h1:FKiEzy3gOyWDGXs6mTMS9qFHGXSxH97N5w+08ljbEtU= -github.com/iotaledger/iota.go/v4 v4.0.0-20231019174124-aa2290512bcd/go.mod h1:XFwcDfAF0hv6wB/trCsyDmQyPLjEZ60yTrllJ2bprN8= -github.com/ipfs/boxo v0.10.0 h1:tdDAxq8jrsbRkYoF+5Rcqyeb91hgWe2hp7iLu7ORZLY= -github.com/ipfs/boxo v0.10.0/go.mod h1:Fg+BnfxZ0RPzR0nOodzdIq3A7KgoWAOWsEIImrIQdBM= +github.com/iotaledger/inx-app v1.0.0-rc.3.0.20231031135002-4c79ea5193f5 h1:17JDzMKTMXKF3xys6gPURRddkZhg1LY+xwfhbr/sVqg= +github.com/iotaledger/inx-app v1.0.0-rc.3.0.20231031135002-4c79ea5193f5/go.mod h1:LsJvoBUVVnY7tkwwByCVtAwmp5bFXdyJNGU/+KVQJVM= +github.com/iotaledger/inx/go v1.0.0-rc.2.0.20231031134131-b6ad918dc1ac h1:c7R33+TQGMYP6pvLUQQaqpdDFl+GZbhAcfGMI0285fo= +github.com/iotaledger/inx/go v1.0.0-rc.2.0.20231031134131-b6ad918dc1ac/go.mod h1:qPuMUvCTaghsnYRDnRoRuztTyEKFlmi2S7gb44rH7WM= +github.com/iotaledger/iota.go/v4 v4.0.0-20231102113728-20b8d01e826e h1:ZYRC1MHn/ghsqtjIpYGTxLQrh5n5eUmC0/YWnJiTRhk= +github.com/iotaledger/iota.go/v4 v4.0.0-20231102113728-20b8d01e826e/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= github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= github.com/ipfs/go-datastore v0.6.0 h1:JKyz+Gvz1QEZw0LsX1IBn+JFCJQH4SJVFtM4uWU0Myk= @@ -326,8 +324,8 @@ github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JP github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g= github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY= github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI= -github.com/ipld/go-ipld-prime v0.20.0 h1:Ud3VwE9ClxpO2LkCYP7vWPc0Fo+dYdYzgxUJZ3uRG4g= -github.com/ipld/go-ipld-prime v0.20.0/go.mod h1:PzqZ/ZR981eKbgdr3y2DJYeD/8bgMawdGVlJDE8kK+M= +github.com/ipld/go-ipld-prime v0.21.0 h1:n4JmcpOlPDIxBcY037SVfpd1G+Sj1nKZah0m6QH9C2E= +github.com/ipld/go-ipld-prime v0.21.0/go.mod h1:3RLqy//ERg/y5oShXXdx5YIp50cFGOanyMctpPjsvxQ= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= @@ -352,8 +350,8 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8 github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= 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/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= -github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4= +github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= 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= @@ -383,8 +381,8 @@ github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38y github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic= github.com/libp2p/go-flow-metrics v0.1.0 h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFGkx3Q3WM= github.com/libp2p/go-flow-metrics v0.1.0/go.mod h1:4Xi8MX8wj5aWNDAZttg6UPmc0ZrnFNsMtpsYUClFtro= -github.com/libp2p/go-libp2p v0.30.0 h1:9EZwFtJPFBcs/yJTnP90TpN1hgrT/EsFfM+OZuwV87U= -github.com/libp2p/go-libp2p v0.30.0/go.mod h1:nr2g5V7lfftwgiJ78/HrID+pwvayLyqKCEirT2Y3Byg= +github.com/libp2p/go-libp2p v0.32.0 h1:86I4B7nBUPIyTgw3+5Ibq6K7DdKRCuZw8URCfPc1hQM= +github.com/libp2p/go-libp2p v0.32.0/go.mod h1:hXXC3kXPlBZ1eu8Q2hptGrMB4mZ3048JUoS4EKaHW5c= github.com/libp2p/go-libp2p-asn-util v0.3.0 h1:gMDcMyYiZKkocGXDQ5nsUQyquC9+H+iLEQHwOCZ7s8s= github.com/libp2p/go-libp2p-asn-util v0.3.0/go.mod h1:B1mcOrKUE35Xq/ASTmQ4tN3LNzVVaMNmq2NACuqyB9w= github.com/libp2p/go-libp2p-kad-dht v0.25.1 h1:ofFNrf6MMEy4vi3R1VbJ7LOcTn3Csh0cDcaWHTxtWNA= @@ -393,8 +391,8 @@ github.com/libp2p/go-libp2p-kbucket v0.6.3 h1:p507271wWzpy2f1XxPzCQG9NiN6R6lHL9G github.com/libp2p/go-libp2p-kbucket v0.6.3/go.mod h1:RCseT7AH6eJWxxk2ol03xtP9pEHetYSPXOaJnOiD8i0= github.com/libp2p/go-libp2p-record v0.2.0 h1:oiNUOCWno2BFuxt3my4i1frNrt7PerzB3queqa1NkQ0= github.com/libp2p/go-libp2p-record v0.2.0/go.mod h1:I+3zMkvvg5m2OcSdoL0KPljyJyvNDFGKX7QdlpYUcwk= -github.com/libp2p/go-libp2p-routing-helpers v0.7.2 h1:xJMFyhQ3Iuqnk9Q2dYE1eUTzsah7NLw3Qs2zjUV78T0= -github.com/libp2p/go-libp2p-routing-helpers v0.7.2/go.mod h1:cN4mJAD/7zfPKXBcs9ze31JGYAZgzdABEm+q/hkswb8= +github.com/libp2p/go-libp2p-routing-helpers v0.7.3 h1:u1LGzAMVRK9Nqq5aYDVOiq/HaB93U9WWczBzGyAC5ZY= +github.com/libp2p/go-libp2p-routing-helpers v0.7.3/go.mod h1:cN4mJAD/7zfPKXBcs9ze31JGYAZgzdABEm+q/hkswb8= github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg= github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0= @@ -424,16 +422,16 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +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/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= 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/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo= -github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= +github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= +github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms= github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc= @@ -473,8 +471,8 @@ github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9 github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4= -github.com/multiformats/go-multiaddr v0.11.0 h1:XqGyJ8ufbCE0HmTDwx2kPdsrQ36AGPZNZX6s6xfJH10= -github.com/multiformats/go-multiaddr v0.11.0/go.mod h1:gWUm0QLR4thQ6+ZF6SXUw8YjtwQSPapICM+NmCkxHSM= +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-multiaddr-dns v0.3.1 h1:QgQgR+LQVt3NPTjbrLLpsaT2ufAA2y0Mkk+QRVJbW3A= github.com/multiformats/go-multiaddr-dns v0.3.1/go.mod h1:G/245BRQ6FJGmryJCrOuTdB37AMA5AMOVuO6NY3JwTk= github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= @@ -486,8 +484,8 @@ github.com/multiformats/go-multicodec v0.9.0/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI1 github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= 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-multistream v0.4.1 h1:rFy0Iiyn3YT0asivDUIR05leAdwZq3de4741sbiSdfo= -github.com/multiformats/go-multistream v0.4.1/go.mod h1:Mz5eykRVAjJWckE2U78c6xqdtyNUEhKSM0Lwar2p77Q= +github.com/multiformats/go-multistream v0.5.0 h1:5htLSLl7lvJk3xx3qT/8Zm9J4K8vEOf/QGkvOGQAyiE= +github.com/multiformats/go-multistream v0.5.0/go.mod h1:n6tMZiwiP2wUsR8DgfDWw1dydlEqV3l6N3/GBsX6ILA= github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= 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= @@ -499,8 +497,8 @@ github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5 h1:BvoENQQU+fZ9uukda/R github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= 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/onsi/ginkgo/v2 v2.12.0 h1:UIVDowFPwpg6yMUpPjGkYvf06K3RAiJXUhCxEwQVHRI= -github.com/onsi/ginkgo/v2 v2.12.0/go.mod h1:ZNEzXISYlqpb8S36iN71ifqLi3vVD1rVJGvWRCJOUpQ= +github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= +github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= @@ -552,29 +550,29 @@ github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1: 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/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM= -github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 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/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= -github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= +github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= +github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 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/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= -github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= -github.com/quic-go/qtls-go1-20 v0.3.3 h1:17/glZSLI9P9fDAeyCHBFSWSqJcwx1byhLwP5eUIDCM= -github.com/quic-go/qtls-go1-20 v0.3.3/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= -github.com/quic-go/quic-go v0.38.1 h1:M36YWA5dEhEeT+slOu/SwMEucbYd0YFidxG3KlGPZaE= -github.com/quic-go/quic-go v0.38.1/go.mod h1:ijnZM7JsFIkp4cRyjxJNIzdSfCLmUMg9wdyhGmg+SN4= -github.com/quic-go/webtransport-go v0.5.3 h1:5XMlzemqB4qmOlgIus5zB45AcZ2kCgCy2EptUrfOPWU= -github.com/quic-go/webtransport-go v0.5.3/go.mod h1:OhmmgJIzTTqXK5xvtuX0oBpLV2GkLWNDA+UeTGJXErU= +github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs= +github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= +github.com/quic-go/quic-go v0.40.0 h1:GYd1iznlKm7dpHD7pOVpUvItgMPo/jrMgDWZhMCecqw= +github.com/quic-go/quic-go v0.40.0/go.mod h1:PeN7kuVJ4xZbxSv/4OX6S1USOX8MJvydwpTx31vx60c= +github.com/quic-go/webtransport-go v0.6.0 h1:CvNsKqc4W2HljHJnoT+rMmbRJybShZ0YPFDD3NxaZLY= +github.com/quic-go/webtransport-go v0.6.0/go.mod h1:9KjU4AEBqEQidGHNDkZrb8CAa1abRaosM2yGOyiikEc= github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk= github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU= github.com/rhnvrm/simples3 v0.6.1/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA= @@ -669,7 +667,6 @@ github.com/wollac/iota-crypto-demo v0.0.0-20221117162917-b10619eccb98/go.mod h1: 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= @@ -678,23 +675,25 @@ go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/ go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= -go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= -go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= -go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= -go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= -go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= +go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= +go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= +go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= +go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= +go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 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/dig v1.17.1 h1:Tga8Lz8PcYNsWsyHMZ1Vm0OQOUaJNDyvPImgbAu9YSc= go.uber.org/dig v1.17.1/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= -go.uber.org/fx v1.20.0 h1:ZMC/pnRvhsthOZh9MZjMq5U8Or3mA9zBSPaLnzs3ihQ= -go.uber.org/fx v1.20.0/go.mod h1:qCUj0btiR3/JnanEr1TYEePfSw6o/4qYJscgvzQ5Ub0= +go.uber.org/fx v1.20.1 h1:zVwVQGS8zYvhh9Xxcu4w1M6ESyeMzebzj2NbSayZ4Mk= +go.uber.org/fx v1.20.1/go.mod h1:iSYNbHf2y55acNCwCXKx7LbWb5WG1Bnue5RDXz1OREg= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= 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/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo= +go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= @@ -718,14 +717,13 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -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-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= -golang.org/x/image v0.11.0 h1:ds2RoQvBvYTiJkwpSFDwCcDFNX7DqjL2WsUgTNk0Ooo= -golang.org/x/image v0.11.0/go.mod h1:bglhjqbqVuEb9e9+eNR45Jfu7D+T4Qan+NhQk8Ck2P8= +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/image v0.13.0 h1:3cge/F/QTkNLauhf2QoE9zp+7sr+ZcL4HnoZmdwg9sg= +golang.org/x/image v0.13.0/go.mod h1:6mmbMOeV28HuMTgA6OSRkdXKYw/t5W9Uwn2Yv1r3Yxk= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 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= @@ -738,10 +736,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB 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/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= +golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -765,8 +761,6 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v 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.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 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= @@ -784,8 +778,6 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ 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.1.0/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-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -823,22 +815,16 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w 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-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/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.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -846,9 +832,6 @@ 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.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 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-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -873,19 +856,16 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn 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.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= +golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= 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= -gonum.org/v1/gonum v0.13.0 h1:a0T3bh+7fhRyqeNbiC3qVHYmkiQgit3wnNan/2c0HMM= -gonum.org/v1/gonum v0.13.0/go.mod h1:/WPYRckkfWrhWefxyYTfrTtQR0KH4iyHNuzxqXAKyAU= +gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0= +gonum.org/v1/gonum v0.14.0/go.mod h1:AoWeoz0becf9QMWtE8iWXNXc27fK4fNeHNf/oMejGfU= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= @@ -903,8 +883,8 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98 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/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b h1:ZlWIi1wSK56/8hn4QcBp/j9M7Gt3U/3hZw3mC7vDICo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 h1:AB/lmRny7e2pLhFEYIbl5qkDAUt2h0ZRO4wGPhZf+ik= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405/go.mod h1:67X1fPuzjcrkymZzZV1vvkFeTn2Rvc6lYF9MYFGCcwE= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= diff --git a/pkg/blockhandler/account.go b/pkg/blockhandler/account.go deleted file mode 100644 index 5d71cb86b..000000000 --- a/pkg/blockhandler/account.go +++ /dev/null @@ -1,70 +0,0 @@ -package blockhandler - -import ( - "crypto/ed25519" - "fmt" - - "github.com/iotaledger/hive.go/crypto" - iotago "github.com/iotaledger/iota.go/v4" -) - -// Account represents an account. -type Account interface { - // ID returns the accountID. - ID() iotago.AccountID - - // Address returns the account address. - Address() iotago.Address - - // PrivateKey returns the account private key for signing. - PrivateKey() ed25519.PrivateKey -} - -var _ Account = &Ed25519Account{} - -// Ed25519Account is an account that uses an Ed25519 key pair. -type Ed25519Account struct { - accountID iotago.AccountID - privateKey ed25519.PrivateKey -} - -// NewEd25519Account creates a new Ed25519Account. -func NewEd25519Account(accountID iotago.AccountID, privateKey ed25519.PrivateKey) *Ed25519Account { - return &Ed25519Account{ - accountID: accountID, - privateKey: privateKey, - } -} - -// ID returns the accountID. -func (e *Ed25519Account) ID() iotago.AccountID { - return e.accountID -} - -// Address returns the account address. -func (e *Ed25519Account) Address() iotago.Address { - ed25519PubKey, ok := e.privateKey.Public().(ed25519.PublicKey) - if !ok { - panic("invalid public key type") - } - - return iotago.Ed25519AddressFromPubKey(ed25519PubKey) -} - -// PrivateKey returns the account private key for signing. -func (e *Ed25519Account) PrivateKey() ed25519.PrivateKey { - return e.privateKey -} - -func AccountFromParams(accountHex, privateKey string) Account { - accountID, err := iotago.AccountIDFromHexString(accountHex) - if err != nil { - panic(fmt.Sprintln("invalid accountID hex string", err)) - } - privKey, err := crypto.ParseEd25519PrivateKeyFromString(privateKey) - if err != nil { - panic(fmt.Sprintln("invalid ed25519 private key string", err)) - } - - return NewEd25519Account(accountID, privKey) -} diff --git a/pkg/blockhandler/block_params.go b/pkg/blockhandler/block_params.go deleted file mode 100644 index 951f285c8..000000000 --- a/pkg/blockhandler/block_params.go +++ /dev/null @@ -1,122 +0,0 @@ -package blockhandler - -import ( - "time" - - "github.com/iotaledger/hive.go/runtime/options" - "github.com/iotaledger/iota-core/pkg/model" - iotago "github.com/iotaledger/iota.go/v4" -) - -type BlockHeaderParams struct { - ParentsCount int - References model.ParentReferences - SlotCommitment *iotago.Commitment - LatestFinalizedSlot *iotago.SlotIndex - IssuingTime *time.Time - ProtocolVersion *iotago.Version - Issuer Account -} -type BasicBlockParams struct { - BlockHeader *BlockHeaderParams - Payload iotago.Payload -} -type ValidatorBlockParams struct { - BlockHeader *BlockHeaderParams - HighestSupportedVersion *iotago.Version - ProtocolParametersHash *iotago.Identifier -} - -func WithParentsCount(parentsCount int) func(builder *BlockHeaderParams) { - return func(builder *BlockHeaderParams) { - builder.ParentsCount = parentsCount - } -} - -func WithStrongParents(blockIDs ...iotago.BlockID) func(builder *BlockHeaderParams) { - return func(builder *BlockHeaderParams) { - if builder.References == nil { - builder.References = make(model.ParentReferences) - } - - builder.References[iotago.StrongParentType] = blockIDs - } -} -func WithWeakParents(blockIDs ...iotago.BlockID) func(builder *BlockHeaderParams) { - return func(builder *BlockHeaderParams) { - if builder.References == nil { - builder.References = make(model.ParentReferences) - } - - builder.References[iotago.WeakParentType] = blockIDs - } -} - -func WithShallowLikeParents(blockIDs ...iotago.BlockID) func(builder *BlockHeaderParams) { - return func(builder *BlockHeaderParams) { - if builder.References == nil { - builder.References = make(model.ParentReferences) - } - - builder.References[iotago.ShallowLikeParentType] = blockIDs - } -} - -func WithSlotCommitment(commitment *iotago.Commitment) func(builder *BlockHeaderParams) { - return func(builder *BlockHeaderParams) { - builder.SlotCommitment = commitment - } -} - -func WithLatestFinalizedSlot(commitmentIndex iotago.SlotIndex) func(builder *BlockHeaderParams) { - return func(builder *BlockHeaderParams) { - builder.LatestFinalizedSlot = &commitmentIndex - } -} - -func WithIssuingTime(issuingTime time.Time) func(builder *BlockHeaderParams) { - return func(builder *BlockHeaderParams) { - builder.IssuingTime = &issuingTime - } -} - -func WithProtocolVersion(version iotago.Version) func(builder *BlockHeaderParams) { - return func(builder *BlockHeaderParams) { - builder.ProtocolVersion = &version - } -} -func WithIssuer(issuer Account) func(builder *BlockHeaderParams) { - return func(builder *BlockHeaderParams) { - builder.Issuer = issuer - } -} - -func WithValidationBlockHeaderOptions(opts ...options.Option[BlockHeaderParams]) func(builder *ValidatorBlockParams) { - return func(builder *ValidatorBlockParams) { - builder.BlockHeader = options.Apply(&BlockHeaderParams{}, opts) - } -} - -func WithBasicBlockHeader(opts ...options.Option[BlockHeaderParams]) func(builder *BasicBlockParams) { - return func(builder *BasicBlockParams) { - builder.BlockHeader = options.Apply(&BlockHeaderParams{}, opts) - } -} - -func WithPayload(payload iotago.Payload) func(builder *BasicBlockParams) { - return func(builder *BasicBlockParams) { - builder.Payload = payload - } -} - -func WithHighestSupportedVersion(highestSupportedVersion iotago.Version) func(builder *ValidatorBlockParams) { - return func(builder *ValidatorBlockParams) { - builder.HighestSupportedVersion = &highestSupportedVersion - } -} - -func WithProtocolParametersHash(protocolParametersHash iotago.Identifier) func(builder *ValidatorBlockParams) { - return func(builder *ValidatorBlockParams) { - builder.ProtocolParametersHash = &protocolParametersHash - } -} diff --git a/pkg/blockhandler/blockissuer.go b/pkg/blockhandler/blockhandler.go similarity index 98% rename from pkg/blockhandler/blockissuer.go rename to pkg/blockhandler/blockhandler.go index ed6c99f01..b712bab88 100644 --- a/pkg/blockhandler/blockissuer.go +++ b/pkg/blockhandler/blockhandler.go @@ -94,7 +94,7 @@ func (i *BlockHandler) SubmitBlockAndAwaitEvent(ctx context.Context, block *mode } } -func (i *BlockHandler) AttachBlock(ctx context.Context, iotaBlock *iotago.ProtocolBlock) (iotago.BlockID, error) { +func (i *BlockHandler) AttachBlock(ctx context.Context, iotaBlock *iotago.Block) (iotago.BlockID, error) { modelBlock, err := model.BlockFromBlock(iotaBlock) if err != nil { return iotago.EmptyBlockID, ierrors.Wrap(err, "error serializing block to model block") diff --git a/pkg/model/block.go b/pkg/model/block.go index d5c212153..a41c491c0 100644 --- a/pkg/model/block.go +++ b/pkg/model/block.go @@ -11,45 +11,45 @@ import ( type Block struct { blockID iotago.BlockID - data []byte - protocolBlock *iotago.ProtocolBlock + data []byte + block *iotago.Block } -func newBlock(blockID iotago.BlockID, iotaBlock *iotago.ProtocolBlock, data []byte) (*Block, error) { +func newBlock(blockID iotago.BlockID, iotaBlock *iotago.Block, data []byte) (*Block, error) { block := &Block{ - blockID: blockID, - data: data, - protocolBlock: iotaBlock, + blockID: blockID, + data: data, + block: iotaBlock, } return block, nil } -func BlockFromBlock(protocolBlock *iotago.ProtocolBlock, opts ...serix.Option) (*Block, error) { - data, err := protocolBlock.API.Encode(protocolBlock, opts...) +func BlockFromBlock(block *iotago.Block, opts ...serix.Option) (*Block, error) { + data, err := block.API.Encode(block, opts...) if err != nil { return nil, err } - blockID, err := protocolBlock.ID() + blockID, err := block.ID() if err != nil { return nil, err } - return newBlock(blockID, protocolBlock, data) + return newBlock(blockID, block, data) } func BlockFromIDAndBytes(blockID iotago.BlockID, data []byte, api iotago.API, opts ...serix.Option) (*Block, error) { - protocolBlock := new(iotago.ProtocolBlock) - if _, err := api.Decode(data, protocolBlock, opts...); err != nil { + block := new(iotago.Block) + if _, err := api.Decode(data, block, opts...); err != nil { return nil, err } - return newBlock(blockID, protocolBlock, data) + return newBlock(blockID, block, data) } func BlockFromBytes(data []byte, apiProvider iotago.APIProvider) (*Block, error) { - iotaBlock, _, err := iotago.ProtocolBlockFromBytes(apiProvider)(data) + iotaBlock, _, err := iotago.BlockFromBytes(apiProvider)(data) if err != nil { return nil, err } @@ -85,8 +85,8 @@ func (blk *Block) Bytes() ([]byte, error) { return blk.data, nil } -func (blk *Block) ProtocolBlock() *iotago.ProtocolBlock { - return blk.protocolBlock +func (blk *Block) ProtocolBlock() *iotago.Block { + return blk.block } func (blk *Block) Payload() iotago.Payload { @@ -109,18 +109,18 @@ func (blk *Block) SignedTransaction() (tx *iotago.SignedTransaction, isTransacti return tx, isTransaction } -func (blk *Block) BasicBlock() (basicBlock *iotago.BasicBlock, isBasicBlock bool) { - basicBlock, isBasicBlock = blk.ProtocolBlock().Block.(*iotago.BasicBlock) +func (blk *Block) BasicBlock() (basicBlock *iotago.BasicBlockBody, isBasicBlock bool) { + basicBlock, isBasicBlock = blk.ProtocolBlock().Body.(*iotago.BasicBlockBody) return basicBlock, isBasicBlock } -func (blk *Block) ValidationBlock() (validationBlock *iotago.ValidationBlock, isValidationBlock bool) { - validationBlock, isValidationBlock = blk.ProtocolBlock().Block.(*iotago.ValidationBlock) +func (blk *Block) ValidationBlock() (validationBlock *iotago.ValidationBlockBody, isValidationBlock bool) { + validationBlock, isValidationBlock = blk.ProtocolBlock().Body.(*iotago.ValidationBlockBody) return validationBlock, isValidationBlock } func (blk *Block) String() string { - encode, err := blk.protocolBlock.API.JSONEncode(blk.ProtocolBlock()) + encode, err := blk.block.API.JSONEncode(blk.ProtocolBlock()) if err != nil { panic(err) } diff --git a/pkg/model/signaled_block.go b/pkg/model/signaled_block.go index e620b243d..c4bf457d4 100644 --- a/pkg/model/signaled_block.go +++ b/pkg/model/signaled_block.go @@ -15,10 +15,10 @@ type SignaledBlock struct { ProtocolParametersHash iotago.Identifier `serix:"3"` } -func NewSignaledBlock(blockID iotago.BlockID, block *iotago.ProtocolBlock, validationBlock *iotago.ValidationBlock) *SignaledBlock { +func NewSignaledBlock(blockID iotago.BlockID, block *iotago.Block, validationBlock *iotago.ValidationBlockBody) *SignaledBlock { return &SignaledBlock{ ID: blockID, - IssuingTime: block.IssuingTime, + IssuingTime: block.Header.IssuingTime, HighestSupportedVersion: validationBlock.HighestSupportedVersion, ProtocolParametersHash: validationBlock.ProtocolParametersHash, } diff --git a/pkg/protocol/commitment_verifier.go b/pkg/protocol/commitment_verifier.go index 8e49f4c42..4abf24704 100644 --- a/pkg/protocol/commitment_verifier.go +++ b/pkg/protocol/commitment_verifier.go @@ -18,22 +18,25 @@ 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, - 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 *Commitment, attestations []*iotago.Attestation, merkleProof *merklehasher.Proof[iotago.Identifier]) (blockIDsFromAttestations iotago.BlockIDs, cumulativeWeight uint64, err error) { tree := ads.NewMap[iotago.Identifier](mapdb.NewMapDB(), iotago.AccountID.Bytes, iotago.AccountIDFromBytes, (*iotago.Attestation).Bytes, iotago.AttestationFromBytes(c.engine)) for _, att := range attestations { - if setErr := tree.Set(att.IssuerID, att); setErr != nil { - return nil, 0, ierrors.Wrapf(err, "failed to set attestation for issuerID %s", att.IssuerID) + if setErr := tree.Set(att.Header.IssuerID, att); setErr != nil { + return nil, 0, ierrors.Wrapf(err, "failed to set attestation for issuerID %s", att.Header.IssuerID) } } @@ -64,11 +67,11 @@ func (c *CommitmentVerifier) verifyAttestations(attestations []*iotago.Attestati // 1. The attestation might be fake. // 2. The issuer might have added a new public key in the meantime, but we don't know about it yet // since we only have the ledger state at the forking point. - accountData, exists := c.validatorAccountsAtFork[att.IssuerID] + accountData, exists := c.validatorAccountsAtFork[att.Header.IssuerID] // We always need to have the accountData for a validator. if !exists { - return nil, 0, ierrors.Errorf("accountData for issuerID %s does not exist", att.IssuerID) + return nil, 0, ierrors.Errorf("accountData for issuerID %s does not exist", att.Header.IssuerID) } switch signature := att.Signature.(type) { @@ -92,8 +95,8 @@ func (c *CommitmentVerifier) verifyAttestations(attestations []*iotago.Attestati } // 3. A valid set of attestations can't contain multiple attestations from the same issuerID. - if visitedIdentities.Has(att.IssuerID) { - return nil, 0, ierrors.Errorf("issuerID %s contained in multiple attestations", att.IssuerID) + if visitedIdentities.Has(att.Header.IssuerID) { + return nil, 0, ierrors.Errorf("issuerID %s contained in multiple attestations", att.Header.IssuerID) } // TODO: this might differ if we have a Accounts with changing weights depending on the SlotIndex/epoch @@ -101,11 +104,17 @@ 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.Header.IssuerID); seatExists { seatCount++ } - visitedIdentities.Add(att.IssuerID) + visitedIdentities.Add(att.Header.IssuerID) blockID, err := att.BlockID() if err != nil { diff --git a/pkg/protocol/engine/accounts/accountsledger/manager.go b/pkg/protocol/engine/accounts/accountsledger/manager.go index c369914e7..814b27c57 100644 --- a/pkg/protocol/engine/accounts/accountsledger/manager.go +++ b/pkg/protocol/engine/accounts/accountsledger/manager.go @@ -98,7 +98,7 @@ func (m *Manager) TrackBlock(block *blocks.Block) { if validationBlock, isValidationBlock := block.ValidationBlock(); isValidationBlock { newSignaledBlock := model.NewSignaledBlock(block.ID(), block.ProtocolBlock(), validationBlock) - m.latestSupportedVersionSignals.Get(block.ID().Slot(), true).Compute(block.ProtocolBlock().IssuerID, func(currentValue *model.SignaledBlock, exists bool) *model.SignaledBlock { + m.latestSupportedVersionSignals.Get(block.ID().Slot(), true).Compute(block.ProtocolBlock().Header.IssuerID, func(currentValue *model.SignaledBlock, exists bool) *model.SignaledBlock { if !exists { return newSignaledBlock } @@ -496,9 +496,9 @@ func (m *Manager) computeBlockBurnsForSlot(slot iotago.SlotIndex, rmc iotago.Man return nil, ierrors.Errorf("cannot apply the new diff, block %s not found in the block cache", blockID) } if _, isBasicBlock := block.BasicBlock(); isBasicBlock { - burns[block.ProtocolBlock().IssuerID] += iotago.Mana(block.WorkScore()) * rmc + burns[block.ProtocolBlock().Header.IssuerID] += iotago.Mana(block.WorkScore()) * rmc } else if _, isValidationBlock := block.ValidationBlock(); isValidationBlock { - validationBlockCount[block.ProtocolBlock().IssuerID]++ + validationBlockCount[block.ProtocolBlock().Header.IssuerID]++ } } validationBlocksPerSlot := int(apiForSlot.ProtocolParameters().ValidationBlocksPerSlot()) diff --git a/pkg/protocol/engine/accounts/accountsledger/testsuite_test.go b/pkg/protocol/engine/accounts/accountsledger/testsuite_test.go index b090c64c5..64d7f8152 100644 --- a/pkg/protocol/engine/accounts/accountsledger/testsuite_test.go +++ b/pkg/protocol/engine/accounts/accountsledger/testsuite_test.go @@ -190,7 +190,7 @@ func (t *TestSuite) ApplySlotActions(slot iotago.SlotIndex, rmc iotago.Mana, act func (t *TestSuite) createBlockWithRMC(accountID iotago.AccountID, slot iotago.SlotIndex, rmc iotago.Mana) *blocks.Block { innerBlock := tpkg.RandBasicBlockWithIssuerAndRMC(tpkg.TestAPI, accountID, rmc) - innerBlock.IssuingTime = tpkg.TestAPI.TimeProvider().SlotStartTime(slot) + innerBlock.Header.IssuingTime = tpkg.TestAPI.TimeProvider().SlotStartTime(slot) modelBlock, err := model.BlockFromBlock(innerBlock) require.NoError(t.T, err) diff --git a/pkg/protocol/engine/attestation/attestations.go b/pkg/protocol/engine/attestation/attestations.go index 35e9fe7fa..859d16486 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 4486e89b6..ac69e3871 100644 --- a/pkg/protocol/engine/attestation/slotattestation/manager.go +++ b/pkg/protocol/engine/attestation/slotattestation/manager.go @@ -50,7 +50,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] @@ -74,7 +74,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, ) }) @@ -84,7 +84,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{ @@ -146,15 +146,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().Header.IssuerID); !exists { + return nil } m.commitmentMutex.RLock() @@ -162,13 +166,13 @@ 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()) // We keep only the latest attestation for each committee member. - m.futureAttestations.Get(block.ID().Slot(), true).Compute(block.ProtocolBlock().IssuerID, func(currentValue *iotago.Attestation, exists bool) *iotago.Attestation { + m.futureAttestations.Get(block.ID().Slot(), true).Compute(block.ProtocolBlock().Header.IssuerID, func(currentValue *iotago.Attestation, exists bool) *iotago.Attestation { if !exists { return newAttestation } @@ -180,16 +184,18 @@ func (m *Manager) AddAttestationFromValidationBlock(block *blocks.Block) { return currentValue }) + + return nil } func (m *Manager) applyToPendingAttestations(attestation *iotago.Attestation, cutoffSlot iotago.SlotIndex) { - if attestation.SlotCommitmentID.Slot() < cutoffSlot { + if attestation.Header.SlotCommitmentID.Slot() < cutoffSlot { return } updated := true - updatedAttestation := m.pendingAttestations.Get(cutoffSlot, true).Compute(attestation.IssuerID, func(currentValue *iotago.Attestation, exists bool) *iotago.Attestation { + updatedAttestation := m.pendingAttestations.Get(cutoffSlot, true).Compute(attestation.Header.IssuerID, func(currentValue *iotago.Attestation, exists bool) *iotago.Attestation { if !exists { return attestation } @@ -209,8 +215,8 @@ func (m *Manager) applyToPendingAttestations(attestation *iotago.Attestation, cu return } - for i := cutoffSlot; i <= updatedAttestation.SlotCommitmentID.Slot(); i++ { - m.pendingAttestations.Get(i, true).Set(attestation.IssuerID, updatedAttestation) + for i := cutoffSlot; i <= updatedAttestation.Header.SlotCommitmentID.Slot(); i++ { + m.pendingAttestations.Get(i, true).Set(attestation.Header.IssuerID, updatedAttestation) } } @@ -255,11 +261,16 @@ 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 err := tree.Set(a.IssuerID, a); err != nil { - return 0, iotago.Identifier{}, ierrors.Wrapf(err, "failed to set attestation %s in tree", a.IssuerID) + if _, exists := committee.GetSeat(a.Header.IssuerID); exists { + if err := tree.Set(a.Header.IssuerID, a); err != nil { + return 0, iotago.Identifier{}, ierrors.Wrapf(err, "failed to set attestation %s in tree", a.Header.IssuerID) } m.lastCumulativeWeight++ diff --git a/pkg/protocol/engine/attestation/slotattestation/snapshot.go b/pkg/protocol/engine/attestation/slotattestation/snapshot.go index 12f70bd4a..ec2ae69fd 100644 --- a/pkg/protocol/engine/attestation/slotattestation/snapshot.go +++ b/pkg/protocol/engine/attestation/slotattestation/snapshot.go @@ -90,9 +90,9 @@ func (m *Manager) Export(writer io.WriteSeeker, targetSlot iotago.SlotIndex) err if err = stream.WriteCollection(writer, func() (uint64, error) { for _, a := range attestations { - apiForVersion, err := m.apiProvider.APIForVersion(a.ProtocolVersion) + apiForVersion, err := m.apiProvider.APIForVersion(a.Header.ProtocolVersion) if err != nil { - return 0, ierrors.Wrapf(err, "failed to get API for version %d", a.ProtocolVersion) + return 0, ierrors.Wrapf(err, "failed to get API for version %d", a.Header.ProtocolVersion) } bytes, err := apiForVersion.Encode(a) if err != nil { diff --git a/pkg/protocol/engine/attestation/slotattestation/storage.go b/pkg/protocol/engine/attestation/slotattestation/storage.go index b5c198e30..6f74e60c0 100644 --- a/pkg/protocol/engine/attestation/slotattestation/storage.go +++ b/pkg/protocol/engine/attestation/slotattestation/storage.go @@ -53,7 +53,7 @@ func (m *Manager) writeToDisk() error { attestations := m.determineAttestationsFromWindow(i) for _, a := range attestations { - if err := storage.Set(a.IssuerID, a); err != nil { + if err := storage.Set(a.Header.IssuerID, a); err != nil { return ierrors.Wrapf(err, "failed to set attestation %v", a) } } 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/blockdag/inmemoryblockdag/blockdag.go b/pkg/protocol/engine/blockdag/inmemoryblockdag/blockdag.go index 713b20c8c..a94828f56 100644 --- a/pkg/protocol/engine/blockdag/inmemoryblockdag/blockdag.go +++ b/pkg/protocol/engine/blockdag/inmemoryblockdag/blockdag.go @@ -48,7 +48,7 @@ func NewProvider(opts ...options.Option[BlockDAG]) module.Provider[*engine.Engin e.Events.Filter.BlockPreAllowed.Hook(func(block *model.Block) { if _, _, err := b.Attach(block); err != nil { - b.errorHandler(ierrors.Wrapf(err, "failed to attach block with %s (issuerID: %s)", block.ID(), block.ProtocolBlock().IssuerID)) + b.errorHandler(ierrors.Wrapf(err, "failed to attach block with %s (issuerID: %s)", block.ID(), block.ProtocolBlock().Header.IssuerID)) } }, event.WithWorkerPool(wp)) @@ -193,7 +193,7 @@ func (b *BlockDAG) attach(data *model.Block) (block *blocks.Block, wasAttached b func (b *BlockDAG) shouldAttach(data *model.Block) (shouldAttach bool, err error) { if b.evictionState.InRootBlockSlot(data.ID()) && !b.evictionState.IsRootBlock(data.ID()) { b.retainBlockFailure(data.ID(), apimodels.BlockFailureIsTooOld) - return false, ierrors.Errorf("block data with %s is too old (issued at: %s)", data.ID(), data.ProtocolBlock().IssuingTime) + return false, ierrors.Errorf("block data with %s is too old (issued at: %s)", data.ID(), data.ProtocolBlock().Header.IssuingTime) } storedBlock, storedBlockExists := b.blockCache.Block(data.ID()) diff --git a/pkg/protocol/engine/blocks/block.go b/pkg/protocol/engine/blocks/block.go index 33de43b2e..10df6cbb5 100644 --- a/pkg/protocol/engine/blocks/block.go +++ b/pkg/protocol/engine/blocks/block.go @@ -139,7 +139,7 @@ func NewMissingBlock(blockID iotago.BlockID) *Block { } } -func (b *Block) ProtocolBlock() *iotago.ProtocolBlock { +func (b *Block) ProtocolBlock() *iotago.Block { if b.modelBlock == nil { return nil } @@ -152,7 +152,7 @@ func (b *Block) Parents() (parents []iotago.BlockID) { } func (b *Block) StrongParents() (parents []iotago.BlockID) { - return b.modelBlock.ProtocolBlock().Block.StrongParentIDs() + return b.modelBlock.ProtocolBlock().Body.StrongParentIDs() } // ParentsWithType returns the parents of the block with their type. @@ -185,7 +185,7 @@ func (b *Block) SignedTransaction() (tx *iotago.SignedTransaction, hasTransactio return b.modelBlock.SignedTransaction() } -func (b *Block) BasicBlock() (basicBlock *iotago.BasicBlock, isBasicBlock bool) { +func (b *Block) BasicBlock() (basicBlock *iotago.BasicBlockBody, isBasicBlock bool) { if b.modelBlock == nil { return nil, false } @@ -193,7 +193,7 @@ func (b *Block) BasicBlock() (basicBlock *iotago.BasicBlock, isBasicBlock bool) return b.modelBlock.BasicBlock() } -func (b *Block) ValidationBlock() (validationBlock *iotago.ValidationBlock, isValidationBlock bool) { +func (b *Block) ValidationBlock() (validationBlock *iotago.ValidationBlockBody, isValidationBlock bool) { if b.modelBlock == nil { return nil, false } @@ -232,7 +232,7 @@ func (b *Block) IssuingTime() time.Time { return b.rootBlock.issuingTime } - return b.modelBlock.ProtocolBlock().IssuingTime + return b.modelBlock.ProtocolBlock().Header.IssuingTime } func (b *Block) SlotCommitmentID() iotago.CommitmentID { @@ -247,7 +247,7 @@ func (b *Block) SlotCommitmentID() iotago.CommitmentID { return b.rootBlock.commitmentID } - return b.modelBlock.ProtocolBlock().SlotCommitmentID + return b.modelBlock.ProtocolBlock().Header.SlotCommitmentID } // IsMissing returns a flag that indicates if the underlying Block data hasn't been stored, yet. diff --git a/pkg/protocol/engine/commitmentfilter/accountsfilter/commitmentfilter.go b/pkg/protocol/engine/commitmentfilter/accountsfilter/commitmentfilter.go index f54b7a027..32a2f8d1c 100644 --- a/pkg/protocol/engine/commitmentfilter/accountsfilter/commitmentfilter.go +++ b/pkg/protocol/engine/commitmentfilter/accountsfilter/commitmentfilter.go @@ -65,11 +65,11 @@ func (c *CommitmentFilter) ProcessPreFilteredBlock(block *blocks.Block) { func (c *CommitmentFilter) evaluateBlock(block *blocks.Block) { // check if the account exists in the specified slot. - accountData, exists, err := c.accountRetrieveFunc(block.ProtocolBlock().IssuerID, block.SlotCommitmentID().Slot()) + accountData, exists, err := c.accountRetrieveFunc(block.ProtocolBlock().Header.IssuerID, block.SlotCommitmentID().Slot()) if err != nil { c.events.BlockFiltered.Trigger(&commitmentfilter.BlockFilteredEvent{ Block: block, - Reason: ierrors.Join(iotago.ErrIssuerAccountNotFound, ierrors.Wrapf(err, "could not retrieve account information for block issuer %s", block.ProtocolBlock().IssuerID)), + Reason: ierrors.Join(iotago.ErrIssuerAccountNotFound, ierrors.Wrapf(err, "could not retrieve account information for block issuer %s", block.ProtocolBlock().Header.IssuerID)), }) return @@ -77,22 +77,22 @@ func (c *CommitmentFilter) evaluateBlock(block *blocks.Block) { if !exists { c.events.BlockFiltered.Trigger(&commitmentfilter.BlockFilteredEvent{ Block: block, - Reason: ierrors.Join(iotago.ErrIssuerAccountNotFound, ierrors.Errorf("block issuer account %s does not exist in slot commitment %s", block.ProtocolBlock().IssuerID, block.ProtocolBlock().SlotCommitmentID.Slot())), + Reason: ierrors.Join(iotago.ErrIssuerAccountNotFound, ierrors.Errorf("block issuer account %s does not exist in slot commitment %s", block.ProtocolBlock().Header.IssuerID, block.ProtocolBlock().Header.SlotCommitmentID.Slot())), }) return } // get the api for the block - blockAPI, err := c.apiProvider.APIForVersion(block.ProtocolBlock().BlockHeader.ProtocolVersion) + blockAPI, err := c.apiProvider.APIForVersion(block.ProtocolBlock().Header.ProtocolVersion) if err != nil { c.events.BlockFiltered.Trigger(&commitmentfilter.BlockFilteredEvent{ Block: block, - Reason: ierrors.Join(iotago.ErrBlockVersionInvalid, ierrors.Wrapf(err, "could not retrieve API for block version %d", block.ProtocolBlock().BlockHeader.ProtocolVersion)), + Reason: ierrors.Join(iotago.ErrBlockVersionInvalid, ierrors.Wrapf(err, "could not retrieve API for block version %d", block.ProtocolBlock().Header.ProtocolVersion)), }) } // check that the block burns sufficient Mana, use slot index of the commitment - rmcSlot := block.ProtocolBlock().SlotCommitmentID.Slot() + rmcSlot := block.ProtocolBlock().Header.SlotCommitmentID.Slot() rmc, err := c.rmcRetrieveFunc(rmcSlot) if err != nil { @@ -114,7 +114,7 @@ func (c *CommitmentFilter) evaluateBlock(block *blocks.Block) { if basicBlock.MaxBurnedMana < manaCost { c.events.BlockFiltered.Trigger(&commitmentfilter.BlockFilteredEvent{ Block: block, - Reason: ierrors.Join(iotago.ErrBurnedInsufficientMana, ierrors.Errorf("block issuer account %s burned insufficient Mana, required %d, burned %d", block.ProtocolBlock().IssuerID, manaCost, basicBlock.MaxBurnedMana)), + Reason: ierrors.Join(iotago.ErrBurnedInsufficientMana, ierrors.Errorf("block issuer account %s burned insufficient Mana, required %d, burned %d", block.ProtocolBlock().Header.IssuerID, manaCost, basicBlock.MaxBurnedMana)), }) return @@ -125,17 +125,17 @@ func (c *CommitmentFilter) evaluateBlock(block *blocks.Block) { if accountData.Credits.Value < 0 { c.events.BlockFiltered.Trigger(&commitmentfilter.BlockFilteredEvent{ Block: block, - Reason: ierrors.Wrapf(iotago.ErrNegativeBIC, "block issuer account %s is locked due to negative BIC", block.ProtocolBlock().IssuerID), + Reason: ierrors.Wrapf(iotago.ErrNegativeBIC, "block issuer account %s is locked due to negative BIC", block.ProtocolBlock().Header.IssuerID), }) return } // Check that the account is not expired - if accountData.ExpirySlot < block.ProtocolBlock().SlotCommitmentID.Slot() { + if accountData.ExpirySlot < block.ProtocolBlock().Header.SlotCommitmentID.Slot() { c.events.BlockFiltered.Trigger(&commitmentfilter.BlockFilteredEvent{ Block: block, - Reason: ierrors.Wrapf(iotago.ErrAccountExpired, "block issuer account %s is expired, expiry slot %d in commitment %d", block.ProtocolBlock().IssuerID, accountData.ExpirySlot, block.ProtocolBlock().SlotCommitmentID.Slot()), + Reason: ierrors.Wrapf(iotago.ErrAccountExpired, "block issuer account %s is expired, expiry slot %d in commitment %d", block.ProtocolBlock().Header.IssuerID, accountData.ExpirySlot, block.ProtocolBlock().Header.SlotCommitmentID.Slot()), }) return @@ -155,7 +155,7 @@ func (c *CommitmentFilter) evaluateBlock(block *blocks.Block) { if !isBikPubKeyHash || bikPubKeyHash.PublicKeyHash != iotago.Ed25519PublicKeyHashBlockIssuerKeyFromPublicKey(signature.PublicKey[:]).PublicKeyHash { c.events.BlockFiltered.Trigger(&commitmentfilter.BlockFilteredEvent{ Block: block, - Reason: ierrors.Wrapf(iotago.ErrInvalidSignature, "block issuer account %s does not have block issuer key corresponding to public key %s in slot %d", block.ProtocolBlock().IssuerID, signature.PublicKey, block.ProtocolBlock().SlotCommitmentID.Index()), + Reason: ierrors.Wrapf(iotago.ErrInvalidSignature, "block issuer account %s does not have block issuer key corresponding to public key %s in slot %d", block.ProtocolBlock().Header.IssuerID, signature.PublicKey, block.ProtocolBlock().Header.SlotCommitmentID.Index()), }) return diff --git a/pkg/protocol/engine/commitmentfilter/accountsfilter/commitmentfilter_test.go b/pkg/protocol/engine/commitmentfilter/accountsfilter/commitmentfilter_test.go index dfb7c02a6..127c5fb1c 100644 --- a/pkg/protocol/engine/commitmentfilter/accountsfilter/commitmentfilter_test.go +++ b/pkg/protocol/engine/commitmentfilter/accountsfilter/commitmentfilter_test.go @@ -85,7 +85,7 @@ func (t *TestFramework) AddRMCData(slot iotago.SlotIndex, rmcData iotago.Mana) { // q: how to get an engine block.Block from protocol block -func (t *TestFramework) processBlock(alias string, block *iotago.ProtocolBlock) { +func (t *TestFramework) processBlock(alias string, block *iotago.Block) { modelBlock, err := model.BlockFromBlock(block) require.NoError(t.Test, err) diff --git a/pkg/protocol/engine/congestioncontrol/scheduler/drr/drrbuffer.go b/pkg/protocol/engine/congestioncontrol/scheduler/drr/drrbuffer.go index 0fa45bc4d..86dc0dac6 100644 --- a/pkg/protocol/engine/congestioncontrol/scheduler/drr/drrbuffer.go +++ b/pkg/protocol/engine/congestioncontrol/scheduler/drr/drrbuffer.go @@ -6,16 +6,12 @@ import ( "time" "github.com/iotaledger/hive.go/ds/shrinkingmap" - "github.com/iotaledger/hive.go/ierrors" "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/iota-core/pkg/protocol/engine/blocks" iotago "github.com/iotaledger/iota.go/v4" ) -// ErrInsufficientMana is returned when the mana is insufficient. -var ErrInsufficientMana = ierrors.New("insufficient issuer's mana to schedule the block") - // region BufferQueue ///////////////////////////////////////////////////////////////////////////////////////////// // BufferQueue represents a buffer of IssuerQueue. @@ -65,6 +61,26 @@ func (b *BufferQueue) IssuerQueue(issuerID iotago.AccountID) *IssuerQueue { return issuerQueue } +// IssuerQueueWork returns the total WorkScore of block in the queue for the corresponding issuer. +func (b *BufferQueue) IssuerQueueWork(issuerID iotago.AccountID) iotago.WorkScore { + issuerQueue := b.IssuerQueue(issuerID) + if issuerQueue == nil { + return 0 + } + + return issuerQueue.Work() +} + +// IssuerQueueSize returns the number of blocks in the queue for the corresponding issuer. +func (b *BufferQueue) IssuerQueueBlockCount(issuerID iotago.AccountID) int { + issuerQueue := b.IssuerQueue(issuerID) + if issuerQueue == nil { + return 0 + } + + return issuerQueue.Size() +} + func (b *BufferQueue) CreateIssuerQueue(issuerID iotago.AccountID) *IssuerQueue { issuerQueue := NewIssuerQueue(issuerID) b.activeIssuers.Set(issuerID, b.ringInsert(issuerQueue)) @@ -72,24 +88,56 @@ func (b *BufferQueue) CreateIssuerQueue(issuerID iotago.AccountID) *IssuerQueue return issuerQueue } -func (b *BufferQueue) GetIssuerQueue(issuerID iotago.AccountID) (*IssuerQueue, error) { +func (b *BufferQueue) GetOrCreateIssuerQueue(issuerID iotago.AccountID) *IssuerQueue { element, issuerActive := b.activeIssuers.Get(issuerID) if !issuerActive { - return nil, ierrors.New("issuer queue does not exist") + // create new issuer queue + return b.CreateIssuerQueue(issuerID) } issuerQueue, isIQ := element.Value.(*IssuerQueue) if !isIQ { - return nil, ierrors.New("buffer contains elements that are not issuer queues") + panic("buffer contains elements that are not issuer queues") } - // issuer queue exists - return issuerQueue, nil + return issuerQueue +} + +// RemoveIssuerQueue removes all blocks (submitted and ready) for the given issuer and deletes the issuer queue. +func (b *BufferQueue) RemoveIssuerQueue(issuerID iotago.AccountID) { + element, ok := b.activeIssuers.Get(issuerID) + if !ok { + return + } + issuerQueue, isIQ := element.Value.(*IssuerQueue) + if !isIQ { + panic("buffer contains elements that are not issuer queues") + } + b.size -= issuerQueue.Size() + + b.ringRemove(element) + b.activeIssuers.Delete(issuerID) +} + +// RemoveIssuerQueueIfEmpty removes all blocks (submitted and ready) for the given issuer and deletes the issuer queue if it is empty. +func (b *BufferQueue) RemoveIssuerQueueIfEmpty(issuerID iotago.AccountID) { + element, ok := b.activeIssuers.Get(issuerID) + if !ok { + return + } + issuerQueue, isIQ := element.Value.(*IssuerQueue) + if !isIQ { + panic("buffer contains elements that are not issuer queues") + } + + if issuerQueue.Size() == 0 { + b.ringRemove(element) + b.activeIssuers.Delete(issuerID) + } } // Submit submits a block. Return blocks dropped from the scheduler to make room for the submitted block. // The submitted block can also be returned as dropped if the issuer does not have enough mana. func (b *BufferQueue) Submit(blk *blocks.Block, issuerQueue *IssuerQueue, quantumFunc func(iotago.AccountID) Deficit, maxBuffer int) ([]*blocks.Block, bool) { - // first we submit the block, and if it turns out that the issuer doesn't have enough bandwidth to submit, it will be removed by dropTail if !issuerQueue.Submit(blk) { return nil, false @@ -105,49 +153,10 @@ func (b *BufferQueue) Submit(blk *blocks.Block, issuerQueue *IssuerQueue, quantu return nil, true } -func (b *BufferQueue) dropTail(quantumFunc func(iotago.AccountID) Deficit, maxBuffer int) (droppedBlocks []*blocks.Block) { - start := b.Current() - ringStart := b.ring - // remove as many blocks as necessary to stay within max buffer size - for b.Size() > maxBuffer { - // TODO: extract to util func - // find longest mana-scaled queue - maxScale := math.Inf(-1) - var maxIssuerID iotago.AccountID - for q := start; ; { - if issuerQuantum := quantumFunc(q.IssuerID()); issuerQuantum > 0 { - if scale := float64(q.Work()) / float64(issuerQuantum); scale > maxScale { - maxScale = scale - maxIssuerID = q.IssuerID() - } - } else if q.Size() > 0 { - maxIssuerID = q.IssuerID() - // return to the start of the issuer ring and break as this is the max value we can have. - b.ring = ringStart - - break - } - q = b.Next() - if q == start { - break - } - } - - if longestQueue := b.IssuerQueue(maxIssuerID); longestQueue != nil { - if tail := longestQueue.RemoveTail(); tail != nil { - b.size-- - droppedBlocks = append(droppedBlocks, tail) - } - } - } - - return droppedBlocks -} - // Unsubmit removes a block from the submitted blocks. // If that block is already marked as ready, Unsubmit has no effect. func (b *BufferQueue) Unsubmit(block *blocks.Block) bool { - issuerID := block.ProtocolBlock().IssuerID + issuerID := block.ProtocolBlock().Header.IssuerID issuerQueue := b.IssuerQueue(issuerID) if issuerQueue == nil { @@ -165,7 +174,7 @@ func (b *BufferQueue) Unsubmit(block *blocks.Block) bool { // Ready marks a previously submitted block as ready to be scheduled. func (b *BufferQueue) Ready(block *blocks.Block) bool { - issuerQueue := b.IssuerQueue(block.ProtocolBlock().IssuerID) + issuerQueue := b.IssuerQueue(block.ProtocolBlock().Header.IssuerID) if issuerQueue == nil { return false } @@ -209,34 +218,6 @@ func (b *BufferQueue) TotalBlocksCount() (blocksCount int) { return } -// InsertIssuer creates a queue for the given issuer and adds it to the list of active issuers. -func (b *BufferQueue) InsertIssuer(issuerID iotago.AccountID) { - _, issuerActive := b.activeIssuers.Get(issuerID) - if issuerActive { - return - } - - issuerQueue := NewIssuerQueue(issuerID) - b.activeIssuers.Set(issuerID, b.ringInsert(issuerQueue)) -} - -// RemoveIssuer removes all blocks (submitted and ready) for the given issuer. -func (b *BufferQueue) RemoveIssuer(issuerID iotago.AccountID) { - element, ok := b.activeIssuers.Get(issuerID) - if !ok { - return - } - - issuerQueue, isIQ := element.Value.(*IssuerQueue) - if !isIQ { - return - } - b.size -= issuerQueue.Size() - - b.ringRemove(element) - b.activeIssuers.Delete(issuerID) -} - // Next returns the next IssuerQueue in round-robin order. func (b *BufferQueue) Next() *IssuerQueue { if b.ring != nil { @@ -297,6 +278,49 @@ func (b *BufferQueue) IssuerIDs() []iotago.AccountID { return issuerIDs } +func (b *BufferQueue) dropTail(quantumFunc func(iotago.AccountID) Deficit, maxBuffer int) (droppedBlocks []*blocks.Block) { + // remove as many blocks as necessary to stay within max buffer size + for b.Size() > maxBuffer { + // find the longest mana-scaled queue + maxIssuerID := b.longestQueueIssuerID(quantumFunc) + if longestQueue := b.IssuerQueue(maxIssuerID); longestQueue != nil { + if tail := longestQueue.RemoveTail(); tail != nil { + b.size-- + droppedBlocks = append(droppedBlocks, tail) + } + } + } + + return droppedBlocks +} + +func (b *BufferQueue) longestQueueIssuerID(quantumFunc func(iotago.AccountID) Deficit) iotago.AccountID { + start := b.Current() + ringStart := b.ring + maxScale := math.Inf(-1) + var maxIssuerID iotago.AccountID + for q := start; ; { + if issuerQuantum := quantumFunc(q.IssuerID()); issuerQuantum > 0 { + if scale := float64(q.Work()) / float64(issuerQuantum); scale > maxScale { + maxScale = scale + maxIssuerID = q.IssuerID() + } + } else if q.Size() > 0 { + // if the issuer has no quantum, then this is the max queue size + maxIssuerID = q.IssuerID() + b.ring = ringStart + + break + } + q = b.Next() + if q == start { + break + } + } + + return maxIssuerID +} + func (b *BufferQueue) ringRemove(r *ring.Ring) { n := b.ring.Next() if r == b.ring { diff --git a/pkg/protocol/engine/congestioncontrol/scheduler/drr/issuerqueue.go b/pkg/protocol/engine/congestioncontrol/scheduler/drr/issuerqueue.go index 6b26acbc5..80fc750b9 100644 --- a/pkg/protocol/engine/congestioncontrol/scheduler/drr/issuerqueue.go +++ b/pkg/protocol/engine/congestioncontrol/scheduler/drr/issuerqueue.go @@ -61,7 +61,7 @@ func (q *IssuerQueue) IssuerID() iotago.AccountID { // Submit submits a block for the queue. func (q *IssuerQueue) Submit(element *blocks.Block) bool { // this is just a debugging check, it will never happen in practice - if blkIssuerID := element.ProtocolBlock().IssuerID; q.issuerID != blkIssuerID { + if blkIssuerID := element.ProtocolBlock().Header.IssuerID; q.issuerID != blkIssuerID { panic(fmt.Sprintf("issuerqueue: queue issuer ID(%x) and issuer ID(%x) does not match.", q.issuerID, blkIssuerID)) } diff --git a/pkg/protocol/engine/congestioncontrol/scheduler/drr/scheduler.go b/pkg/protocol/engine/congestioncontrol/scheduler/drr/scheduler.go index d50622d61..29ead755a 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) } @@ -148,12 +154,12 @@ func (s *Scheduler) Start() { s.TriggerInitialized() } -// IssuerQueueSizeCount returns the number of blocks in the queue of the given issuer. +// IssuerQueueBlockCount returns the number of blocks in the queue of the given issuer. func (s *Scheduler) IssuerQueueBlockCount(issuerID iotago.AccountID) int { s.bufferMutex.RLock() defer s.bufferMutex.RUnlock() - return s.basicBuffer.IssuerQueue(issuerID).Size() + return s.basicBuffer.IssuerQueueBlockCount(issuerID) } // IssuerQueueWork returns the queue size of the given issuer in work units. @@ -161,7 +167,7 @@ func (s *Scheduler) IssuerQueueWork(issuerID iotago.AccountID) iotago.WorkScore s.bufferMutex.RLock() defer s.bufferMutex.RUnlock() - return s.basicBuffer.IssuerQueue(issuerID).Work() + return s.basicBuffer.IssuerQueueWork(issuerID) } // ValidatorQueueBlockCount returns the number of validation blocks in the validator queue of the given issuer. @@ -227,7 +233,7 @@ func (s *Scheduler) IsBlockIssuerReady(accountID iotago.AccountID, blocks ...*bl return false } - return deficit >= s.deficitFromWork(work+s.basicBuffer.IssuerQueue(accountID).Work()) + return deficit >= s.deficitFromWork(work+s.basicBuffer.IssuerQueueWork(accountID)) } func (s *Scheduler) AddBlock(block *blocks.Block) { @@ -249,18 +255,8 @@ func (s *Scheduler) enqueueBasicBlock(block *blocks.Block) { slot := s.latestCommittedSlot() - issuerID := block.ProtocolBlock().IssuerID - issuerQueue, err := s.basicBuffer.GetIssuerQueue(issuerID) - if err != nil { - // this should only ever happen if the issuer has been removed due to insufficient Mana. - // if Mana is now sufficient again, we can add the issuer again. - _, quantumErr := s.quantumFunc(issuerID, slot) - if quantumErr != nil { - s.errorHandler(ierrors.Wrapf(quantumErr, "failed to retrieve quantum for issuerID %s in slot %d when adding a block", issuerID, slot)) - } - - issuerQueue = s.createIssuer(issuerID) - } + issuerID := block.ProtocolBlock().Header.IssuerID + issuerQueue := s.getOrCreateIssuer(issuerID) droppedBlocks, submitted := s.basicBuffer.Submit( block, @@ -295,9 +291,9 @@ func (s *Scheduler) enqueueValidationBlock(block *blocks.Block) { s.bufferMutex.Lock() defer s.bufferMutex.Unlock() - _, exists := s.validatorBuffer.Get(block.ProtocolBlock().IssuerID) + _, exists := s.validatorBuffer.Get(block.ProtocolBlock().Header.IssuerID) if !exists { - s.addValidator(block.ProtocolBlock().IssuerID) + s.addValidator(block.ProtocolBlock().Header.IssuerID) } droppedBlock, submitted := s.validatorBuffer.Submit(block, int(s.apiProvider.CommittedAPI().ProtocolParameters().CongestionControlParameters().MaxValidationBufferSize)) if !submitted { @@ -442,7 +438,7 @@ func (s *Scheduler) selectBasicBlockWithoutLocking() { // increment every issuer's deficit for the required number of rounds for q := start; ; { issuerID := q.IssuerID() - if err := s.incrementDeficit(issuerID, rounds, slot); err != nil { + if _, err := s.incrementDeficit(issuerID, rounds, slot); err != nil { s.errorHandler(ierrors.Wrapf(err, "failed to increment deficit for issuerID %s in slot %d", issuerID, slot)) s.removeIssuer(issuerID, err) @@ -461,20 +457,24 @@ func (s *Scheduler) selectBasicBlockWithoutLocking() { // increment the deficit for all issuers before schedulingIssuer one more time for q := start; q != schedulingIssuer; q = s.basicBuffer.Next() { issuerID := q.IssuerID() - if err := s.incrementDeficit(issuerID, 1, slot); err != nil { + newDeficit, err := s.incrementDeficit(issuerID, 1, slot) + if err != nil { s.errorHandler(ierrors.Wrapf(err, "failed to increment deficit for issuerID %s in slot %d", issuerID, slot)) s.removeIssuer(issuerID, err) return } + + // remove empty issuer queues of issuers with max deficit. + if newDeficit == s.maxDeficit() { + s.basicBuffer.RemoveIssuerQueueIfEmpty(issuerID) + } } // remove the block from the buffer and adjust issuer's deficit block := s.basicBuffer.PopFront() - issuerID := block.ProtocolBlock().IssuerID - err := s.updateDeficit(issuerID, -s.deficitFromWork(block.WorkScore())) - - if err != nil { + issuerID := block.ProtocolBlock().Header.IssuerID + if _, err := s.updateDeficit(issuerID, -s.deficitFromWork(block.WorkScore())); err != nil { // if something goes wrong with deficit update, drop the block instead of scheduling it. block.SetDropped() s.events.BlockDropped.Trigger(block, err) @@ -507,7 +507,7 @@ func (s *Scheduler) selectIssuer(start *IssuerQueue, slot iotago.SlotIndex) (Def continue } - issuerID := block.ProtocolBlock().IssuerID + issuerID := block.ProtocolBlock().Header.IssuerID // compute how often the deficit needs to be incremented until the block can be scheduled deficit, exists := s.deficits.Get(issuerID) @@ -575,8 +575,14 @@ func (s *Scheduler) removeIssuer(issuerID iotago.AccountID, err error) { } s.deficits.Delete(issuerID) + s.basicBuffer.RemoveIssuerQueue(issuerID) +} - s.basicBuffer.RemoveIssuer(issuerID) +func (s *Scheduler) getOrCreateIssuer(accountID iotago.AccountID) *IssuerQueue { + issuerQueue := s.basicBuffer.GetOrCreateIssuerQueue(accountID) + s.deficits.GetOrCreate(accountID, func() Deficit { return 0 }) + + return issuerQueue } func (s *Scheduler) createIssuer(accountID iotago.AccountID) *IssuerQueue { @@ -586,16 +592,16 @@ func (s *Scheduler) createIssuer(accountID iotago.AccountID) *IssuerQueue { return issuerQueue } -func (s *Scheduler) updateDeficit(accountID iotago.AccountID, delta Deficit) error { +func (s *Scheduler) updateDeficit(accountID iotago.AccountID, delta Deficit) (Deficit, error) { var updateErr error - s.deficits.Compute(accountID, func(currentValue Deficit, exists bool) Deficit { + updatedDeficit := s.deficits.Compute(accountID, func(currentValue Deficit, exists bool) Deficit { if !exists { updateErr = ierrors.Errorf("could not get deficit for issuer %s", accountID) return 0 } newDeficit, err := safemath.SafeAdd(currentValue, delta) - if err != nil { - // It can only overflow. We never allow the value to go below 0, so underflow is impossible. + // It can only overflow. We never allow the value to go below 0, so underflow is impossible. + if err != nil || newDeficit >= s.maxDeficit() { return s.maxDeficit() } @@ -605,22 +611,22 @@ func (s *Scheduler) updateDeficit(accountID iotago.AccountID, delta Deficit) err return 0 } - return lo.Min(newDeficit, s.maxDeficit()) + return newDeficit }) if updateErr != nil { s.removeIssuer(accountID, updateErr) - return updateErr + return 0, updateErr } - return nil + return updatedDeficit, nil } -func (s *Scheduler) incrementDeficit(issuerID iotago.AccountID, rounds Deficit, slot iotago.SlotIndex) error { +func (s *Scheduler) incrementDeficit(issuerID iotago.AccountID, rounds Deficit, slot iotago.SlotIndex) (Deficit, error) { quantum, err := s.quantumFunc(issuerID, slot) if err != nil { - return err + return 0, ierrors.Wrap(err, "failed to retrieve quantum") } delta, err := safemath.SafeMul(quantum, rounds) @@ -671,7 +677,7 @@ func (s *Scheduler) ready(block *blocks.Block) { } func (s *Scheduler) readyValidationBlock(block *blocks.Block) { - if validatorQueue, exists := s.validatorBuffer.Get(block.ProtocolBlock().IssuerID); exists { + if validatorQueue, exists := s.validatorBuffer.Get(block.ProtocolBlock().Header.IssuerID); exists { validatorQueue.Ready(block) } } diff --git a/pkg/protocol/engine/congestioncontrol/scheduler/drr/validatorqueue.go b/pkg/protocol/engine/congestioncontrol/scheduler/drr/validatorqueue.go index 8d2fd0aaf..900262b82 100644 --- a/pkg/protocol/engine/congestioncontrol/scheduler/drr/validatorqueue.go +++ b/pkg/protocol/engine/congestioncontrol/scheduler/drr/validatorqueue.go @@ -53,7 +53,7 @@ func (q *ValidatorQueue) AccountID() iotago.AccountID { } func (q *ValidatorQueue) Submit(block *blocks.Block, maxBuffer int) (*blocks.Block, bool) { - if blkAccountID := block.ProtocolBlock().IssuerID; q.accountID != blkAccountID { + if blkAccountID := block.ProtocolBlock().Header.IssuerID; q.accountID != blkAccountID { panic(fmt.Sprintf("issuerqueue: queue issuer ID(%x) and issuer ID(%x) does not match.", q.accountID, blkAccountID)) } @@ -202,7 +202,7 @@ func (b *ValidatorBuffer) Set(accountID iotago.AccountID, validatorQueue *Valida } func (b *ValidatorBuffer) Submit(block *blocks.Block, maxBuffer int) (*blocks.Block, bool) { - validatorQueue, exists := b.buffer.Get(block.ProtocolBlock().IssuerID) + validatorQueue, exists := b.buffer.Get(block.ProtocolBlock().Header.IssuerID) if !exists { return nil, false } diff --git a/pkg/protocol/engine/consensus/blockgadget/testframework_test.go b/pkg/protocol/engine/consensus/blockgadget/testframework_test.go index dacce645a..211c540c0 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{}, 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,11 +56,14 @@ 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 - genesisBlock := blocks.NewRootBlock(iotago.EmptyBlockID, iotago.NewEmptyCommitment(tpkg.TestAPI.Version()).MustID(), time.Unix(tpkg.TestAPI.ProtocolParameters().TimeProvider().GenesisUnixTime(), 0)) + genesisBlock := blocks.NewRootBlock(iotago.EmptyBlockID, iotago.NewEmptyCommitment(tpkg.TestAPI.Version()).MustID(), time.Unix(tpkg.TestAPI.TimeProvider().GenesisUnixTime(), 0)) t.blocks.Set("Genesis", genesisBlock) genesisBlock.ID().RegisterAlias("Genesis") evictionState.AddRootBlock(genesisBlock.ID(), genesisBlock.SlotCommitmentID()) diff --git a/pkg/protocol/engine/consensus/blockgadget/thresholdblockgadget/gadget.go b/pkg/protocol/engine/consensus/blockgadget/thresholdblockgadget/gadget.go index 1fa87b568..e55a639b9 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, @@ -101,8 +104,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().Header.IssuerID) } func anyChildInSet(block *blocks.Block, set ds.Set[iotago.BlockID]) bool { diff --git a/pkg/protocol/engine/consensus/slotgadget/totalweightslotgadget/gadget.go b/pkg/protocol/engine/consensus/slotgadget/totalweightslotgadget/gadget.go index e6a518023..b265f7969 100644 --- a/pkg/protocol/engine/consensus/slotgadget/totalweightslotgadget/gadget.go +++ b/pkg/protocol/engine/consensus/slotgadget/totalweightslotgadget/gadget.go @@ -104,7 +104,7 @@ func (g *Gadget) trackVotes(block *blocks.Block) { return slottracker.NewSlotTracker() }) - prevLatestSlot, latestSlot, updated := tracker.TrackVotes(block.SlotCommitmentID().Slot(), block.ProtocolBlock().IssuerID, g.lastFinalizedSlot) + prevLatestSlot, latestSlot, updated := tracker.TrackVotes(block.SlotCommitmentID().Slot(), block.ProtocolBlock().Header.IssuerID, g.lastFinalizedSlot) if !updated { return nil } diff --git a/pkg/protocol/engine/engine.go b/pkg/protocol/engine/engine.go index a67aa469f..1db4bb5d9 100644 --- a/pkg/protocol/engine/engine.go +++ b/pkg/protocol/engine/engine.go @@ -422,7 +422,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) @@ -458,7 +458,7 @@ func (e *Engine) setupEvictionState() { e.errorHandler(ierrors.Errorf("cannot store root block (%s) because it is missing", parent.ID)) return } - e.EvictionState.AddRootBlock(parentBlock.ID(), parentBlock.ProtocolBlock().SlotCommitmentID) + e.EvictionState.AddRootBlock(parentBlock.ID(), parentBlock.ProtocolBlock().Header.SlotCommitmentID) } }) }, event.WithWorkerPool(wp)) diff --git a/pkg/protocol/engine/filter/blockfilter/filter.go b/pkg/protocol/engine/filter/blockfilter/filter.go index 351f92105..8c0098469 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.Constructed.OnTrigger(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() }) @@ -68,10 +68,10 @@ func New(apiProvider iotago.APIProvider, opts ...options.Option[Filter]) *Filter func (f *Filter) ProcessReceivedBlock(block *model.Block, source peer.ID) { // Verify the block's version corresponds to the protocol version for the slot. apiForSlot := f.apiProvider.APIForSlot(block.ID().Slot()) - if apiForSlot.Version() != block.ProtocolBlock().ProtocolVersion { + if apiForSlot.Version() != block.ProtocolBlock().Header.ProtocolVersion { f.events.BlockPreFiltered.Trigger(&filter.BlockPreFilteredEvent{ Block: block, - Reason: ierrors.Wrapf(ErrInvalidBlockVersion, "invalid protocol version %d (expected %d) for epoch %d", block.ProtocolBlock().ProtocolVersion, apiForSlot.Version(), apiForSlot.TimeProvider().EpochFromSlot(block.ID().Slot())), + Reason: ierrors.Wrapf(ErrInvalidBlockVersion, "invalid protocol version %d (expected %d) for epoch %d", block.ProtocolBlock().Header.ProtocolVersion, apiForSlot.Version(), apiForSlot.TimeProvider().EpochFromSlot(block.ID().Slot())), Source: source, }) @@ -79,7 +79,7 @@ func (f *Filter) ProcessReceivedBlock(block *model.Block, source peer.ID) { } // Verify the timestamp is not too far in the future. - timeDelta := time.Since(block.ProtocolBlock().IssuingTime) + timeDelta := time.Since(block.ProtocolBlock().Header.IssuingTime) if timeDelta < -f.optsMaxAllowedWallClockDrift { f.events.BlockPreFiltered.Trigger(&filter.BlockPreFilteredEvent{ Block: block, @@ -91,11 +91,22 @@ 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) { + blockSlot := block.ProtocolBlock().API.TimeProvider().SlotFromTime(block.ProtocolBlock().Header.IssuingTime) + committee, exists := f.committeeFunc(blockSlot) + if !exists { 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), + Reason: ierrors.Wrapf(ErrValidatorNotInCommittee, "no committee for slot %d", blockSlot), + Source: source, + }) + + return + } + + if !committee.HasAccount(block.ProtocolBlock().Header.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().Header.IssuerID, blockSlot), Source: source, }) diff --git a/pkg/protocol/engine/filter/blockfilter/filter_test.go b/pkg/protocol/engine/filter/blockfilter/filter_test.go index 1e7347fc7..6f1b4bdc4 100644 --- a/pkg/protocol/engine/filter/blockfilter/filter_test.go +++ b/pkg/protocol/engine/filter/blockfilter/filter_test.go @@ -44,7 +44,7 @@ func NewTestFramework(t *testing.T, apiProvider iotago.APIProvider, optsFilter . return tf } -func (t *TestFramework) processBlock(alias string, block *iotago.ProtocolBlock) error { +func (t *TestFramework) processBlock(alias string, block *iotago.Block) error { modelBlock, err := model.BlockFromBlock(block, serix.WithValidation()) if err != nil { return err @@ -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 584ec3f42..dcbc2509a 100644 --- a/pkg/protocol/engine/ledger/ledger/ledger.go +++ b/pkg/protocol/engine/ledger/ledger/ledger.go @@ -661,7 +661,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 @@ -752,9 +752,14 @@ func (l *Ledger) resolveState(stateRef mempool.StateReference) *promise.Promise[ } func (l *Ledger) blockPreAccepted(block *blocks.Block) { - voteRank := ledger.NewBlockVoteRank(block.ID(), block.ProtocolBlock().IssuingTime) + voteRank := ledger.NewBlockVoteRank(block.ID(), block.ProtocolBlock().Header.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().Header.IssuerID) if !exists { return } diff --git a/pkg/protocol/engine/notarization/slotnotarization/manager.go b/pkg/protocol/engine/notarization/slotnotarization/manager.go index 29993ce3e..8a0a65461 100644 --- a/pkg/protocol/engine/notarization/slotnotarization/manager.go +++ b/pkg/protocol/engine/notarization/slotnotarization/manager.go @@ -160,9 +160,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/syncmanager/syncmanager.go b/pkg/protocol/engine/syncmanager/syncmanager.go index dfd2b0a2d..82b1b5ff8 100644 --- a/pkg/protocol/engine/syncmanager/syncmanager.go +++ b/pkg/protocol/engine/syncmanager/syncmanager.go @@ -41,6 +41,7 @@ type SyncManager interface { } type SyncStatus struct { + NodeBootstrapped bool NodeSynced bool LastAcceptedBlockSlot iotago.SlotIndex LastConfirmedBlockSlot iotago.SlotIndex diff --git a/pkg/protocol/engine/syncmanager/trivialsyncmanager/syncmanager.go b/pkg/protocol/engine/syncmanager/trivialsyncmanager/syncmanager.go index a3b8649bc..c15a29b89 100644 --- a/pkg/protocol/engine/syncmanager/trivialsyncmanager/syncmanager.go +++ b/pkg/protocol/engine/syncmanager/trivialsyncmanager/syncmanager.go @@ -139,6 +139,7 @@ func (s *SyncManager) SyncStatus() *syncmanager.SyncStatus { return &syncmanager.SyncStatus{ NodeSynced: s.IsNodeSynced(), + NodeBootstrapped: s.IsBootstrapped(), LastAcceptedBlockSlot: s.lastAcceptedBlockSlot, LastConfirmedBlockSlot: s.lastConfirmedBlockSlot, LatestCommitment: s.latestCommitment, diff --git a/pkg/protocol/engine/upgrade/signalingupgradeorchestrator/orchestrator.go b/pkg/protocol/engine/upgrade/signalingupgradeorchestrator/orchestrator.go index e802d1a20..cd771ec0e 100644 --- a/pkg/protocol/engine/upgrade/signalingupgradeorchestrator/orchestrator.go +++ b/pkg/protocol/engine/upgrade/signalingupgradeorchestrator/orchestrator.go @@ -151,8 +151,12 @@ func (o *Orchestrator) TrackValidationBlock(block *blocks.Block) { } newSignaledBlock := model.NewSignaledBlock(block.ID(), block.ProtocolBlock(), validationBlock) - committee := o.seatManager.Committee(block.ID().Slot()) - seat, exists := committee.GetSeat(block.ProtocolBlock().IssuerID) + committee, exists := o.seatManager.CommitteeInSlot(block.ID().Slot()) + if !exists { + return + } + + seat, exists := committee.GetSeat(block.ProtocolBlock().Header.IssuerID) if !exists { return } diff --git a/pkg/protocol/engine/utxoledger/iteration_test.go b/pkg/protocol/engine/utxoledger/iteration_test.go index 41a8b7050..968ed62b4 100644 --- a/pkg/protocol/engine/utxoledger/iteration_test.go +++ b/pkg/protocol/engine/utxoledger/iteration_test.go @@ -21,6 +21,7 @@ func TestUTXOComputeBalance(t *testing.T) { initialOutput := tpkg.RandLedgerStateOutputOnAddressWithAmount(iotago.OutputBasic, utils.RandAddress(iotago.AddressEd25519), 2_134_656_365) require.NoError(t, manager.AddGenesisUnspentOutput(initialOutput)) require.NoError(t, manager.AddGenesisUnspentOutput(tpkg.RandLedgerStateOutputOnAddressWithAmount(iotago.OutputAccount, utils.RandAddress(iotago.AddressAccount), 56_549_524))) + require.NoError(t, manager.AddGenesisUnspentOutput(tpkg.RandLedgerStateOutputOnAddressWithAmount(iotago.OutputAnchor, utils.RandAddress(iotago.AddressAccount), 56_549_524))) require.NoError(t, manager.AddGenesisUnspentOutput(tpkg.RandLedgerStateOutputOnAddressWithAmount(iotago.OutputFoundry, utils.RandAddress(iotago.AddressAccount), 25_548_858))) require.NoError(t, manager.AddGenesisUnspentOutput(tpkg.RandLedgerStateOutputOnAddressWithAmount(iotago.OutputNFT, utils.RandAddress(iotago.AddressEd25519), 545_699_656))) require.NoError(t, manager.AddGenesisUnspentOutput(tpkg.RandLedgerStateOutputOnAddressWithAmount(iotago.OutputBasic, utils.RandAddress(iotago.AddressAccount), 626_659_696))) @@ -43,12 +44,12 @@ func TestUTXOComputeBalance(t *testing.T) { unspent, err := manager.UnspentOutputs() require.NoError(t, err) - require.Equal(t, 5, len(unspent)) + require.Equal(t, 6, len(unspent)) balance, count, err := manager.ComputeLedgerBalance() require.NoError(t, err) - require.Equal(t, 5, count) - require.Equal(t, iotago.BaseToken(2_134_656_365+56_549_524+25_548_858+545_699_656+626_659_696), balance) + require.Equal(t, 6, count) + require.Equal(t, iotago.BaseToken(2_134_656_365+56_549_524+56_549_524+25_548_858+545_699_656+626_659_696), balance) } func TestUTXOIteration(t *testing.T) { @@ -67,6 +68,7 @@ func TestUTXOIteration(t *testing.T) { tpkg.RandLedgerStateOutputOnAddress(iotago.OutputNFT, utils.RandAddress(iotago.AddressNFT)), tpkg.RandLedgerStateOutputOnAddress(iotago.OutputNFT, utils.RandAddress(iotago.AddressAccount)), tpkg.RandLedgerStateOutputOnAddress(iotago.OutputAccount, utils.RandAddress(iotago.AddressEd25519)), + tpkg.RandLedgerStateOutputOnAddress(iotago.OutputAnchor, utils.RandAddress(iotago.AddressEd25519)), tpkg.RandLedgerStateOutputOnAddress(iotago.OutputFoundry, utils.RandAddress(iotago.AddressAccount)), tpkg.RandLedgerStateOutputOnAddress(iotago.OutputFoundry, utils.RandAddress(iotago.AddressAccount)), tpkg.RandLedgerStateOutputOnAddress(iotago.OutputFoundry, utils.RandAddress(iotago.AddressAccount)), diff --git a/pkg/protocol/engine/utxoledger/manager_test.go b/pkg/protocol/engine/utxoledger/manager_test.go index e9f38fe39..fe14c840e 100644 --- a/pkg/protocol/engine/utxoledger/manager_test.go +++ b/pkg/protocol/engine/utxoledger/manager_test.go @@ -23,6 +23,7 @@ func TestConfirmationApplyAndRollbackToEmptyLedger(t *testing.T) { tpkg.RandLedgerStateOutputWithType(iotago.OutputNFT), // spent tpkg.RandLedgerStateOutputWithType(iotago.OutputBasic), // spent tpkg.RandLedgerStateOutputWithType(iotago.OutputAccount), + tpkg.RandLedgerStateOutputWithType(iotago.OutputAnchor), tpkg.RandLedgerStateOutputWithType(iotago.OutputNFT), tpkg.RandLedgerStateOutputWithType(iotago.OutputFoundry), } @@ -45,7 +46,7 @@ func TestConfirmationApplyAndRollbackToEmptyLedger(t *testing.T) { return true })) - require.Equal(t, 7, outputCount) + require.Equal(t, 8, outputCount) var unspentCount int require.NoError(t, manager.ForEachUnspentOutput(func(_ *utxoledger.Output) bool { @@ -53,7 +54,7 @@ func TestConfirmationApplyAndRollbackToEmptyLedger(t *testing.T) { return true })) - require.Equal(t, 5, unspentCount) + require.Equal(t, 6, unspentCount) var spentCount int require.NoError(t, manager.ForEachSpentOutput(func(_ *utxoledger.Spent) bool { @@ -113,6 +114,7 @@ func TestConfirmationApplyAndRollbackToPreviousLedger(t *testing.T) { tpkg.RandLedgerStateOutputWithType(iotago.OutputFoundry), tpkg.RandLedgerStateOutputWithType(iotago.OutputBasic), // spent tpkg.RandLedgerStateOutputWithType(iotago.OutputAccount), + tpkg.RandLedgerStateOutputWithType(iotago.OutputAnchor), } index := iotago.SlotIndex(49) @@ -161,7 +163,7 @@ func TestConfirmationApplyAndRollbackToPreviousLedger(t *testing.T) { return true })) require.Empty(t, outputByOutputID) - require.Equal(t, 7, outputCount) + require.Equal(t, 8, outputCount) var unspentCount int require.NoError(t, manager.ForEachUnspentOutput(func(output *utxoledger.Output) bool { @@ -172,7 +174,7 @@ func TestConfirmationApplyAndRollbackToPreviousLedger(t *testing.T) { return true })) - require.Equal(t, 4, unspentCount) + require.Equal(t, 5, unspentCount) require.Empty(t, unspentByOutputID) var spentCount int diff --git a/pkg/protocol/engine/utxoledger/output_test.go b/pkg/protocol/engine/utxoledger/output_test.go index 7cfc6548e..66f882265 100644 --- a/pkg/protocol/engine/utxoledger/output_test.go +++ b/pkg/protocol/engine/utxoledger/output_test.go @@ -290,8 +290,7 @@ func TestAccountOutputSerialization(t *testing.T) { txCreationSlot := utils.RandSlotIndex() blockID := utils.RandBlockID() aliasID := utils.RandAccountID() - stateController := utils.RandAccountID() - governor := utils.RandAddress(iotago.AddressEd25519).(*iotago.Ed25519Address) + address := utils.RandAccountID().ToAddress() issuer := utils.RandNFTID() sender := utils.RandAccountID() amount := iotago_tpkg.RandBaseToken(iotago.MaxBaseToken) @@ -301,6 +300,49 @@ func TestAccountOutputSerialization(t *testing.T) { Amount: amount, AccountID: aliasID, Conditions: iotago.AccountOutputUnlockConditions{ + &iotago.AddressUnlockCondition{ + Address: address, + }, + }, + Features: iotago.AccountOutputFeatures{ + &iotago.SenderFeature{ + Address: sender.ToAddress(), + }, + }, + ImmutableFeatures: iotago.AccountOutputImmFeatures{ + &iotago.IssuerFeature{ + Address: issuer.ToAddress(), + }, + }, + } + + outputProof, err := iotago.NewOutputIDProof(iotago_tpkg.TestAPI, txCommitment, txCreationSlot, iotago.TxEssenceOutputs{iotaOutput}, 0) + require.NoError(t, err) + + output := CreateOutputAndAssertSerialization(t, blockID, index, iotaOutput, outputProof) + spent := CreateSpentAndAssertSerialization(t, output) + outputID := output.OutputID() + + require.ElementsMatch(t, byteutils.ConcatBytes([]byte{utxoledger.StoreKeyPrefixOutputUnspent}, outputID[:]), output.UnspentLookupKey()) + AssertOutputUnspentAndSpentTransitions(t, output, spent) +} + +func TestAnchorOutputSerialization(t *testing.T) { + txCommitment := iotago_tpkg.Rand32ByteArray() + txCreationSlot := utils.RandSlotIndex() + blockID := utils.RandBlockID() + aliasID := utils.RandAnchorID() + stateController := utils.RandAnchorID() + governor := utils.RandAddress(iotago.AddressEd25519).(*iotago.Ed25519Address) + issuer := utils.RandNFTID() + sender := utils.RandAnchorID() + amount := iotago_tpkg.RandBaseToken(iotago.MaxBaseToken) + index := utils.RandSlotIndex() + + iotaOutput := &iotago.AnchorOutput{ + Amount: amount, + AnchorID: aliasID, + Conditions: iotago.AnchorOutputUnlockConditions{ &iotago.StateControllerAddressUnlockCondition{ Address: stateController.ToAddress(), }, @@ -309,12 +351,12 @@ func TestAccountOutputSerialization(t *testing.T) { }, }, StateMetadata: make([]byte, 0), - Features: iotago.AccountOutputFeatures{ + Features: iotago.AnchorOutputFeatures{ &iotago.SenderFeature{ Address: sender.ToAddress(), }, }, - ImmutableFeatures: iotago.AccountOutputImmFeatures{ + ImmutableFeatures: iotago.AnchorOutputImmFeatures{ &iotago.IssuerFeature{ Address: issuer.ToAddress(), }, diff --git a/pkg/protocol/network_clock.go b/pkg/protocol/network_clock.go index 33a80cb8a..19a4b566d 100644 --- a/pkg/protocol/network_clock.go +++ b/pkg/protocol/network_clock.go @@ -31,7 +31,7 @@ func NewNetworkClock(protocol *Protocol) *NetworkClock { protocol.Constructed.OnTrigger(func() { unsubscribe := lo.Batch( protocol.Network.OnBlockReceived(func(block *model.Block, src peer.ID) { - n.Set(block.ProtocolBlock().IssuingTime) + n.Set(block.ProtocolBlock().Header.IssuingTime) }), protocol.OnChainCreated(func(chain *Chain) { diff --git a/pkg/protocol/protocol_attestations.go b/pkg/protocol/protocol_attestations.go index 7c52e908e..0b39cd399 100644 --- a/pkg/protocol/protocol_attestations.go +++ b/pkg/protocol/protocol_attestations.go @@ -43,7 +43,8 @@ func NewAttestationsProtocol(protocol *Protocol) *AttestationsProtocol { if requestAttestations { if commitmentBeforeForkingPoint := forkingPoint.Parent.Get(); commitmentBeforeForkingPoint != nil { a.commitmentVerifiers.GetOrCreate(forkingPoint.ID(), func() *CommitmentVerifier { - return NewCommitmentVerifier(chain.Engine.Get(), commitmentBeforeForkingPoint.Commitment) + // TODO: HANDLE ERROR GRACEFULLY + return lo.PanicOnErr(NewCommitmentVerifier(chain.Engine.Get(), commitmentBeforeForkingPoint.Commitment)) }) } } else { diff --git a/pkg/protocol/protocol_blocks.go b/pkg/protocol/protocol_blocks.go index c22372363..69bedc620 100644 --- a/pkg/protocol/protocol_blocks.go +++ b/pkg/protocol/protocol_blocks.go @@ -87,29 +87,29 @@ func (b *BlocksProtocol) SendResponse(block *model.Block) { func (b *BlocksProtocol) ProcessResponse(block *model.Block, from peer.ID) { b.workerPool.Submit(func() { - commitmentRequest := b.protocol.requestCommitment(block.ProtocolBlock().SlotCommitmentID, true) + commitmentRequest := b.protocol.requestCommitment(block.ProtocolBlock().Header.SlotCommitmentID, true) if commitmentRequest.WasRejected() { - b.LogError("dropped block referencing unsolidifiable commitment", "commitmentID", block.ProtocolBlock().SlotCommitmentID, "blockID", block.ID(), "err", commitmentRequest.Err()) + b.LogError("dropped block referencing unsolidifiable commitment", "commitmentID", block.ProtocolBlock().Header.SlotCommitmentID, "blockID", block.ID(), "err", commitmentRequest.Err()) return } commitment := commitmentRequest.Result() if commitment == nil { - if !b.droppedBlocksBuffer.Add(block.ProtocolBlock().SlotCommitmentID, types.NewTuple(block, from)) { - b.LogError("failed to add dropped block referencing unsolid commitment to dropped blocks buffer", "commitmentID", block.ProtocolBlock().SlotCommitmentID, "blockID", block.ID()) + if !b.droppedBlocksBuffer.Add(block.ProtocolBlock().Header.SlotCommitmentID, types.NewTuple(block, from)) { + b.LogError("failed to add dropped block referencing unsolid commitment to dropped blocks buffer", "commitmentID", block.ProtocolBlock().Header.SlotCommitmentID, "blockID", block.ID()) } else { - b.LogTrace("dropped block referencing unsolid commitment added to dropped blocks buffer", "commitmentID", block.ProtocolBlock().SlotCommitmentID, "blockID", block.ID()) + b.LogTrace("dropped block referencing unsolid commitment added to dropped blocks buffer", "commitmentID", block.ProtocolBlock().Header.SlotCommitmentID, "blockID", block.ID()) } return } if !commitment.Chain.Get().DispatchBlock(block, from) { - if !b.droppedBlocksBuffer.Add(block.ProtocolBlock().SlotCommitmentID, types.NewTuple(block, from)) { - b.LogError("afailed to add dropped block referencing unsolid commitment to dropped blocks buffer", "commitmentID", block.ProtocolBlock().SlotCommitmentID, "blockID", block.ID()) + if !b.droppedBlocksBuffer.Add(block.ProtocolBlock().Header.SlotCommitmentID, types.NewTuple(block, from)) { + b.LogError("afailed to add dropped block referencing unsolid commitment to dropped blocks buffer", "commitmentID", block.ProtocolBlock().Header.SlotCommitmentID, "blockID", block.ID()) } else { - b.LogTrace("adropped block referencing unsolid commitment added to dropped blocks buffer", "commitmentID", block.ProtocolBlock().SlotCommitmentID, "blockID", block.ID()) + b.LogTrace("adropped block referencing unsolid commitment added to dropped blocks buffer", "commitmentID", block.ProtocolBlock().Header.SlotCommitmentID, "blockID", block.ID()) } return 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..9a58b6680 --- /dev/null +++ b/pkg/protocol/sybilprotection/activitytracker/activitytrackerv1/activitytracker.go @@ -0,0 +1,85 @@ +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) + + // Only trigger the event if online committee member is removed. + if 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 97633d0db..a6f8365a2 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.Constructed.OnTrigger(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().Header.IssuerID) if exists { - s.markSeatActive(seat, block.ProtocolBlock().IssuerID, block.IssuingTime()) + s.activityTracker.MarkSeatActive(seat, block.ProtocolBlock().Header.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..c6f8a49ff --- /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.Constructed.OnTrigger(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().Header.IssuerID) + if exists { + s.activityTracker.MarkSeatActive(seat, block.ProtocolBlock().Header.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..b1b4700de --- /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{}, 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{}, 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 8444c8713..0968ec425 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 1f5cb1121..f5f024a56 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) + isCommitteeMember, err := t.isCommitteeMember(block.ID().Slot(), block.ProtocolBlock().Header.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().Header.IssuerID, block.ID().Slot())) return } @@ -80,35 +76,108 @@ 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().Header.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().Header.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().Header.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) { + candidates := ds.NewSet[iotago.AccountID]() + + // Epoch 0 has no candidates as it's the genesis committee. + if epoch == 0 { + return candidates, nil + } + + // 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) + } + + 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 +185,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 } @@ -235,7 +305,7 @@ func (t *Tracker) aggregatePerformanceFactors(slotActivityVector []*model.Valida epochPerformanceFactor += uint64(slotPerformanceFactor) } - return epochPerformanceFactor >> uint64(protoParamsForEpoch.TimeProvider().SlotsPerEpochExponent()) + return epochPerformanceFactor >> uint64(protoParamsForEpoch.SlotsPerEpochExponent()) } func (t *Tracker) isCommitteeMember(slot iotago.SlotIndex, accountID iotago.AccountID) (bool, error) { @@ -248,7 +318,7 @@ func (t *Tracker) isCommitteeMember(slot iotago.SlotIndex, accountID iotago.Acco return committee.Has(accountID), nil } -func (t *Tracker) trackCommitteeMemberPerformance(validationBlock *iotago.ValidationBlock, block *blocks.Block) { +func (t *Tracker) trackCommitteeMemberPerformance(validationBlock *iotago.ValidationBlockBody, block *blocks.Block) { validatorPerformances, err := t.validatorPerformancesFunc(block.ID().Slot()) if err != nil { t.errHandler(ierrors.Errorf("failed to load performance factor for slot %s", block.ID().Slot())) @@ -256,30 +326,36 @@ func (t *Tracker) trackCommitteeMemberPerformance(validationBlock *iotago.Valida return } - validatorPerformance, err := validatorPerformances.Load(block.ProtocolBlock().IssuerID) + validatorPerformance, err := validatorPerformances.Load(block.ProtocolBlock().Header.IssuerID) if err != nil { - t.errHandler(ierrors.Errorf("failed to load performance factor for account %s", block.ProtocolBlock().IssuerID)) + t.errHandler(ierrors.Errorf("failed to load performance factor for account %s", block.ProtocolBlock().Header.IssuerID)) + + return } + // key not found if validatorPerformance == nil { validatorPerformance = model.NewValidatorPerformance() } - // set bit at subslotIndex to 1 to indicate activity in that subslot - validatorPerformance.SlotActivityVector = validatorPerformance.SlotActivityVector | (1 << t.subslotIndex(block.ID().Slot(), block.ProtocolBlock().IssuingTime)) + // 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().Header.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)) + + if err = validatorPerformances.Store(block.ProtocolBlock().Header.IssuerID, validatorPerformance); err != nil { + t.errHandler(ierrors.Errorf("failed to store performance factor for account %s", block.ProtocolBlock().Header.IssuerID)) } } diff --git a/pkg/protocol/sybilprotection/sybilprotectionv1/performance/testsuite_test.go b/pkg/protocol/sybilprotection/sybilprotectionv1/performance/testsuite_test.go index 08cbc3766..1b948cd13 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{}, mapdb.NewMapDB(), 0) poolStatsStore := epochstore.NewStore(kvstore.Realm{}, mapdb.NewMapDB(), 0, (*model.PoolsStats).Bytes, model.PoolsStatsFromBytes) committeeStore := epochstore.NewStore(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{}, 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) @@ -130,7 +141,7 @@ func (t *TestSuite) ApplyEpochActions(epoch iotago.EpochIndex, actions map[strin t.poolRewards[epoch] = make(map[string]*model.PoolRewards) for alias, action := range actions { - epochPerformanceFactor := action.SlotPerformance * action.ActiveSlotsCount >> t.api.ProtocolParameters().TimeProvider().SlotsPerEpochExponent() + epochPerformanceFactor := action.SlotPerformance * action.ActiveSlotsCount >> t.api.ProtocolParameters().SlotsPerEpochExponent() poolRewards := t.calculatePoolReward(epoch, totalValidatorsStake, totalStake, action.PoolStake, action.ValidatorStake, epochPerformanceFactor) t.poolRewards[epoch][alias] = &model.PoolRewards{ PoolStake: action.PoolStake, @@ -278,14 +289,14 @@ func (t *TestSuite) applyPerformanceFactor(accountID iotago.AccountID, epoch iot for i := uint64(0); i < validationBlocksSentPerSlot; i++ { valBlock := tpkg.RandValidationBlock(t.api) - block := tpkg.RandProtocolBlock(valBlock, t.api, 10) - block.IssuerID = accountID + block := tpkg.RandBlock(valBlock, t.api, 10) + block.Header.IssuerID = accountID subslotIndex := i // issued more than one block in the same slot to reduce performance factor if i >= slotPerformanceFactor { subslotIndex = 0 } - block.IssuingTime = t.api.TimeProvider().SlotStartTime(slot).Add(time.Duration(subslotIndex)*subslotDur + 1*time.Nanosecond) + block.Header.IssuingTime = t.api.TimeProvider().SlotStartTime(slot).Add(time.Duration(subslotIndex)*subslotDur + 1*time.Nanosecond) modelBlock, err := model.BlockFromBlock(block) t.Instance.TrackValidationBlock(blocks.NewBlock(modelBlock)) require.NoError(t.T, err) @@ -346,7 +357,7 @@ func (e *EpochActions) validate(t *testing.T, api iotago.API) { } require.Equal(t, e.PoolStake, delegatorsTotal+e.ValidatorStake, "pool stake must be equal to the sum of delegators stakes plus validator") - sumOfSlots := 1 << api.ProtocolParameters().TimeProvider().SlotsPerEpochExponent() + sumOfSlots := 1 << api.ProtocolParameters().SlotsPerEpochExponent() require.LessOrEqual(t, e.ActiveSlotsCount, uint64(sumOfSlots), "active slots count must be less or equal to the number of slots in the epoch") require.LessOrEqual(t, e.SlotPerformance, e.ValidationBlocksSentPerSlot, "number of subslots covered cannot be greated than number of blocks sent in a slot") diff --git a/pkg/protocol/sybilprotection/sybilprotectionv1/performance/tracker_test.go b/pkg/protocol/sybilprotection/sybilprotectionv1/performance/tracker_test.go index 46033f793..1eddb125d 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.RandBlock(tpkg.RandBasicBlock(ts.api, iotago.PayloadCandidacyAnnouncement), ts.api, 0) + + block1.Header.IssuingTime = ts.api.TimeProvider().SlotStartTime(1) + block1.Header.IssuerID = issuer1 + + block2 := tpkg.RandBlock(tpkg.RandBasicBlock(ts.api, iotago.PayloadCandidacyAnnouncement), ts.api, 0) + + block2.Header.IssuingTime = ts.api.TimeProvider().SlotStartTime(2) + block2.Header.IssuerID = issuer2 + + block3 := tpkg.RandBlock(tpkg.RandBasicBlock(ts.api, iotago.PayloadCandidacyAnnouncement), ts.api, 0) + + block3.Header.IssuingTime = ts.api.TimeProvider().SlotStartTime(3) + block3.Header.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.RandBlock(tpkg.RandBasicBlock(ts.api, iotago.PayloadCandidacyAnnouncement), ts.api, 0) + block4.Header.IssuingTime = ts.api.TimeProvider().SlotStartTime(4) + block4.Header.IssuerID = issuer1 + + block5 := tpkg.RandBlock(tpkg.RandBasicBlock(ts.api, iotago.PayloadCandidacyAnnouncement), ts.api, 0) + + block5.Header.IssuingTime = ts.api.TimeProvider().SlotStartTime(5) + block5.Header.IssuerID = issuer2 + + block6 := tpkg.RandBlock(tpkg.RandBasicBlock(ts.api, iotago.PayloadCandidacyAnnouncement), ts.api, 0) + + block6.Header.IssuingTime = ts.api.TimeProvider().SlotStartTime(6) + block6.Header.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 d8003a0aa..9bd9d8e0b 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.Initialized.OnTrigger(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().Header.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().Header.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]( @@ -256,23 +282,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) @@ -296,8 +329,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 { @@ -340,43 +380,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/block_metadata.go b/pkg/retainer/block_metadata.go index 41fc2ead8..e829045fe 100644 --- a/pkg/retainer/block_metadata.go +++ b/pkg/retainer/block_metadata.go @@ -6,23 +6,23 @@ import ( ) type BlockMetadata struct { - BlockID iotago.BlockID - BlockState apimodels.BlockState - BlockFailureReason apimodels.BlockFailureReason - TxState apimodels.TransactionState - TxFailureReason apimodels.TransactionFailureReason + BlockID iotago.BlockID + BlockState apimodels.BlockState + BlockFailureReason apimodels.BlockFailureReason + TransactionState apimodels.TransactionState + TransactionFailureReason apimodels.TransactionFailureReason } func (b *BlockMetadata) BlockMetadataResponse() *apimodels.BlockMetadataResponse { response := &apimodels.BlockMetadataResponse{ - BlockID: b.BlockID, - BlockState: b.BlockState.String(), - BlockFailureReason: b.BlockFailureReason, - TxFailureReason: b.TxFailureReason, + BlockID: b.BlockID, + BlockState: b.BlockState.String(), + BlockFailureReason: b.BlockFailureReason, + TransactionFailureReason: b.TransactionFailureReason, } - if b.TxState != apimodels.TransactionStateNoTransaction { - response.TxState = b.TxState.String() + if b.TransactionState != apimodels.TransactionStateNoTransaction { + response.TransactionState = b.TransactionState.String() } return response diff --git a/pkg/retainer/retainer/errors.go b/pkg/retainer/retainer/errors.go index 4d465c247..88d9594f6 100644 --- a/pkg/retainer/retainer/errors.go +++ b/pkg/retainer/retainer/errors.go @@ -8,7 +8,7 @@ import ( var blocksErrorsFailureReasonMap = map[error]apimodels.BlockFailureReason{ iotago.ErrIssuerAccountNotFound: apimodels.BlockFailureIssuerAccountNotFound, - iotago.ErrBurnedInsufficientMana: apimodels.BlockFailurBurnedInsufficientMana, + iotago.ErrBurnedInsufficientMana: apimodels.BlockFailureBurnedInsufficientMana, iotago.ErrBlockVersionInvalid: apimodels.BlockFailureVersionInvalid, iotago.ErrRMCNotFound: apimodels.BlockFailureAccountInvalid, iotago.ErrFailedToCalculateManaCost: apimodels.BlockFailureManaCostCalculationFailed, @@ -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/retainer/retainer/retainer.go b/pkg/retainer/retainer/retainer.go index 8f32d6705..7ebef210e 100644 --- a/pkg/retainer/retainer/retainer.go +++ b/pkg/retainer/retainer/retainer.go @@ -165,11 +165,11 @@ func (r *Retainer) BlockMetadata(blockID iotago.BlockID) (*retainer.BlockMetadat txStatus, txFailureReason := r.transactionStatus(blockID) return &retainer.BlockMetadata{ - BlockID: blockID, - BlockState: blockStatus, - BlockFailureReason: blockFailureReason, - TxState: txStatus, - TxFailureReason: txFailureReason, + BlockID: blockID, + BlockState: blockStatus, + BlockFailureReason: blockFailureReason, + TransactionState: txStatus, + TransactionFailureReason: txFailureReason, }, nil } diff --git a/pkg/storage/prunable/prunable.go b/pkg/storage/prunable/prunable.go index edb3ccfb1..88891ddd7 100644 --- a/pkg/storage/prunable/prunable.go +++ b/pkg/storage/prunable/prunable.go @@ -224,3 +224,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 51307cff1..0760d6591 100644 --- a/pkg/storage/storage_prunable.go +++ b/pkg/storage/storage_prunable.go @@ -30,6 +30,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) { s.lastAccessedBlocks.Compute(func(lastAccessedBlocks iotago.SlotIndex) iotago.SlotIndex { return max(lastAccessedBlocks, slot) diff --git a/pkg/storage/testframework_test.go b/pkg/storage/testframework_test.go index c88a32ff2..374d22fc4 100644 --- a/pkg/storage/testframework_test.go +++ b/pkg/storage/testframework_test.go @@ -90,8 +90,8 @@ func (t *TestFramework) GeneratePrunableData(epoch iotago.EpochIndex, size int64 endSlot := apiForEpoch.TimeProvider().EpochEnd(epoch) var createdBytes int64 for createdBytes < size { - block := tpkg.RandProtocolBlock(&iotago.BasicBlock{ - StrongParents: tpkg.SortedRandBlockIDs(1 + rand.Intn(iotago.BlockMaxParents)), + block := tpkg.RandBlock(&iotago.BasicBlockBody{ + StrongParents: tpkg.SortedRandBlockIDs(1 + rand.Intn(iotago.BasicBlockMaxParents)), Payload: &iotago.TaggedData{Data: make([]byte, 8192)}, MaxBurnedMana: 1000, }, apiForEpoch, 0) diff --git a/pkg/tests/accounts_test.go b/pkg/tests/accounts_test.go index 5fcc87483..d890747cd 100644 --- a/pkg/tests/accounts_test.go +++ b/pkg/tests/accounts_test.go @@ -3,43 +3,34 @@ package tests import ( "testing" - "github.com/iotaledger/hive.go/lo" - "github.com/iotaledger/hive.go/log" - "github.com/iotaledger/hive.go/runtime/options" "github.com/iotaledger/iota-core/pkg/model" - "github.com/iotaledger/iota-core/pkg/protocol" "github.com/iotaledger/iota-core/pkg/protocol/engine/accounts" - "github.com/iotaledger/iota-core/pkg/protocol/snapshotcreator" "github.com/iotaledger/iota-core/pkg/testsuite" "github.com/iotaledger/iota-core/pkg/testsuite/mock" + "github.com/iotaledger/iota-core/pkg/testsuite/snapshotcreator" "github.com/iotaledger/iota-core/pkg/utils" iotago "github.com/iotaledger/iota.go/v4" ) -// TODO: implement tests for staking and delegation transitions that cover edge cases - part of hardening phase. -func Test_TransitionAccount(t *testing.T) { - //for { - // func() { +func Test_TransitionAndDestroyAccount(t *testing.T) { oldGenesisOutputKey := utils.RandBlockIssuerKey() + ts := testsuite.NewTestSuite(t, testsuite.WithAccounts(snapshotcreator.AccountDetails{ // Nil address will be replaced with the address generated from genesis seed. - // A single key may unlock multiple accounts; that's why it can't be used as a source for AccountID derivation. Address: nil, - // Set an amount enough to cover the rent and to cover an additional key that is added in the test. - // If it's too little, then the test will fail. - Amount: testsuite.MinIssuerAccountAmount * 10, + // Set an amount enough to cover storage deposit and more issuer keys. + Amount: mock.MinIssuerAccountAmount * 10, Mana: 0, // AccountID is derived from this field, so this must be set uniquely for each account. IssuerKey: oldGenesisOutputKey, // Expiry Slot is the slot index at which the account expires. - ExpirySlot: 1, + ExpirySlot: iotago.MaxSlotIndex, // BlockIssuanceCredits on this account is custom because it never needs to issue. - // On Validator nodes it's unlimited (MaxInt64). BlockIssuanceCredits: iotago.BlockIssuanceCredits(123), }), testsuite.WithProtocolParametersOptions( iotago.WithTimeProviderOptions( - testsuite.GenesisTimeWithOffsetBySlots(100, testsuite.DefaultSlotDurationInSeconds), + testsuite.GenesisTimeWithOffsetBySlots(200, testsuite.DefaultSlotDurationInSeconds), testsuite.DefaultSlotDurationInSeconds, 8, ), @@ -54,71 +45,73 @@ func Test_TransitionAccount(t *testing.T) { ) defer ts.Shutdown() + // add a validator node to the network. This will add a validator account to the snapshot. node1 := ts.AddValidatorNode("node1") - node2 := ts.AddNode("node2") + // add a non-validator node to the network. This will not add any accounts to the snapshot. + _ = ts.AddNode("node2") + // add a default block issuer to the network. This will add another block issuer account to the snapshot. + wallet := ts.AddGenesisWallet("default", node1, iotago.MaxBlockIssuanceCredits/2) - blockIssuer := ts.AddBasicBlockIssuer("default", iotago.MaxBlockIssuanceCredits/2) - - ts.Run(true, map[string][]options.Option[protocol.Protocol]{}) - node1.Protocol.SetLogLevel(log.LevelTrace) - node2.Protocol.SetLogLevel(log.LevelTrace) + ts.Run(true) + // check that the accounts added in the genesis snapshot were added to account manager correctly. + // genesis account. genesisAccount := ts.AccountOutput("Genesis:1") genesisAccountOutput := genesisAccount.Output().(*iotago.AccountOutput) - ts.AssertAccountData(&accounts.AccountData{ ID: genesisAccountOutput.AccountID, Credits: accounts.NewBlockIssuanceCredits(iotago.BlockIssuanceCredits(123), 0), OutputID: genesisAccount.OutputID(), - ExpirySlot: 1, + ExpirySlot: iotago.MaxSlotIndex, BlockIssuerKeys: iotago.NewBlockIssuerKeys(oldGenesisOutputKey), }, ts.Nodes()...) + // validator node account. + validatorAccountOutput := ts.AccountOutput("Genesis:2") + ts.AssertAccountData(&accounts.AccountData{ + ID: node1.Validator.AccountID, + Credits: accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2, 0), + OutputID: validatorAccountOutput.OutputID(), + ExpirySlot: iotago.MaxSlotIndex, + BlockIssuerKeys: node1.Validator.BlockIssuerKeys(), + StakeEndEpoch: iotago.MaxEpochIndex, + ValidatorStake: mock.MinValidatorAccountAmount, + }, ts.Nodes()...) + // default wallet block issuer account. + blockIssuerAccountOutput := ts.AccountOutput("Genesis:3") + ts.AssertAccountData(&accounts.AccountData{ + ID: wallet.BlockIssuer.AccountID, + Credits: accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2, 0), + OutputID: blockIssuerAccountOutput.OutputID(), + ExpirySlot: iotago.MaxSlotIndex, + BlockIssuerKeys: wallet.BlockIssuer.BlockIssuerKeys(), + }, ts.Nodes()...) - // MODIFY EXISTING GENESIS ACCOUNT AND PREPARE SOME BASIC OUTPUTS - + // MODIFY EXISTING GENESIS ACCOUNT newGenesisOutputKey := utils.RandBlockIssuerKey() - newAccountBlockIssuerKey := utils.RandBlockIssuerKey() - latestCommitmentID := node1.Protocol.Engines.Main.Get().Storage.Settings().LatestCommitment().Commitment().MustID() + var block1Slot iotago.SlotIndex = 1 + // set the expiry of the genesis account to be the block slot + max committable age. + newExpirySlot := block1Slot + ts.API.ProtocolParameters().MaxCommittableAge() - accountInput, accountOutputs, accountWallets := ts.TransactionFramework.TransitionAccount( + tx1 := ts.DefaultWallet().TransitionAccount( + "TX1", "Genesis:1", - testsuite.WithAddBlockIssuerKey(newGenesisOutputKey), - testsuite.WithBlockIssuerExpirySlot(1), + mock.WithAddBlockIssuerKey(newGenesisOutputKey), + mock.WithBlockIssuerExpirySlot(newExpirySlot), ) - consumedInputs, equalOutputs, equalWallets := ts.TransactionFramework.CreateBasicOutputsEqually(4, "Genesis:0") - - tx1 := lo.PanicOnErr(ts.TransactionFramework.CreateSignedTransactionWithOptions("TX1", append(accountWallets, equalWallets...), - testsuite.WithAccountInput(accountInput, true), - testsuite.WithInputs(consumedInputs), - testsuite.WithContextInputs(iotago.TxEssenceContextInputs{ - &iotago.BlockIssuanceCreditInput{ - AccountID: genesisAccountOutput.AccountID, - }, - &iotago.CommitmentInput{ - CommitmentID: latestCommitmentID, - }, - }), - testsuite.WithOutputs(append(accountOutputs, equalOutputs...)), - testsuite.WithAllotments(iotago.Allotments{&iotago.Allotment{ - AccountID: genesisAccountOutput.AccountID, - Value: 0, - }}), - )) - var block1Slot iotago.SlotIndex = 1 + // default block issuer issues a block containing the transaction in slot 1. genesisCommitment := iotago.NewEmptyCommitment(ts.API.ProtocolParameters().Version()) genesisCommitment.ReferenceManaCost = ts.API.ProtocolParameters().CongestionControlParameters().MinReferenceManaCost - - block1 := ts.IssueBasicBlockAtSlotWithOptions("block1", block1Slot, genesisCommitment, blockIssuer, node1, tx1) - + block1 := ts.IssueBasicBlockAtSlotWithOptions("block1", block1Slot, ts.DefaultWallet(), tx1, mock.WithSlotCommitment(genesisCommitment)) latestParent := ts.CommitUntilSlot(ts.BlockID("block1").Slot(), block1) + // assert diff of the genesis account, it should have a new output ID, new expiry slot and a new block issuer key. ts.AssertAccountDiff(genesisAccountOutput.AccountID, block1Slot, &model.AccountDiff{ BICChange: 0, PreviousUpdatedTime: 0, - PreviousExpirySlot: 1, - NewExpirySlot: 1, - NewOutputID: iotago.OutputIDFromTransactionIDAndIndex(ts.TransactionFramework.TransactionID("TX1"), 0), + PreviousExpirySlot: iotago.MaxSlotIndex, + NewExpirySlot: newExpirySlot, + NewOutputID: ts.DefaultWallet().Output("TX1:0").OutputID(), PreviousOutputID: genesisAccount.OutputID(), BlockIssuerKeysRemoved: iotago.NewBlockIssuerKeys(), BlockIssuerKeysAdded: iotago.NewBlockIssuerKeys(newGenesisOutputKey), @@ -127,57 +120,30 @@ func Test_TransitionAccount(t *testing.T) { ts.AssertAccountData(&accounts.AccountData{ ID: genesisAccountOutput.AccountID, Credits: accounts.NewBlockIssuanceCredits(iotago.BlockIssuanceCredits(123), 0), - OutputID: iotago.OutputIDFromTransactionIDAndIndex(ts.TransactionFramework.TransactionID("TX1"), 0), + OutputID: ts.DefaultWallet().Output("TX1:0").OutputID(), BlockIssuerKeys: iotago.NewBlockIssuerKeys(oldGenesisOutputKey, newGenesisOutputKey), - ExpirySlot: 1, + ExpirySlot: newExpirySlot, }, ts.Nodes()...) - // DESTROY GENESIS ACCOUNT, CREATE NEW ACCOUNT WITH BLOCK ISSUER AND STAKING FEATURES FROM BASIC UTXO - - // commit until the expiry slot of the transitioned genesis account plus one - latestParent = ts.CommitUntilSlot(accountOutputs[0].FeatureSet().BlockIssuer().ExpirySlot+1, latestParent) - // set the expiry slof of the transitioned genesis account to the latest committed + MaxCommittableAge - newAccountExpirySlot := node1.Protocol.Engines.Main.Get().Storage.Settings().LatestCommitment().Slot() + ts.API.ProtocolParameters().MaxCommittableAge() - inputForNewAccount, newAccountOutputs, newAccountWallets := ts.TransactionFramework.CreateAccountFromInput("TX1:1", - testsuite.WithAccountConditions(iotago.AccountOutputUnlockConditions{ - &iotago.StateControllerAddressUnlockCondition{Address: ts.TransactionFramework.DefaultAddress()}, - &iotago.GovernorAddressUnlockCondition{Address: ts.TransactionFramework.DefaultAddress()}, - }), - testsuite.WithBlockIssuerFeature(iotago.BlockIssuerKeys{newAccountBlockIssuerKey}, newAccountExpirySlot), - testsuite.WithStakingFeature(10000, 421, 0, 10), - ) - - destroyedAccountInput, destructionOutputs, destroyWallets := ts.TransactionFramework.DestroyAccount("TX1:0") + // DESTROY GENESIS ACCOUNT + // commit until the expiry slot of the transitioned genesis account plus one. + latestParent = ts.CommitUntilSlot(newExpirySlot+1, latestParent) + // issue the block containing the transaction in the same slot as the latest parent block. block2Slot := latestParent.ID().Slot() - - tx2 := lo.PanicOnErr(ts.TransactionFramework.CreateSignedTransactionWithOptions("TX2", append(newAccountWallets, destroyWallets...), - testsuite.WithContextInputs(iotago.TxEssenceContextInputs{ - &iotago.BlockIssuanceCreditInput{ - AccountID: genesisAccountOutput.AccountID, - }, - &iotago.CommitmentInput{ - CommitmentID: node1.Protocol.Engines.Main.Get().Storage.Settings().LatestCommitment().Commitment().MustID(), - }, - }), - testsuite.WithInputs(inputForNewAccount), - testsuite.WithAccountInput(destroyedAccountInput, true), - testsuite.WithOutputs(append(newAccountOutputs, destructionOutputs...)), - testsuite.WithSlotCreated(block2Slot), - )) - - block2 := ts.IssueBasicBlockAtSlotWithOptions("block2", block2Slot, node1.Protocol.Engines.Main.Get().Storage.Settings().LatestCommitment().Commitment(), blockIssuer, node1, tx2, mock.WithStrongParents(latestParent.ID())) - + // create a transaction which destroys the genesis account. + tx2 := ts.DefaultWallet().DestroyAccount("TX2", "TX1:0", block2Slot) + block2 := ts.IssueBasicBlockAtSlotWithOptions("block2", block2Slot, ts.DefaultWallet(), tx2, mock.WithStrongParents(latestParent.ID())) latestParent = ts.CommitUntilSlot(block2Slot, block2) - // assert diff of a destroyed account, to make sure we can correctly restore it + // assert diff of the destroyed account. ts.AssertAccountDiff(genesisAccountOutput.AccountID, block2Slot, &model.AccountDiff{ BICChange: -iotago.BlockIssuanceCredits(123), PreviousUpdatedTime: 0, NewExpirySlot: 0, - PreviousExpirySlot: 1, + PreviousExpirySlot: newExpirySlot, NewOutputID: iotago.EmptyOutputID, - PreviousOutputID: iotago.OutputIDFromTransactionIDAndIndex(lo.PanicOnErr(ts.TransactionFramework.Transaction("TX1").ID()), 0), + PreviousOutputID: ts.DefaultWallet().Output("TX1:0").OutputID(), BlockIssuerKeysAdded: iotago.NewBlockIssuerKeys(), BlockIssuerKeysRemoved: iotago.NewBlockIssuerKeys(oldGenesisOutputKey, newGenesisOutputKey), ValidatorStakeChange: 0, @@ -185,11 +151,83 @@ func Test_TransitionAccount(t *testing.T) { FixedCostChange: 0, DelegationStakeChange: 0, }, true, ts.Nodes()...) +} + +func Test_StakeDelegateAndDelayedClaim(t *testing.T) { + ts := testsuite.NewTestSuite(t, + testsuite.WithProtocolParametersOptions( + iotago.WithTimeProviderOptions( + testsuite.GenesisTimeWithOffsetBySlots(100, testsuite.DefaultSlotDurationInSeconds), + testsuite.DefaultSlotDurationInSeconds, + 8, + ), + iotago.WithLivenessOptions( + testsuite.DefaultLivenessThresholdLowerBoundInSeconds, + testsuite.DefaultLivenessThresholdUpperBoundInSeconds, + testsuite.DefaultMinCommittableAge, + 100, + testsuite.DefaultEpochNearingThreshold, + ), + ), + ) + defer ts.Shutdown() + + // add a validator node to the network. This will add a validator account to the snapshot. + node1 := ts.AddValidatorNode("node1") + // add a non-validator node to the network. This will not add any accounts to the snapshot. + _ = ts.AddNode("node2") + // add a default block issuer to the network. This will add another block issuer account to the snapshot. + wallet := ts.AddGenesisWallet("default", node1, iotago.MaxBlockIssuanceCredits/2) + + ts.Run(true) - newAccount := ts.AccountOutput("TX2:0") + // assert validator and block issuer accounts in genesis snapshot. + // validator node account. + validatorAccountOutput := ts.AccountOutput("Genesis:1") + ts.AssertAccountData(&accounts.AccountData{ + ID: node1.Validator.AccountID, + Credits: accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2, 0), + OutputID: validatorAccountOutput.OutputID(), + ExpirySlot: iotago.MaxSlotIndex, + BlockIssuerKeys: node1.Validator.BlockIssuerKeys(), + StakeEndEpoch: iotago.MaxEpochIndex, + ValidatorStake: mock.MinValidatorAccountAmount, + }, ts.Nodes()...) + // default wallet block issuer account. + blockIssuerAccountOutput := ts.AccountOutput("Genesis:2") + ts.AssertAccountData(&accounts.AccountData{ + ID: wallet.BlockIssuer.AccountID, + Credits: accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2, 0), + OutputID: blockIssuerAccountOutput.OutputID(), + ExpirySlot: iotago.MaxSlotIndex, + BlockIssuerKeys: wallet.BlockIssuer.BlockIssuerKeys(), + }, ts.Nodes()...) + + //CREATE NEW ACCOUNT WITH BLOCK ISSUER AND STAKING FEATURES FROM BASIC UTXO + newAccountBlockIssuerKey := utils.RandBlockIssuerKey() + // set the expiry slot of the transitioned genesis account to the latest committed + MaxCommittableAge + newAccountExpirySlot := node1.Protocol.Engines.Main.Get().Storage.Settings().LatestCommitment().Slot() + ts.API.ProtocolParameters().MaxCommittableAge() + + var block1Slot iotago.SlotIndex = 1 + tx1 := ts.DefaultWallet().CreateAccountFromInput( + "TX1", + "Genesis:0", + ts.DefaultWallet(), + block1Slot, + mock.WithBlockIssuerFeature(iotago.BlockIssuerKeys{newAccountBlockIssuerKey}, newAccountExpirySlot), + mock.WithStakingFeature(10000, 421, 0, 10), + mock.WithAccountAmount(mock.MinIssuerAccountAmount), + ) + + genesisCommitment := iotago.NewEmptyCommitment(ts.API.ProtocolParameters().Version()) + genesisCommitment.ReferenceManaCost = ts.API.ProtocolParameters().CongestionControlParameters().MinReferenceManaCost + block1 := ts.IssueBasicBlockAtSlotWithOptions("block1", block1Slot, ts.DefaultWallet(), tx1) + latestParent := ts.CommitUntilSlot(block1Slot, block1) + + newAccount := ts.DefaultWallet().AccountOutput("TX1:0") newAccountOutput := newAccount.Output().(*iotago.AccountOutput) - ts.AssertAccountDiff(newAccountOutput.AccountID, block2Slot, &model.AccountDiff{ + ts.AssertAccountDiff(newAccountOutput.AccountID, block1Slot, &model.AccountDiff{ BICChange: 0, PreviousUpdatedTime: 0, NewExpirySlot: newAccountExpirySlot, @@ -206,7 +244,7 @@ func Test_TransitionAccount(t *testing.T) { ts.AssertAccountData(&accounts.AccountData{ ID: newAccountOutput.AccountID, - Credits: accounts.NewBlockIssuanceCredits(0, block2Slot), + Credits: accounts.NewBlockIssuanceCredits(0, block1Slot), ExpirySlot: newAccountExpirySlot, OutputID: newAccount.OutputID(), BlockIssuerKeys: iotago.NewBlockIssuerKeys(newAccountBlockIssuerKey), @@ -216,32 +254,22 @@ func Test_TransitionAccount(t *testing.T) { ValidatorStake: 10000, }, ts.Nodes()...) + // CREATE DELEGATION TO NEW ACCOUNT FROM BASIC UTXO accountAddress := iotago.AccountAddress(newAccountOutput.AccountID) - // create a delegation output delegating to the newly created account - inputForNewDelegation, newDelegationOutputs, newDelegationWallets := ts.TransactionFramework.CreateDelegationFromInput("TX1:2", - testsuite.WithDelegatedValidatorAddress(&accountAddress), - testsuite.WithDelegationStartEpoch(1), + block2Slot := latestParent.ID().Slot() + tx2 := ts.DefaultWallet().CreateDelegationFromInput( + "TX2", + "TX1:1", + block2Slot, + mock.WithDelegatedValidatorAddress(&accountAddress), + mock.WithDelegationStartEpoch(1), ) + block2 := ts.IssueBasicBlockAtSlotWithOptions("block2", block2Slot, ts.DefaultWallet(), tx2, mock.WithStrongParents(latestParent.ID())) - block3Slot := latestParent.ID().Slot() - - tx3 := lo.PanicOnErr(ts.TransactionFramework.CreateSignedTransactionWithOptions("TX3", newDelegationWallets, - testsuite.WithContextInputs(iotago.TxEssenceContextInputs{ - &iotago.CommitmentInput{ - CommitmentID: node1.Protocol.Engines.Main.Get().Storage.Settings().LatestCommitment().Commitment().MustID(), - }, - }), - testsuite.WithInputs(inputForNewDelegation), - testsuite.WithOutputs(newDelegationOutputs), - testsuite.WithSlotCreated(block3Slot), - )) - - block3 := ts.IssueBasicBlockAtSlotWithOptions("block3", block3Slot, node1.Protocol.Engines.Main.Get().Storage.Settings().LatestCommitment().Commitment(), blockIssuer, node1, tx3, mock.WithStrongParents(latestParent.ID())) - - latestParent = ts.CommitUntilSlot(block3Slot, block3) - delegatedAmount := inputForNewDelegation[0].BaseTokenAmount() + latestParent = ts.CommitUntilSlot(block2Slot, block2) + delegatedAmount := ts.DefaultWallet().Output("TX1:1").BaseTokenAmount() - ts.AssertAccountDiff(newAccountOutput.AccountID, block3Slot, &model.AccountDiff{ + ts.AssertAccountDiff(newAccountOutput.AccountID, block2Slot, &model.AccountDiff{ BICChange: 0, PreviousUpdatedTime: 0, NewOutputID: iotago.EmptyOutputID, @@ -256,7 +284,7 @@ func Test_TransitionAccount(t *testing.T) { ts.AssertAccountData(&accounts.AccountData{ ID: newAccountOutput.AccountID, - Credits: accounts.NewBlockIssuanceCredits(0, block2Slot), + Credits: accounts.NewBlockIssuanceCredits(0, block1Slot), ExpirySlot: newAccountExpirySlot, OutputID: newAccount.OutputID(), BlockIssuerKeys: iotago.NewBlockIssuerKeys(newAccountBlockIssuerKey), @@ -267,27 +295,14 @@ func Test_TransitionAccount(t *testing.T) { }, ts.Nodes()...) // transition a delegation output to a delayed claiming state - inputForDelegationTransition, delegationTransitionOutputs, delegationTransitionWallets := ts.TransactionFramework.DelayedClaimingTransition("TX3:0", 0) - - tx4 := lo.PanicOnErr(ts.TransactionFramework.CreateSignedTransactionWithOptions("TX4", delegationTransitionWallets, - testsuite.WithContextInputs(iotago.TxEssenceContextInputs{ - &iotago.CommitmentInput{ - CommitmentID: node1.Protocol.Engines.Main.Get().Storage.Settings().LatestCommitment().Commitment().MustID(), - }, - }), - testsuite.WithInputs(inputForDelegationTransition), - testsuite.WithOutputs(delegationTransitionOutputs), - testsuite.WithSlotCreated(block3Slot), - )) - - block4Slot := latestParent.ID().Slot() - - block4 := ts.IssueBasicBlockAtSlotWithOptions("block4", block4Slot, node1.Protocol.Engines.Main.Get().Storage.Settings().LatestCommitment().Commitment(), blockIssuer, node1, tx4, mock.WithStrongParents(latestParent.ID())) + block3Slot := latestParent.ID().Slot() + tx3 := ts.DefaultWallet().DelayedClaimingTransition("TX3", "TX2:0", block3Slot, 0) + block3 := ts.IssueBasicBlockAtSlotWithOptions("block3", block3Slot, ts.DefaultWallet(), tx3, mock.WithStrongParents(latestParent.ID())) - latestParent = ts.CommitUntilSlot(block4Slot, block4) + latestParent = ts.CommitUntilSlot(block3Slot, block3) // Transitioning to delayed claiming effectively removes the delegation, so we expect a negative delegation stake change. - ts.AssertAccountDiff(newAccountOutput.AccountID, block4Slot, &model.AccountDiff{ + ts.AssertAccountDiff(newAccountOutput.AccountID, block3Slot, &model.AccountDiff{ BICChange: 0, PreviousUpdatedTime: 0, NewOutputID: iotago.EmptyOutputID, @@ -302,7 +317,7 @@ func Test_TransitionAccount(t *testing.T) { ts.AssertAccountData(&accounts.AccountData{ ID: newAccountOutput.AccountID, - Credits: accounts.NewBlockIssuanceCredits(0, block2Slot), + Credits: accounts.NewBlockIssuanceCredits(0, block1Slot), ExpirySlot: newAccountExpirySlot, OutputID: newAccount.OutputID(), BlockIssuerKeys: iotago.NewBlockIssuerKeys(newAccountBlockIssuerKey), @@ -311,72 +326,110 @@ func Test_TransitionAccount(t *testing.T) { DelegationStake: iotago.BaseToken(0), ValidatorStake: 10000, }, ts.Nodes()...) +} - // CREATE IMPLICIT ACCOUNT FROM BASIC UTXO - inputForImplicitAccount, outputsForImplicitAccount, implicitAccountAddress, implicitWallet := ts.TransactionFramework.CreateImplicitAccountFromInput("TX1:3") - - tx5 := lo.PanicOnErr(ts.TransactionFramework.CreateSignedTransactionWithOptions("TX5", implicitWallet, - testsuite.WithInputs(inputForImplicitAccount), - testsuite.WithOutputs(outputsForImplicitAccount), - )) - - implicitAccountOutput := ts.TransactionFramework.Output("TX5:0") - implicitAccountOutputID := implicitAccountOutput.OutputID() - implicitAccountID := iotago.AccountIDFromOutputID(implicitAccountOutputID) +func Test_ImplicitAccounts(t *testing.T) { + ts := testsuite.NewTestSuite(t, + testsuite.WithProtocolParametersOptions( + iotago.WithTimeProviderOptions( + testsuite.GenesisTimeWithOffsetBySlots(100, testsuite.DefaultSlotDurationInSeconds), + testsuite.DefaultSlotDurationInSeconds, + 8, + ), + iotago.WithLivenessOptions( + testsuite.DefaultLivenessThresholdLowerBoundInSeconds, + testsuite.DefaultLivenessThresholdUpperBoundInSeconds, + testsuite.DefaultMinCommittableAge, + 100, + testsuite.DefaultEpochNearingThreshold, + ), + ), + ) + defer ts.Shutdown() - slotIndexBlock5 := latestParent.ID().Index() + // add a validator node to the network. This will add a validator account to the snapshot. + node1 := ts.AddValidatorNode("node1") + // add a non-validator node to the network. This will not add any accounts to the snapshot. + _ = ts.AddNode("node2") + // add a default block issuer to the network. This will add another block issuer account to the snapshot. + wallet := ts.AddGenesisWallet("default", node1, iotago.MaxBlockIssuanceCredits/2) - block5 := ts.IssueBasicBlockAtSlotWithOptions("block5", slotIndexBlock5, node1.Protocol.Engines.Main.Get().Storage.Settings().LatestCommitment().Commitment(), blockIssuer, node1, tx5, mock.WithStrongParents(latestParent.ID())) + ts.Run(true) - latestParent = ts.CommitUntilSlot(slotIndexBlock5, block5) + // assert validator and block issuer accounts in genesis snapshot. + // validator node account. + validatorAccountOutput := ts.AccountOutput("Genesis:1") + ts.AssertAccountData(&accounts.AccountData{ + ID: node1.Validator.AccountID, + Credits: accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2, 0), + OutputID: validatorAccountOutput.OutputID(), + ExpirySlot: iotago.MaxSlotIndex, + BlockIssuerKeys: node1.Validator.BlockIssuerKeys(), + StakeEndEpoch: iotago.MaxEpochIndex, + ValidatorStake: mock.MinValidatorAccountAmount, + }, ts.Nodes()...) + // default wallet block issuer account. + blockIssuerAccountOutput := ts.AccountOutput("Genesis:2") + ts.AssertAccountData(&accounts.AccountData{ + ID: wallet.BlockIssuer.AccountID, + Credits: accounts.NewBlockIssuanceCredits(iotago.MaxBlockIssuanceCredits/2, 0), + OutputID: blockIssuerAccountOutput.OutputID(), + ExpirySlot: iotago.MaxSlotIndex, + BlockIssuerKeys: wallet.BlockIssuer.BlockIssuerKeys(), + }, ts.Nodes()...) - var implicitBlockIssuerKey iotago.BlockIssuerKey = iotago.Ed25519PublicKeyHashBlockIssuerKeyFromImplicitAccountCreationAddress(implicitAccountAddress) + // CREATE IMPLICIT ACCOUNT FROM GENESIS BASIC UTXO, SENT TO A NEW USER WALLET. + // this wallet is not registered in the ledger yet. + newUserWallet := mock.NewWallet(ts.Testing, "newUser", node1) + // a default wallet, already registered in the ledger, will issue the transaction and block. + tx1 := ts.DefaultWallet().CreateImplicitAccountFromInput( + "TX1", + "Genesis:0", + newUserWallet, + ) + var block1Slot iotago.SlotIndex = 1 + block1 := ts.IssueBasicBlockAtSlotWithOptions("block1", block1Slot, ts.DefaultWallet(), tx1) + latestParent := ts.CommitUntilSlot(block1Slot, block1) + implicitAccountOutput := newUserWallet.Output("TX1:0") + implicitAccountOutputID := implicitAccountOutput.OutputID() + implicitAccountID := iotago.AccountIDFromOutputID(implicitAccountOutputID) + var implicitBlockIssuerKey iotago.BlockIssuerKey = iotago.Ed25519PublicKeyHashBlockIssuerKeyFromImplicitAccountCreationAddress(newUserWallet.ImplicitAccountCreationAddress()) + // the new implicit account should now be registered in the accounts ledger. ts.AssertAccountData(&accounts.AccountData{ ID: implicitAccountID, - Credits: accounts.NewBlockIssuanceCredits(0, slotIndexBlock5), + Credits: accounts.NewBlockIssuanceCredits(0, block1Slot), ExpirySlot: iotago.MaxSlotIndex, OutputID: implicitAccountOutputID, BlockIssuerKeys: iotago.NewBlockIssuerKeys(implicitBlockIssuerKey), }, ts.Nodes()...) - // TRANSITION IMPLICIT ACCOUNT TO ACCOUNT OUTPUT - + // TRANSITION IMPLICIT ACCOUNT TO ACCOUNT OUTPUT. + // USE IMPLICIT ACCOUNT AS BLOCK ISSUER. fullAccountBlockIssuerKey := utils.RandBlockIssuerKey() - inputForImplicitAccountTransition, outputsForImplicitAccountTransition, fullAccountWallet := ts.TransactionFramework.TransitionImplicitAccountToAccountOutput( - "TX5:0", - testsuite.WithBlockIssuerFeature( + block2Slot := latestParent.ID().Index() + tx2 := newUserWallet.TransitionImplicitAccountToAccountOutput( + "TX2", + "TX1:0", + block2Slot, + mock.WithBlockIssuerFeature( iotago.BlockIssuerKeys{fullAccountBlockIssuerKey}, iotago.MaxSlotIndex, ), + mock.WithAccountAmount(mock.MinIssuerAccountAmount), ) + block2Commitment := node1.Protocol.Engines.Main.Get().Storage.Settings().LatestCommitment().Commitment() + block2 := ts.IssueBasicBlockAtSlotWithOptions("block2", block2Slot, newUserWallet, tx2, mock.WithStrongParents(latestParent.ID())) + latestParent = ts.CommitUntilSlot(block2Slot, block2) - tx6 := lo.PanicOnErr(ts.TransactionFramework.CreateSignedTransactionWithOptions("TX6", fullAccountWallet, - testsuite.WithContextInputs(iotago.TxEssenceContextInputs{ - &iotago.BlockIssuanceCreditInput{ - AccountID: implicitAccountID, - }, - &iotago.CommitmentInput{ - CommitmentID: node1.Protocol.Engines.Main.Get().Storage.Settings().LatestCommitment().Commitment().MustID(), - }, - }), - testsuite.WithInputs(inputForImplicitAccountTransition), - testsuite.WithOutputs(outputsForImplicitAccountTransition), - testsuite.WithSlotCreated(slotIndexBlock5), - )) - - slotIndexBlock6 := latestParent.ID().Index() - - block6 := ts.IssueBasicBlockAtSlotWithOptions("block6", slotIndexBlock6, node1.Protocol.Engines.Main.Get().Storage.Settings().LatestCommitment().Commitment(), blockIssuer, node1, tx6, mock.WithStrongParents(latestParent.ID())) - - latestParent = ts.CommitUntilSlot(slotIndexBlock6, block6) - - fullAccountOutputID := ts.TransactionFramework.Output("TX6:0").OutputID() - - ts.AssertAccountDiff(implicitAccountID, slotIndexBlock6, &model.AccountDiff{ - BICChange: 0, - PreviousUpdatedTime: 0, + fullAccountOutputID := newUserWallet.Output("TX2:0").OutputID() + allotted := iotago.BlockIssuanceCredits(tx2.Transaction.Allotments.Get(implicitAccountID)) + burned := iotago.BlockIssuanceCredits(block2.WorkScore()) * iotago.BlockIssuanceCredits(block2Commitment.ReferenceManaCost) + // the implicit account should now have been transitioned to a full account in the accounts ledger. + ts.AssertAccountDiff(implicitAccountID, block2Slot, &model.AccountDiff{ + BICChange: allotted - burned, + PreviousUpdatedTime: block1Slot, NewOutputID: fullAccountOutputID, PreviousOutputID: implicitAccountOutputID, PreviousExpirySlot: iotago.MaxSlotIndex, @@ -388,10 +441,9 @@ func Test_TransitionAccount(t *testing.T) { FixedCostChange: 0, DelegationStakeChange: 0, }, false, ts.Nodes()...) - ts.AssertAccountData(&accounts.AccountData{ ID: implicitAccountID, - Credits: accounts.NewBlockIssuanceCredits(0, slotIndexBlock5), + Credits: accounts.NewBlockIssuanceCredits(allotted-burned, block2Slot), ExpirySlot: iotago.MaxSlotIndex, OutputID: fullAccountOutputID, BlockIssuerKeys: iotago.NewBlockIssuerKeys(fullAccountBlockIssuerKey), diff --git a/pkg/tests/booker_test.go b/pkg/tests/booker_test.go index 62b70f623..daf8b0424 100644 --- a/pkg/tests/booker_test.go +++ b/pkg/tests/booker_test.go @@ -19,33 +19,33 @@ func Test_IssuingTransactionsOutOfOrder(t *testing.T) { defer ts.Shutdown() node1 := ts.AddValidatorNode("node1") - blockIssuer := ts.AddBasicBlockIssuer("default") + wallet := ts.AddGenesisWallet("default", node1) ts.Run(true, map[string][]options.Option[protocol.Protocol]{}) - tx1 := lo.PanicOnErr(ts.TransactionFramework.CreateSimpleTransaction("tx1", 1, "Genesis:0")) + tx1 := wallet.CreateBasicOutputsEquallyFromInputs("tx1", 1, "Genesis:0") - tx2 := lo.PanicOnErr(ts.TransactionFramework.CreateSimpleTransaction("tx2", 1, "tx1:0")) + tx2 := wallet.CreateBasicOutputsEquallyFromInputs("tx2", 1, "tx1:0") - ts.IssuePayloadWithOptions("block1", blockIssuer, node1, tx2) + ts.IssuePayloadWithOptions("block1", wallet, tx2) - ts.AssertTransactionsExist(ts.TransactionFramework.Transactions("tx2"), true, node1) - ts.AssertTransactionsExist(ts.TransactionFramework.Transactions("tx1"), false, node1) + ts.AssertTransactionsExist(wallet.Transactions("tx2"), true, node1) + ts.AssertTransactionsExist(wallet.Transactions("tx1"), false, node1) - ts.AssertTransactionsInCacheBooked(ts.TransactionFramework.Transactions("tx2"), false, node1) + ts.AssertTransactionsInCacheBooked(wallet.Transactions("tx2"), false, node1) // make sure that the block is not booked - ts.IssuePayloadWithOptions("block2", blockIssuer, node1, tx1) + ts.IssuePayloadWithOptions("block2", wallet, tx1) - ts.AssertTransactionsExist(ts.TransactionFramework.Transactions("tx1", "tx2"), true, node1) - ts.AssertTransactionsInCacheBooked(ts.TransactionFramework.Transactions("tx1", "tx2"), true, node1) + ts.AssertTransactionsExist(wallet.Transactions("tx1", "tx2"), true, node1) + ts.AssertTransactionsInCacheBooked(wallet.Transactions("tx1", "tx2"), true, node1) ts.AssertBlocksInCacheConflicts(map[*blocks.Block][]string{ ts.Block("block1"): {"tx2"}, ts.Block("block2"): {"tx1"}, }, node1) ts.AssertTransactionInCacheConflicts(map[*iotago.Transaction][]string{ - ts.TransactionFramework.Transaction("tx2"): {"tx2"}, - ts.TransactionFramework.Transaction("tx1"): {"tx1"}, + wallet.Transaction("tx2"): {"tx2"}, + wallet.Transaction("tx1"): {"tx1"}, }, node1) } @@ -55,7 +55,7 @@ func Test_DoubleSpend(t *testing.T) { node1 := ts.AddValidatorNode("node1") node2 := ts.AddValidatorNode("node2") - blockIssuer := ts.AddBasicBlockIssuer("default") + wallet := ts.AddGenesisWallet("default", node1) ts.Run(true, map[string][]options.Option[protocol.Protocol]{}) @@ -66,23 +66,23 @@ func Test_DoubleSpend(t *testing.T) { // Create and issue double spends { - tx1 := lo.PanicOnErr(ts.TransactionFramework.CreateSimpleTransaction("tx1", 1, "Genesis:0")) - tx2 := lo.PanicOnErr(ts.TransactionFramework.CreateSimpleTransaction("tx2", 1, "Genesis:0")) + tx1 := wallet.CreateBasicOutputsEquallyFromInputs("tx1", 1, "Genesis:0") + tx2 := wallet.CreateBasicOutputsEquallyFromInputs("tx2", 1, "Genesis:0") - ts.IssuePayloadWithOptions("block1", blockIssuer, node1, tx1, mock.WithStrongParents(ts.BlockID("Genesis"))) - ts.IssuePayloadWithOptions("block2", blockIssuer, node1, tx2, mock.WithStrongParents(ts.BlockID("Genesis"))) + ts.IssuePayloadWithOptions("block1", wallet, tx1, mock.WithStrongParents(ts.BlockID("Genesis"))) + ts.IssuePayloadWithOptions("block2", wallet, tx2, mock.WithStrongParents(ts.BlockID("Genesis"))) - ts.AssertTransactionsExist(ts.TransactionFramework.Transactions("tx1", "tx2"), true, node1, node2) - ts.AssertTransactionsInCacheBooked(ts.TransactionFramework.Transactions("tx1", "tx2"), true, node1, node2) - ts.AssertTransactionsInCachePending(ts.TransactionFramework.Transactions("tx1", "tx2"), true, node1, node2) + ts.AssertTransactionsExist(wallet.Transactions("tx1", "tx2"), true, node1, node2) + ts.AssertTransactionsInCacheBooked(wallet.Transactions("tx1", "tx2"), true, node1, node2) + ts.AssertTransactionsInCachePending(wallet.Transactions("tx1", "tx2"), true, node1, node2) ts.AssertBlocksInCacheConflicts(map[*blocks.Block][]string{ ts.Block("block1"): {"tx1"}, ts.Block("block2"): {"tx2"}, }, node1, node2) ts.AssertTransactionInCacheConflicts(map[*iotago.Transaction][]string{ - ts.TransactionFramework.Transaction("tx2"): {"tx2"}, - ts.TransactionFramework.Transaction("tx1"): {"tx1"}, + wallet.Transaction("tx2"): {"tx2"}, + wallet.Transaction("tx1"): {"tx1"}, }, node1, node2) } @@ -95,14 +95,14 @@ func Test_DoubleSpend(t *testing.T) { ts.Block("block3"): {"tx1"}, ts.Block("block4"): {"tx2"}, }, node1, node2) - ts.AssertTransactionsInCachePending(ts.TransactionFramework.Transactions("tx1", "tx2"), true, node1, node2) + ts.AssertTransactionsInCachePending(wallet.Transactions("tx1", "tx2"), true, node1, node2) } // Issue an invalid block and assert that its vote is not cast. { ts.IssueValidationBlock("block5", node2, mock.WithStrongParents(ts.BlockIDs("block3", "block4")...)) - ts.AssertTransactionsInCachePending(ts.TransactionFramework.Transactions("tx1", "tx2"), true, node1, node2) + ts.AssertTransactionsInCachePending(wallet.Transactions("tx1", "tx2"), true, node1, node2) } // Issue valid blocks that resolve the conflict. @@ -113,8 +113,8 @@ func Test_DoubleSpend(t *testing.T) { ts.AssertBlocksInCacheConflicts(map[*blocks.Block][]string{ ts.Block("block6"): {"tx2"}, }, node1, node2) - ts.AssertTransactionsInCacheAccepted(ts.TransactionFramework.Transactions("tx2"), true, node1, node2) - ts.AssertTransactionsInCacheRejected(ts.TransactionFramework.Transactions("tx1"), true, node1, node2) + ts.AssertTransactionsInCacheAccepted(wallet.Transactions("tx2"), true, node1, node2) + ts.AssertTransactionsInCacheRejected(wallet.Transactions("tx1"), true, node1, node2) } } @@ -125,8 +125,7 @@ func Test_MultipleAttachments(t *testing.T) { nodeA := ts.AddValidatorNode("nodeA") nodeB := ts.AddValidatorNode("nodeB") - blockIssuerA := ts.AddBasicBlockIssuer("blockIssuerA") - blockIssuerB := ts.AddBasicBlockIssuer("blockIssuerA") + wallet := ts.AddGenesisWallet("default", nodeA) ts.Run(true, map[string][]options.Option[protocol.Protocol]{}) @@ -134,11 +133,12 @@ func Test_MultipleAttachments(t *testing.T) { // Create a transaction and issue it from both nodes, so that the conflict is accepted, but no attachment is included yet. { - tx1 := lo.PanicOnErr(ts.TransactionFramework.CreateSimpleTransaction("tx1", 2, "Genesis:0")) + tx1 := wallet.CreateBasicOutputsEquallyFromInputs("tx1", 2, "Genesis:0") - ts.IssuePayloadWithOptions("A.1", blockIssuerA, nodeA, tx1, mock.WithStrongParents(ts.BlockID("Genesis"))) + ts.IssuePayloadWithOptions("A.1", wallet, tx1, mock.WithStrongParents(ts.BlockID("Genesis"))) ts.IssueValidationBlock("A.1.1", nodeA, mock.WithStrongParents(ts.BlockID("A.1"))) - ts.IssuePayloadWithOptions("B.1", blockIssuerB, nodeB, tx1, mock.WithStrongParents(ts.BlockID("Genesis"))) + wallet.SetDefaultNode(nodeB) + ts.IssuePayloadWithOptions("B.1", wallet, tx1, mock.WithStrongParents(ts.BlockID("Genesis"))) ts.IssueValidationBlock("B.1.1", nodeB, mock.WithStrongParents(ts.BlockID("B.1"))) ts.IssueValidationBlock("A.2", nodeA, mock.WithStrongParents(ts.BlockID("B.1.1"))) @@ -154,17 +154,19 @@ func Test_MultipleAttachments(t *testing.T) { ts.Block("B.2"): {"tx1"}, }), ts.Nodes()...) ts.AssertTransactionInCacheConflicts(map[*iotago.Transaction][]string{ - ts.TransactionFramework.Transaction("tx1"): {"tx1"}, + wallet.Transaction("tx1"): {"tx1"}, }, ts.Nodes()...) ts.AssertConflictsInCacheAcceptanceState([]string{"tx1"}, acceptance.Accepted, ts.Nodes()...) } // Create a transaction that is included and whose conflict is accepted, but whose inputs are not accepted. { - tx2 := lo.PanicOnErr(ts.TransactionFramework.CreateSimpleTransaction("tx2", 1, "tx1:1")) + tx2 := wallet.CreateBasicOutputsEquallyFromInputs("tx2", 1, "tx1:1") - ts.IssuePayloadWithOptions("A.3", nodeA.Validator, nodeA, tx2, mock.WithStrongParents(ts.BlockID("Genesis"))) - ts.IssueValidationBlock("B.3", nodeB, mock.WithStrongParents(ts.BlockID("A.3"))) + wallet.SetDefaultNode(nodeA) + ts.IssuePayloadWithOptions("A.3", wallet, tx2, mock.WithStrongParents(ts.BlockID("Genesis"))) + ts.IssueValidationBlock("A.3.1", nodeA, mock.WithStrongParents(ts.BlockID("A.3"))) + ts.IssueValidationBlock("B.3", nodeB, mock.WithStrongParents(ts.BlockID("A.3.1"))) ts.IssueValidationBlock("A.4", nodeA, mock.WithStrongParents(ts.BlockID("B.3"))) ts.AssertBlocksInCachePreAccepted(ts.Blocks("A.3"), true, ts.Nodes()...) @@ -176,8 +178,8 @@ func Test_MultipleAttachments(t *testing.T) { ts.AssertBlocksInCachePreAccepted(ts.Blocks("B.4", "A.5"), false, ts.Nodes()...) ts.AssertBlocksInCacheAccepted(ts.Blocks("A.3"), true, ts.Nodes()...) - ts.AssertTransactionsInCacheBooked(ts.TransactionFramework.Transactions("tx1", "tx2"), true, ts.Nodes()...) - ts.AssertTransactionsInCachePending(ts.TransactionFramework.Transactions("tx1", "tx2"), true, ts.Nodes()...) + ts.AssertTransactionsInCacheBooked(wallet.Transactions("tx1", "tx2"), true, ts.Nodes()...) + ts.AssertTransactionsInCachePending(wallet.Transactions("tx1", "tx2"), true, ts.Nodes()...) ts.AssertBlocksInCacheConflicts(lo.MergeMaps(blocksConflicts, map[*blocks.Block][]string{ ts.Block("A.3"): {"tx2"}, @@ -187,8 +189,8 @@ func Test_MultipleAttachments(t *testing.T) { ts.Block("B.4"): {}, }), ts.Nodes()...) ts.AssertTransactionInCacheConflicts(map[*iotago.Transaction][]string{ - ts.TransactionFramework.Transaction("tx1"): {"tx1"}, - ts.TransactionFramework.Transaction("tx2"): {"tx2"}, + wallet.Transaction("tx1"): {"tx1"}, + wallet.Transaction("tx2"): {"tx2"}, }, nodeA, nodeB) ts.AssertConflictsInCacheAcceptanceState([]string{"tx1", "tx2"}, acceptance.Accepted, ts.Nodes()...) } @@ -205,9 +207,9 @@ func Test_MultipleAttachments(t *testing.T) { ts.AssertBlocksInCacheAccepted(ts.Blocks("A.1", "B.1"), true, ts.Nodes()...) ts.AssertBlocksInCachePreAccepted(ts.Blocks("A.7", "B.6"), false, ts.Nodes()...) - ts.AssertTransactionsExist(ts.TransactionFramework.Transactions("tx1", "tx2"), true, ts.Nodes()...) - ts.AssertTransactionsInCacheBooked(ts.TransactionFramework.Transactions("tx1", "tx2"), true, ts.Nodes()...) - ts.AssertTransactionsInCacheAccepted(ts.TransactionFramework.Transactions("tx1", "tx2"), true, ts.Nodes()...) + ts.AssertTransactionsExist(wallet.Transactions("tx1", "tx2"), true, ts.Nodes()...) + ts.AssertTransactionsInCacheBooked(wallet.Transactions("tx1", "tx2"), true, ts.Nodes()...) + ts.AssertTransactionsInCacheAccepted(wallet.Transactions("tx1", "tx2"), true, ts.Nodes()...) ts.AssertBlocksInCacheConflicts(lo.MergeMaps(blocksConflicts, map[*blocks.Block][]string{ ts.Block("A.6"): {}, @@ -217,8 +219,8 @@ func Test_MultipleAttachments(t *testing.T) { }), ts.Nodes()...) ts.AssertTransactionInCacheConflicts(map[*iotago.Transaction][]string{ - ts.TransactionFramework.Transaction("tx1"): {"tx1"}, - ts.TransactionFramework.Transaction("tx2"): {"tx2"}, + wallet.Transaction("tx1"): {"tx1"}, + wallet.Transaction("tx2"): {"tx2"}, }, nodeA, nodeB) ts.AssertConflictsInCacheAcceptanceState([]string{"tx1", "tx2"}, acceptance.Accepted, nodeA, nodeB) } @@ -245,7 +247,7 @@ func Test_SpendRejectedCommittedRace(t *testing.T) { node1 := ts.AddValidatorNode("node1") node2 := ts.AddValidatorNode("node2") - ts.AddBasicBlockIssuer("default") + wallet := ts.AddGenesisWallet("default", node1) ts.Run(true, map[string][]options.Option[protocol.Protocol]{}) @@ -261,16 +263,17 @@ func Test_SpendRejectedCommittedRace(t *testing.T) { // Create and issue double spends { - tx1 := lo.PanicOnErr(ts.TransactionFramework.CreateSimpleTransaction("tx1", 1, "Genesis:0")) - tx2 := lo.PanicOnErr(ts.TransactionFramework.CreateSimpleTransaction("tx2", 1, "Genesis:0")) + tx1 := wallet.CreateBasicOutputsEquallyFromInputs("tx1", 1, "Genesis:0") + tx2 := wallet.CreateBasicOutputsEquallyFromInputs("tx2", 1, "Genesis:0") - ts.IssueBasicBlockAtSlotWithOptions("block1.1", 1, genesisCommitment, ts.DefaultBasicBlockIssuer(), node1, tx1) - ts.IssueBasicBlockAtSlotWithOptions("block1.2", 1, genesisCommitment, ts.DefaultBasicBlockIssuer(), node1, tx2) + wallet.SetDefaultNode(node1) + ts.IssueBasicBlockAtSlotWithOptions("block1.1", 1, wallet, tx1, mock.WithSlotCommitment(genesisCommitment)) + ts.IssueBasicBlockAtSlotWithOptions("block1.2", 1, wallet, tx2, mock.WithSlotCommitment(genesisCommitment)) ts.IssueValidationBlockAtSlot("block2.tx1", 2, genesisCommitment, node1, ts.BlockIDs("block1.1")...) - ts.AssertTransactionsExist(ts.TransactionFramework.Transactions("tx1", "tx2"), true, node1, node2) - ts.AssertTransactionsInCacheBooked(ts.TransactionFramework.Transactions("tx1", "tx2"), true, node1, node2) - ts.AssertTransactionsInCachePending(ts.TransactionFramework.Transactions("tx1", "tx2"), true, node1, node2) + ts.AssertTransactionsExist(wallet.Transactions("tx1", "tx2"), true, node1, node2) + ts.AssertTransactionsInCacheBooked(wallet.Transactions("tx1", "tx2"), true, node1, node2) + ts.AssertTransactionsInCachePending(wallet.Transactions("tx1", "tx2"), true, node1, node2) ts.AssertBlocksInCacheConflicts(map[*blocks.Block][]string{ ts.Block("block1.1"): {"tx1"}, ts.Block("block1.2"): {"tx2"}, @@ -278,8 +281,8 @@ func Test_SpendRejectedCommittedRace(t *testing.T) { }, node1, node2) ts.AssertTransactionInCacheConflicts(map[*iotago.Transaction][]string{ - ts.TransactionFramework.Transaction("tx2"): {"tx2"}, - ts.TransactionFramework.Transaction("tx1"): {"tx1"}, + wallet.Transaction("tx2"): {"tx2"}, + wallet.Transaction("tx1"): {"tx1"}, }, node1, node2) } @@ -293,7 +296,7 @@ func Test_SpendRejectedCommittedRace(t *testing.T) { ts.Block("block2.2"): {"tx2"}, ts.Block("block2.tx1"): {"tx1"}, }, node1, node2) - ts.AssertTransactionsInCachePending(ts.TransactionFramework.Transactions("tx1", "tx2"), true, node1, node2) + ts.AssertTransactionsInCachePending(wallet.Transactions("tx1", "tx2"), true, node1, node2) } // Issue valid blocks that resolve the conflict. @@ -305,8 +308,8 @@ func Test_SpendRejectedCommittedRace(t *testing.T) { ts.Block("block2.3"): {"tx2"}, ts.Block("block2.tx1"): {"tx1"}, }, node1, node2) - ts.AssertTransactionsInCacheAccepted(ts.TransactionFramework.Transactions("tx2"), true, node1, node2) - ts.AssertTransactionsInCacheRejected(ts.TransactionFramework.Transactions("tx1"), true, node1, node2) + ts.AssertTransactionsInCacheAccepted(wallet.Transactions("tx2"), true, node1, node2) + ts.AssertTransactionsInCacheRejected(wallet.Transactions("tx1"), true, node1, node2) } // Advance both nodes at the edge of slot 1 committability @@ -365,20 +368,21 @@ func Test_SpendRejectedCommittedRace(t *testing.T) { commitment1 := lo.PanicOnErr(node2.Protocol.Engines.Main.Get().Storage.Commitments().Load(1)).Commitment() // This should be booked on the rejected tx1 conflict - tx4 := lo.PanicOnErr(ts.TransactionFramework.CreateSimpleTransaction("tx4", 1, "tx1:0")) + tx4 := wallet.CreateBasicOutputsEquallyFromInputs("tx4", 1, "tx1:0") // Issue TX3 on top of rejected TX1 and 1 commitment on node2 (committed to slot 1) { - ts.IssueBasicBlockAtSlotWithOptions("n2-commit1", 5, commitment1, ts.DefaultBasicBlockIssuer(), node2, tx4) + wallet.SetDefaultNode(node2) + ts.IssueBasicBlockAtSlotWithOptions("n2-commit1", 5, wallet, tx4, mock.WithSlotCommitment(commitment1)) ts.AssertBlocksInCacheConflicts(map[*blocks.Block][]string{ ts.Block("n2-commit1"): {}, // no conflits inherited as the block is invalid and doesn't get booked. ts.Block("block2.tx1"): {"tx1"}, }, node2) - ts.AssertTransactionsExist(ts.TransactionFramework.Transactions("tx1"), true, node2) - ts.AssertTransactionsInCacheRejected(ts.TransactionFramework.Transactions("tx4"), true, node2) - ts.AssertTransactionsInCacheBooked(ts.TransactionFramework.Transactions("tx4"), true, node2) + ts.AssertTransactionsExist(wallet.Transactions("tx1"), true, node2) + ts.AssertTransactionsInCacheRejected(wallet.Transactions("tx4"), true, node2) + ts.AssertTransactionsInCacheBooked(wallet.Transactions("tx4"), true, node2) // As the block commits to 1 but spending something orphaned in 1 it should be invalid ts.AssertBlocksInCacheBooked(ts.Blocks("n2-commit1"), false, node2) @@ -392,7 +396,7 @@ func Test_SpendRejectedCommittedRace(t *testing.T) { ts.AssertBlocksInCacheBooked(ts.Blocks("n1-rejected-genesis"), true, node1) ts.AssertBlocksInCacheInvalid(ts.Blocks("n1-rejected-genesis"), false, node1) - ts.AssertTransactionsInCacheRejected(ts.TransactionFramework.Transactions("tx1"), true, node2) + ts.AssertTransactionsInCacheRejected(wallet.Transactions("tx1"), true, node2) ts.AssertBlocksInCacheConflicts(map[*blocks.Block][]string{ ts.Block("block2.tx1"): {"tx1"}, @@ -402,7 +406,8 @@ func Test_SpendRejectedCommittedRace(t *testing.T) { // Issue TX4 on top of rejected TX1 but Genesis commitment on node2 (committed to slot 1) { - ts.IssueBasicBlockAtSlotWithOptions("n2-genesis", 5, genesisCommitment, ts.DefaultBasicBlockIssuer(), node2, tx4, mock.WithStrongParents(ts.BlockID("Genesis"))) + wallet.SetDefaultNode(node2) + ts.IssueBasicBlockAtSlotWithOptions("n2-genesis", 5, wallet, tx4, mock.WithStrongParents(ts.BlockID("Genesis")), mock.WithSlotCommitment(genesisCommitment)) ts.AssertBlocksInCacheConflicts(map[*blocks.Block][]string{ ts.Block("n2-genesis"): {"tx4"}, // on rejected conflict @@ -414,11 +419,12 @@ func Test_SpendRejectedCommittedRace(t *testing.T) { // Issue TX4 on top of rejected TX1 but Genesis commitment on node1 (committed to slot 0) { - ts.IssueBasicBlockAtSlotWithOptions("n1-genesis", 5, genesisCommitment, ts.DefaultBasicBlockIssuer(), node1, tx4, mock.WithStrongParents(ts.BlockID("Genesis"))) + wallet.SetDefaultNode(node1) + ts.IssueBasicBlockAtSlotWithOptions("n1-genesis", 5, wallet, tx4, mock.WithStrongParents(ts.BlockID("Genesis")), mock.WithSlotCommitment(genesisCommitment)) - ts.AssertTransactionsExist(ts.TransactionFramework.Transactions("tx1"), true, node2) - ts.AssertTransactionsInCacheRejected(ts.TransactionFramework.Transactions("tx4"), true, node2) - ts.AssertTransactionsInCacheBooked(ts.TransactionFramework.Transactions("tx4"), true, node2) + ts.AssertTransactionsExist(wallet.Transactions("tx1"), true, node2) + ts.AssertTransactionsInCacheRejected(wallet.Transactions("tx4"), true, node2) + ts.AssertTransactionsInCacheBooked(wallet.Transactions("tx4"), true, node2) ts.AssertBlocksInCacheConflicts(map[*blocks.Block][]string{ ts.Block("n1-genesis"): {"tx4"}, // on rejected conflict @@ -443,14 +449,16 @@ func Test_SpendRejectedCommittedRace(t *testing.T) { ) // Exchange each-other blocks, ignoring invalidity - ts.IssueExistingBlock("n2-genesis", ts.DefaultBasicBlockIssuer(), node1) - ts.IssueExistingBlock("n2-commit1", ts.DefaultBasicBlockIssuer(), node1) - ts.IssueExistingBlock("n1-genesis", ts.DefaultBasicBlockIssuer(), node2) - ts.IssueExistingBlock("n1-rejected-genesis", ts.DefaultBasicBlockIssuer(), node2) + wallet.SetDefaultNode(node1) + ts.IssueExistingBlock("n2-genesis", wallet) + ts.IssueExistingBlock("n2-commit1", wallet) + wallet.SetDefaultNode(node2) + ts.IssueExistingBlock("n1-genesis", wallet) + ts.IssueExistingBlock("n1-rejected-genesis", wallet) ts.IssueValidationBlockAtSlot("n1-rejected-commit1", 5, commitment1, node1, ts.BlockIDs("n1-rejected-genesis")...) // Needs reissuing on node2 because it is invalid - ts.IssueExistingBlock("n1-rejected-commit1", ts.DefaultBasicBlockIssuer(), node2) + ts.IssueExistingBlock("n1-rejected-commit1", wallet) // The nodes agree on the results of the invalid blocks ts.AssertBlocksInCacheBooked(ts.Blocks("n2-genesis", "n1-genesis", "n1-rejected-genesis"), true, node1, node2) @@ -475,7 +483,7 @@ func Test_SpendRejectedCommittedRace(t *testing.T) { // Commit further and test eviction of transactions { - ts.AssertTransactionsExist(ts.TransactionFramework.Transactions("tx1", "tx2", "tx4"), true, node1, node2) + ts.AssertTransactionsExist(wallet.Transactions("tx1", "tx2", "tx4"), true, node1, node2) ts.IssueBlocksAtSlots("", []iotago.SlotIndex{6, 7, 8, 9, 10}, 5, "5.1", ts.Nodes("node1", "node2"), false, nil) @@ -486,7 +494,7 @@ func Test_SpendRejectedCommittedRace(t *testing.T) { testsuite.WithEvictedSlot(8), ) - ts.AssertTransactionsExist(ts.TransactionFramework.Transactions("tx1", "tx2", "tx4"), false, node1, node2) + ts.AssertTransactionsExist(wallet.Transactions("tx1", "tx2", "tx4"), false, node1, node2) } } @@ -511,7 +519,7 @@ func Test_SpendPendingCommittedRace(t *testing.T) { node1 := ts.AddValidatorNode("node1") node2 := ts.AddValidatorNode("node2") - ts.AddBasicBlockIssuer("default") + wallet := ts.AddGenesisWallet("default", node1) ts.Run(true, map[string][]options.Option[protocol.Protocol]{}) @@ -524,23 +532,24 @@ func Test_SpendPendingCommittedRace(t *testing.T) { // Create and issue double spends { - tx1 := lo.PanicOnErr(ts.TransactionFramework.CreateSimpleTransaction("tx1", 1, "Genesis:0")) - tx2 := lo.PanicOnErr(ts.TransactionFramework.CreateSimpleTransaction("tx2", 1, "Genesis:0")) + tx1 := wallet.CreateBasicOutputsEquallyFromInputs("tx1", 1, "Genesis:0") + tx2 := wallet.CreateBasicOutputsEquallyFromInputs("tx2", 1, "Genesis:0") - ts.IssueBasicBlockAtSlotWithOptions("block1.1", 1, genesisCommitment, ts.DefaultBasicBlockIssuer(), node2, tx1) - ts.IssueBasicBlockAtSlotWithOptions("block1.2", 1, genesisCommitment, ts.DefaultBasicBlockIssuer(), node2, tx2) + wallet.SetDefaultNode(node2) + ts.IssueBasicBlockAtSlotWithOptions("block1.1", 1, wallet, tx1) + ts.IssueBasicBlockAtSlotWithOptions("block1.2", 1, wallet, tx2) - ts.AssertTransactionsExist(ts.TransactionFramework.Transactions("tx1", "tx2"), true, node1, node2) - ts.AssertTransactionsInCacheBooked(ts.TransactionFramework.Transactions("tx1", "tx2"), true, node1, node2) - ts.AssertTransactionsInCachePending(ts.TransactionFramework.Transactions("tx1", "tx2"), true, node1, node2) + ts.AssertTransactionsExist(wallet.Transactions("tx1", "tx2"), true, node1, node2) + ts.AssertTransactionsInCacheBooked(wallet.Transactions("tx1", "tx2"), true, node1, node2) + ts.AssertTransactionsInCachePending(wallet.Transactions("tx1", "tx2"), true, node1, node2) ts.AssertBlocksInCacheConflicts(map[*blocks.Block][]string{ ts.Block("block1.1"): {"tx1"}, ts.Block("block1.2"): {"tx2"}, }, node1, node2) ts.AssertTransactionInCacheConflicts(map[*iotago.Transaction][]string{ - ts.TransactionFramework.Transaction("tx2"): {"tx2"}, - ts.TransactionFramework.Transaction("tx1"): {"tx1"}, + wallet.Transaction("tx2"): {"tx2"}, + wallet.Transaction("tx1"): {"tx1"}, }, node1, node2) } @@ -553,7 +562,7 @@ func Test_SpendPendingCommittedRace(t *testing.T) { ts.Block("block2.1"): {"tx1"}, ts.Block("block2.2"): {"tx2"}, }, node1, node2) - ts.AssertTransactionsInCachePending(ts.TransactionFramework.Transactions("tx1", "tx2"), true, node1, node2) + ts.AssertTransactionsInCachePending(wallet.Transactions("tx1", "tx2"), true, node1, node2) } // Advance both nodes at the edge of slot 1 committability @@ -608,8 +617,8 @@ func Test_SpendPendingCommittedRace(t *testing.T) { ts.IssueValidationBlockAtSlot("n2-pending-genesis", 5, genesisCommitment, node2, ts.BlockIDs("block2.1")...) ts.IssueValidationBlockAtSlot("n2-pending-commit1", 5, commitment1, node2, ts.BlockIDs("block2.1")...) - ts.AssertTransactionsExist(ts.TransactionFramework.Transactions("tx1"), true, node2) - ts.AssertTransactionsInCachePending(ts.TransactionFramework.Transactions("tx1"), true, node2) + ts.AssertTransactionsExist(wallet.Transactions("tx1"), true, node2) + ts.AssertTransactionsInCachePending(wallet.Transactions("tx1"), true, node2) ts.AssertBlocksInCacheBooked(ts.Blocks("n2-pending-genesis", "n2-pending-commit1"), true, node2) ts.AssertBlocksInCacheInvalid(ts.Blocks("n2-pending-genesis", "n2-pending-commit1"), false, node2) @@ -636,8 +645,9 @@ func Test_SpendPendingCommittedRace(t *testing.T) { ) // Exchange each-other blocks, ignoring invalidity - ts.IssueExistingBlock("n2-pending-genesis", ts.DefaultBasicBlockIssuer(), node1) - ts.IssueExistingBlock("n2-pending-commit1", ts.DefaultBasicBlockIssuer(), node1) + wallet.SetDefaultNode(node1) + ts.IssueExistingBlock("n2-pending-genesis", wallet) + ts.IssueExistingBlock("n2-pending-commit1", wallet) // The nodes agree on the results of the invalid blocks ts.AssertBlocksInCacheBooked(ts.Blocks("n2-pending-genesis", "n2-pending-commit1"), true, node1, node2) @@ -649,13 +659,13 @@ func Test_SpendPendingCommittedRace(t *testing.T) { ts.Block("n2-pending-commit1"): {}, // no conflits inherited as the block merges orphaned conflicts. }, node1, node2) - ts.AssertTransactionsInCachePending(ts.TransactionFramework.Transactions("tx1", "tx2"), true, node1, node2) + ts.AssertTransactionsInCachePending(wallet.Transactions("tx1", "tx2"), true, node1, node2) } // Commit further and test eviction of transactions { - ts.AssertTransactionsExist(ts.TransactionFramework.Transactions("tx1", "tx2"), true, node1, node2) - ts.AssertTransactionsInCachePending(ts.TransactionFramework.Transactions("tx1", "tx2"), true, node1, node2) + ts.AssertTransactionsExist(wallet.Transactions("tx1", "tx2"), true, node1, node2) + ts.AssertTransactionsInCachePending(wallet.Transactions("tx1", "tx2"), true, node1, node2) ts.IssueBlocksAtSlots("", []iotago.SlotIndex{6, 7, 8, 9, 10}, 5, "5.1", ts.Nodes("node1", "node2"), false, nil) @@ -666,6 +676,6 @@ func Test_SpendPendingCommittedRace(t *testing.T) { testsuite.WithEvictedSlot(8), ) - ts.AssertTransactionsExist(ts.TransactionFramework.Transactions("tx1", "tx2"), false, node1, node2) + ts.AssertTransactionsExist(wallet.Transactions("tx1", "tx2"), false, node1, node2) } } diff --git a/pkg/tests/committee_rotation_test.go b/pkg/tests/committee_rotation_test.go new file mode 100644 index 000000000..375a3c98f --- /dev/null +++ b/pkg/tests/committee_rotation_test.go @@ -0,0 +1,116 @@ +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/sybilprotection/seatmanager/topstakers" + "github.com/iotaledger/iota-core/pkg/protocol/sybilprotection/sybilprotectionv1" + "github.com/iotaledger/iota-core/pkg/testsuite" + "github.com/iotaledger/iota-core/pkg/testsuite/snapshotcreator" + 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() + + node1 := 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) + ts.AddGenesisWallet("default", node1) + + 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.Wallet("node1")) + ts.IssueCandidacyAnnouncementInSlot("node4-candidacy:1", 5, "node1-candidacy:1", ts.Wallet("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.Wallet("node4")) + ts.IssueCandidacyAnnouncementInSlot("node5-candidacy:1", 9, "node4-candidacy:2", ts.Wallet("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.Wallet("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.Wallet("node2")) + ts.IssueCandidacyAnnouncementInSlot("node3-candidacy:1", 11, "node2-candidacy:1", ts.Wallet("node3")) + ts.IssueCandidacyAnnouncementInSlot("node4-candidacy:3", 11, "node3-candidacy:1", ts.Wallet("node3")) + ts.IssueCandidacyAnnouncementInSlot("node5-candidacy:2", 11, "node4-candidacy:3", ts.Wallet("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 b97c4b549..715bf4ca4 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.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(nodeA.Validator.AccountID))), + testsuite.WithSybilProtectionOnlineCommittee(lo.Return1(lo.Return1(nodeA.Protocol.Engines.Main.Get().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.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(nodeA.Validator.AccountID)), - lo.Return1(nodeA.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(nodeB.Validator.AccountID)), + lo.Return1(lo.Return1(nodeA.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(nodeA.Validator.AccountID)), + lo.Return1(lo.Return1(nodeA.Protocol.Engines.Main.Get().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.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(nodeA.Validator.AccountID)), - lo.Return1(nodeA.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(nodeB.Validator.AccountID)), - lo.Return1(nodeA.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(nodeC.Validator.AccountID)), + lo.Return1(lo.Return1(nodeA.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(nodeA.Validator.AccountID)), + lo.Return1(lo.Return1(nodeA.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(nodeB.Validator.AccountID)), + lo.Return1(lo.Return1(nodeA.Protocol.Engines.Main.Get().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.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(nodeA.Validator.AccountID)), - lo.Return1(nodeA.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(nodeB.Validator.AccountID)), - lo.Return1(nodeA.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(nodeC.Validator.AccountID)), + lo.Return1(lo.Return1(nodeA.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(nodeA.Validator.AccountID)), + lo.Return1(lo.Return1(nodeA.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(nodeB.Validator.AccountID)), + lo.Return1(lo.Return1(nodeA.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(nodeC.Validator.AccountID)), ), testsuite.WithEvictedSlot(3), ) diff --git a/pkg/tests/loss_of_acceptance_test.go b/pkg/tests/loss_of_acceptance_test.go index 7a078023e..347c9804b 100644 --- a/pkg/tests/loss_of_acceptance_test.go +++ b/pkg/tests/loss_of_acceptance_test.go @@ -33,16 +33,14 @@ func TestLossOfAcceptanceFromGenesis(t *testing.T) { ) defer ts.Shutdown() - ts.AddBasicBlockIssuer("default") node0 := ts.AddValidatorNode("node0") - node1 := ts.AddValidatorNode("node1") - node2 := ts.AddNode("node2") + ts.AddGenesisWallet("default", node0) + ts.AddValidatorNode("node1") + ts.AddNode("node2") ts.Run(true, nil) node0.Protocol.SetLogLevel(log.LevelTrace) - node1.Protocol.SetLogLevel(log.LevelTrace) - node2.Protocol.SetLogLevel(log.LevelTrace) // Create snapshot to use later. snapshotPath := ts.Directory.Path(fmt.Sprintf("%d_snapshot", time.Now().Unix())) @@ -127,8 +125,8 @@ func TestLossOfAcceptanceFromSnapshot(t *testing.T) { ) defer ts.Shutdown() - ts.AddBasicBlockIssuer("default") node0 := ts.AddValidatorNode("node0") + ts.AddGenesisWallet("default", node0) ts.AddValidatorNode("node1") node2 := ts.AddNode("node2") @@ -221,8 +219,8 @@ func TestLossOfAcceptanceWithRestartFromDisk(t *testing.T) { ) defer ts.Shutdown() - ts.AddBasicBlockIssuer("default") node0 := ts.AddValidatorNode("node0") + ts.AddGenesisWallet("default", node0) ts.AddValidatorNode("node1") node2 := ts.AddNode("node2") diff --git a/pkg/tests/protocol_engine_rollback_test.go b/pkg/tests/protocol_engine_rollback_test.go index c7647fde4..abf7b8d8e 100644 --- a/pkg/tests/protocol_engine_rollback_test.go +++ b/pkg/tests/protocol_engine_rollback_test.go @@ -64,7 +64,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]{ @@ -98,10 +97,10 @@ func TestProtocol_EngineRollbackFinalization(t *testing.T) { node3.Validator.AccountID, } expectedOnlineCommitteeFull := []account.SeatIndex{ - lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(node0.Validator.AccountID)), - lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(node1.Validator.AccountID)), - lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(node2.Validator.AccountID)), - lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(node3.Validator.AccountID)), + lo.Return1(lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node0.Validator.AccountID)), + lo.Return1(lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node1.Validator.AccountID)), + lo.Return1(lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node2.Validator.AccountID)), + lo.Return1(lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node3.Validator.AccountID)), } for _, node := range ts.Nodes() { @@ -137,7 +136,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), ) @@ -163,7 +162,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), ) @@ -279,15 +278,15 @@ func TestProtocol_EngineRollbackNoFinalization(t *testing.T) { node3.Validator.AccountID, } expectedOnlineCommitteeFull := []account.SeatIndex{ - lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(node0.Validator.AccountID)), - lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(node1.Validator.AccountID)), - lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(node2.Validator.AccountID)), - lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(node3.Validator.AccountID)), + lo.Return1(lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node0.Validator.AccountID)), + lo.Return1(lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node1.Validator.AccountID)), + lo.Return1(lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node2.Validator.AccountID)), + lo.Return1(lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node3.Validator.AccountID)), } expectedOnlineCommitteeHalf := []account.SeatIndex{ - lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(node0.Validator.AccountID)), - lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(node1.Validator.AccountID)), + lo.Return1(lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node0.Validator.AccountID)), + lo.Return1(lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node1.Validator.AccountID)), } for _, node := range ts.Nodes() { @@ -314,7 +313,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) @@ -323,7 +322,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), ) @@ -356,7 +355,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), ) @@ -472,15 +471,15 @@ func TestProtocol_EngineRollbackNoFinalizationLastSlot(t *testing.T) { node3.Validator.AccountID, } expectedOnlineCommitteeFull := []account.SeatIndex{ - lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(node0.Validator.AccountID)), - lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(node1.Validator.AccountID)), - lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(node2.Validator.AccountID)), - lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(node3.Validator.AccountID)), + lo.Return1(lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node0.Validator.AccountID)), + lo.Return1(lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node1.Validator.AccountID)), + lo.Return1(lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node2.Validator.AccountID)), + lo.Return1(lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node3.Validator.AccountID)), } expectedOnlineCommitteeHalf := []account.SeatIndex{ - lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(node0.Validator.AccountID)), - lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(node1.Validator.AccountID)), + lo.Return1(lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node0.Validator.AccountID)), + lo.Return1(lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node1.Validator.AccountID)), } for _, node := range ts.Nodes() { @@ -516,7 +515,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), ) @@ -549,7 +548,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), ) @@ -665,15 +664,15 @@ func TestProtocol_EngineRollbackNoFinalizationBeforePointOfNoReturn(t *testing.T node3.Validator.AccountID, } expectedOnlineCommitteeFull := []account.SeatIndex{ - lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(node0.Validator.AccountID)), - lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(node1.Validator.AccountID)), - lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(node2.Validator.AccountID)), - lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(node3.Validator.AccountID)), + lo.Return1(lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node0.Validator.AccountID)), + lo.Return1(lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node1.Validator.AccountID)), + lo.Return1(lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node2.Validator.AccountID)), + lo.Return1(lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node3.Validator.AccountID)), } expectedOnlineCommitteeHalf := []account.SeatIndex{ - lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(node0.Validator.AccountID)), - lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(node1.Validator.AccountID)), + lo.Return1(lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node0.Validator.AccountID)), + lo.Return1(lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node1.Validator.AccountID)), } for _, node := range ts.Nodes() { @@ -709,7 +708,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), ) @@ -742,7 +741,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 c811207d2..938155599 100644 --- a/pkg/tests/protocol_engine_switching_test.go +++ b/pkg/tests/protocol_engine_switching_test.go @@ -56,7 +56,7 @@ func TestProtocol_EngineSwitching(t *testing.T) { node6 := ts.AddValidatorNode("node6") node7 := ts.AddValidatorNode("node7") node8 := ts.AddNode("node8") - ts.AddBasicBlockIssuer("default", iotago.MaxBlockIssuanceCredits/2) + ts.AddGenesisWallet("default", node0, iotago.MaxBlockIssuanceCredits/2) const expectedCommittedSlotAfterPartitionMerge = 18 nodesP1 := []*mock.Node{node0, node1, node2, node3, node4, node5} @@ -128,15 +128,15 @@ func TestProtocol_EngineSwitching(t *testing.T) { node7.Validator.AccountID, } expectedP1OnlineCommittee := []account.SeatIndex{ - lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(node0.Validator.AccountID)), - lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(node1.Validator.AccountID)), - lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(node2.Validator.AccountID)), - lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(node3.Validator.AccountID)), - lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(node4.Validator.AccountID)), + lo.Return1(lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node0.Validator.AccountID)), + lo.Return1(lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node1.Validator.AccountID)), + lo.Return1(lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node2.Validator.AccountID)), + lo.Return1(lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node3.Validator.AccountID)), + lo.Return1(lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node4.Validator.AccountID)), } expectedP2OnlineCommittee := []account.SeatIndex{ - lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(node6.Validator.AccountID)), - lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(node7.Validator.AccountID)), + lo.Return1(lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node6.Validator.AccountID)), + lo.Return1(lo.Return1(node0.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(node7.Validator.AccountID)), } expectedOnlineCommittee := append(expectedP1OnlineCommittee, expectedP2OnlineCommittee...) @@ -174,7 +174,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), ) @@ -229,7 +229,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), ) @@ -283,7 +283,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 c6a6bbf66..e8ff3f841 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.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(nodeA.Validator.AccountID)), + lo.Return1(lo.Return1(nodeA.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(nodeA.Validator.AccountID)), } // Verify that nodes have the expected states. @@ -141,7 +141,7 @@ func Test_StartNodeFromSnapshotAndDisk(t *testing.T) { nodeA := ts.AddValidatorNode("nodeA") nodeB := ts.AddValidatorNode("nodeB") ts.AddNode("nodeC") - ts.AddBasicBlockIssuer("default", iotago.MaxBlockIssuanceCredits/2) + ts.AddGenesisWallet("default", nodeA, iotago.MaxBlockIssuanceCredits/2) nodeOptions := []options.Option[protocol.Protocol]{ protocol.WithStorageOptions( @@ -168,8 +168,8 @@ func Test_StartNodeFromSnapshotAndDisk(t *testing.T) { } expectedOnlineCommittee := []account.SeatIndex{ - lo.Return1(nodeA.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(nodeA.Validator.AccountID)), - lo.Return1(nodeA.Protocol.Engines.Main.Get().SybilProtection.SeatManager().Committee(1).GetSeat(nodeB.Validator.AccountID)), + lo.Return1(lo.Return1(nodeA.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(1)).GetSeat(nodeA.Validator.AccountID)), + lo.Return1(lo.Return1(nodeA.Protocol.Engines.Main.Get().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/tests/upgrade_signaling_test.go b/pkg/tests/upgrade_signaling_test.go index b2e65976c..c8e1f1308 100644 --- a/pkg/tests/upgrade_signaling_test.go +++ b/pkg/tests/upgrade_signaling_test.go @@ -21,6 +21,7 @@ import ( "github.com/iotaledger/iota-core/pkg/storage" "github.com/iotaledger/iota-core/pkg/storage/permanent" "github.com/iotaledger/iota-core/pkg/testsuite" + "github.com/iotaledger/iota-core/pkg/testsuite/mock" iotago "github.com/iotaledger/iota.go/v4" "github.com/iotaledger/iota.go/v4/api" ) @@ -100,13 +101,13 @@ func Test_Upgrade_Signaling(t *testing.T) { ), ) - ts.AddValidatorNode("nodeA") + nodeA := ts.AddValidatorNode("nodeA") ts.AddValidatorNode("nodeB") ts.AddValidatorNode("nodeC") ts.AddValidatorNode("nodeD") ts.AddNode("nodeE") ts.AddNode("nodeF") - ts.AddBasicBlockIssuer("default", iotago.MaxBlockIssuanceCredits/2) + wallet := ts.AddGenesisWallet("default", nodeA, iotago.MaxBlockIssuanceCredits/2) ts.Run(true, map[string][]options.Option[protocol.Protocol]{ "nodeA": nodeOptionsWithoutV5, @@ -128,7 +129,7 @@ func Test_Upgrade_Signaling(t *testing.T) { ExpirySlot: iotago.MaxSlotIndex, OutputID: ts.AccountOutput("Genesis:1").OutputID(), BlockIssuerKeys: iotago.NewBlockIssuerKeys(iotago.Ed25519PublicKeyBlockIssuerKeyFromPublicKey(ed25519.PublicKey(ts.Node("nodeA").Validator.PublicKey))), - ValidatorStake: testsuite.MinValidatorAccountAmount, + ValidatorStake: mock.MinValidatorAccountAmount, DelegationStake: 0, FixedCost: 0, StakeEndEpoch: iotago.MaxEpochIndex, @@ -136,11 +137,11 @@ func Test_Upgrade_Signaling(t *testing.T) { }, ts.Nodes()...) ts.AssertAccountData(&accounts.AccountData{ - ID: ts.DefaultBasicBlockIssuer().AccountID, + ID: wallet.BlockIssuer.AccountID, Credits: &accounts.BlockIssuanceCredits{Value: iotago.MaxBlockIssuanceCredits / 2, UpdateTime: 0}, ExpirySlot: iotago.MaxSlotIndex, OutputID: ts.AccountOutput("Genesis:5").OutputID(), - BlockIssuerKeys: iotago.NewBlockIssuerKeys(iotago.Ed25519PublicKeyBlockIssuerKeyFromPublicKey(ed25519.PublicKey(ts.DefaultBasicBlockIssuer().PublicKey))), + BlockIssuerKeys: iotago.NewBlockIssuerKeys(iotago.Ed25519PublicKeyBlockIssuerKeyFromPublicKey(ed25519.PublicKey(wallet.BlockIssuer.PublicKey))), ValidatorStake: 0, DelegationStake: 0, FixedCost: 0, @@ -162,7 +163,7 @@ func Test_Upgrade_Signaling(t *testing.T) { ExpirySlot: iotago.MaxSlotIndex, OutputID: ts.AccountOutput("Genesis:1").OutputID(), BlockIssuerKeys: iotago.NewBlockIssuerKeys(iotago.Ed25519PublicKeyBlockIssuerKeyFromPublicKey(ed25519.PublicKey(ts.Node("nodeA").Validator.PublicKey))), - ValidatorStake: testsuite.MinValidatorAccountAmount, + ValidatorStake: mock.MinValidatorAccountAmount, DelegationStake: 0, FixedCost: 0, StakeEndEpoch: iotago.MaxEpochIndex, @@ -175,7 +176,7 @@ func Test_Upgrade_Signaling(t *testing.T) { ExpirySlot: iotago.MaxSlotIndex, OutputID: ts.AccountOutput("Genesis:4").OutputID(), BlockIssuerKeys: iotago.NewBlockIssuerKeys(iotago.Ed25519PublicKeyBlockIssuerKeyFromPublicKey(ed25519.PublicKey(ts.Node("nodeD").Validator.PublicKey))), - ValidatorStake: testsuite.MinValidatorAccountAmount, + ValidatorStake: mock.MinValidatorAccountAmount, DelegationStake: 0, FixedCost: 0, StakeEndEpoch: iotago.MaxEpochIndex, @@ -196,7 +197,7 @@ func Test_Upgrade_Signaling(t *testing.T) { ExpirySlot: iotago.MaxSlotIndex, OutputID: ts.AccountOutput("Genesis:1").OutputID(), BlockIssuerKeys: iotago.NewBlockIssuerKeys(iotago.Ed25519PublicKeyBlockIssuerKeyFromPublicKey(ed25519.PublicKey(ts.Node("nodeA").Validator.PublicKey))), - ValidatorStake: testsuite.MinValidatorAccountAmount, + ValidatorStake: mock.MinValidatorAccountAmount, DelegationStake: 0, FixedCost: 0, StakeEndEpoch: iotago.MaxEpochIndex, @@ -361,7 +362,7 @@ func Test_Upgrade_Signaling(t *testing.T) { ExpirySlot: iotago.MaxSlotIndex, OutputID: ts.AccountOutput("Genesis:1").OutputID(), BlockIssuerKeys: iotago.NewBlockIssuerKeys(iotago.Ed25519PublicKeyBlockIssuerKeyFromPublicKey(ed25519.PublicKey(ts.Node("nodeA").Validator.PublicKey))), - ValidatorStake: testsuite.MinValidatorAccountAmount, + ValidatorStake: mock.MinValidatorAccountAmount, DelegationStake: 0, FixedCost: 0, StakeEndEpoch: iotago.MaxEpochIndex, @@ -374,7 +375,7 @@ func Test_Upgrade_Signaling(t *testing.T) { ExpirySlot: iotago.MaxSlotIndex, OutputID: ts.AccountOutput("Genesis:4").OutputID(), BlockIssuerKeys: iotago.NewBlockIssuerKeys(iotago.Ed25519PublicKeyBlockIssuerKeyFromPublicKey(ed25519.PublicKey(ts.Node("nodeD").Validator.PublicKey))), - ValidatorStake: testsuite.MinValidatorAccountAmount, + ValidatorStake: mock.MinValidatorAccountAmount, DelegationStake: 0, FixedCost: 0, StakeEndEpoch: iotago.MaxEpochIndex, diff --git a/pkg/testsuite/blocks.go b/pkg/testsuite/blocks.go index 3b77f6193..66a905ad8 100644 --- a/pkg/testsuite/blocks.go +++ b/pkg/testsuite/blocks.go @@ -130,7 +130,7 @@ func (t *TestSuite) AssertBlocksInCacheConflicts(blockConflicts map[*blocks.Bloc return ierrors.Errorf("AssertBlocksInCacheConflicts: %s: block %s is root block", node.Name, blockFromCache.ID()) } - expectedConflictIDs := ds.NewSet(lo.Map(conflictAliases, t.TransactionFramework.TransactionID)...) + expectedConflictIDs := ds.NewSet(lo.Map(conflictAliases, t.DefaultWallet().TransactionID)...) actualConflictIDs := blockFromCache.ConflictIDs() if expectedConflictIDs.Size() != actualConflictIDs.Size() { diff --git a/pkg/testsuite/conflicts.go b/pkg/testsuite/conflicts.go index 5a996cfc0..adf2f81cb 100644 --- a/pkg/testsuite/conflicts.go +++ b/pkg/testsuite/conflicts.go @@ -13,7 +13,7 @@ func (t *TestSuite) AssertConflictsInCacheAcceptanceState(expectedConflictAliase for _, node := range nodes { for _, conflictAlias := range expectedConflictAliases { t.Eventually(func() error { - acceptanceState := node.Protocol.Engines.Main.Get().Ledger.ConflictDAG().AcceptanceState(ds.NewSet(t.TransactionFramework.TransactionID(conflictAlias))) + acceptanceState := node.Protocol.Engines.Main.Get().Ledger.ConflictDAG().AcceptanceState(ds.NewSet(t.DefaultWallet().TransactionID(conflictAlias))) if acceptanceState != expectedState { return ierrors.Errorf("assertTransactionsInCacheWithFunc: %s: conflict %s is %s, but expected %s", node.Name, conflictAlias, acceptanceState, expectedState) diff --git a/pkg/testsuite/mock/blockissuer.go b/pkg/testsuite/mock/blockissuer.go index d2b82d11a..5d91997c9 100644 --- a/pkg/testsuite/mock/blockissuer.go +++ b/pkg/testsuite/mock/blockissuer.go @@ -12,6 +12,7 @@ import ( "golang.org/x/crypto/blake2b" "github.com/iotaledger/hive.go/core/safemath" + hiveEd25519 "github.com/iotaledger/hive.go/crypto/ed25519" "github.com/iotaledger/hive.go/ierrors" "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/runtime/event" @@ -45,14 +46,15 @@ type BlockIssuer struct { Name string Validator bool + AccountID iotago.AccountID + OutputID iotago.OutputID + PublicKey ed25519.PublicKey + privateKey ed25519.PrivateKey + events *Events workerPool *workerpool.WorkerPool - privateKey ed25519.PrivateKey - PublicKey ed25519.PublicKey - AccountID iotago.AccountID - optsTipSelectionTimeout time.Duration optsTipSelectionRetryInterval time.Duration // optsIncompleteBlockAccepted defines whether the node allows filling in incomplete block and issuing it for user. @@ -60,13 +62,12 @@ type BlockIssuer struct { optsRateSetterEnabled bool } -func NewBlockIssuer(t *testing.T, name string, validator bool, opts ...options.Option[BlockIssuer]) *BlockIssuer { - pub, priv, err := ed25519.GenerateKey(nil) - if err != nil { - panic(err) - } +func NewBlockIssuer(t *testing.T, name string, keyManager *KeyManager, accountID iotago.AccountID, validator bool, opts ...options.Option[BlockIssuer]) *BlockIssuer { + priv, pub := keyManager.KeyPair() - accountID := iotago.AccountID(blake2b.Sum256(pub)) + if accountID == iotago.EmptyAccountID { + accountID = iotago.AccountID(blake2b.Sum256(pub)) + } accountID.RegisterAlias(name) return options.Apply(&BlockIssuer{ @@ -85,6 +86,10 @@ func NewBlockIssuer(t *testing.T, name string, validator bool, opts ...options.O }, opts) } +func (i *BlockIssuer) BlockIssuerKeys() iotago.BlockIssuerKeys { + return iotago.NewBlockIssuerKeys(iotago.Ed25519PublicKeyBlockIssuerKeyFromPublicKey(hiveEd25519.PublicKey(i.PublicKey))) +} + // Shutdown shuts down the block issuer. func (i *BlockIssuer) Shutdown() { i.workerPool.Shutdown() @@ -120,8 +125,7 @@ func (i *BlockIssuer) CreateValidationBlock(ctx context.Context, alias string, i } if blockParams.BlockHeader.References == nil { - // TODO: change this to get references for validator block - references, err := i.getReferences(ctx, nil, node, blockParams.BlockHeader.ParentsCount) + references, err := i.getReferencesValidationBlock(ctx, node, blockParams.BlockHeader.ParentsCount) require.NoError(i.Testing, err) blockParams.BlockHeader.References = references @@ -150,7 +154,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 { @@ -188,7 +192,7 @@ func (i *BlockIssuer) IssueValidationBlock(ctx context.Context, alias string, no validationBlock, _ := block.ValidationBlock() - node.Protocol.Engines.Main.Get().LogTrace("issued validation block", "blockID", block.ID(), "slot", block.ID().Slot(), "commitment", block.SlotCommitmentID(), "latestFinalizedSlot", block.ProtocolBlock().LatestFinalizedSlot, "version", block.ProtocolBlock().ProtocolVersion, "highestSupportedVersion", validationBlock.HighestSupportedVersion, "hash", validationBlock.ProtocolParametersHash) + node.Protocol.Engines.Main.Get().LogTrace("issued validation block", "blockID", block.ID(), "slot", block.ID().Slot(), "commitment", block.SlotCommitmentID(), "latestFinalizedSlot", block.ProtocolBlock().Header.LatestFinalizedSlot, "version", block.ProtocolBlock().Header.ProtocolVersion, "highestSupportedVersion", validationBlock.HighestSupportedVersion, "hash", validationBlock.ProtocolParametersHash) return block } @@ -221,7 +225,7 @@ func (i *BlockIssuer) CreateBasicBlock(ctx context.Context, alias string, node * } if blockParams.BlockHeader.References == nil { - references, err := i.getReferences(ctx, blockParams.Payload, node, blockParams.BlockHeader.ParentsCount) + references, err := i.getReferencesBasicBlock(ctx, node, blockParams.BlockHeader.ParentsCount) require.NoError(i.Testing, err) blockParams.BlockHeader.References = references } @@ -283,7 +287,7 @@ func (i *BlockIssuer) IssueBasicBlock(ctx context.Context, alias string, node *N require.NoErrorf(i.Testing, i.IssueBlock(block.ModelBlock(), node), "%s > failed to issue block with alias %s", i.Name, alias) - node.Protocol.LogTrace("issued block", "blockID", block.ID(), "slot", block.ID().Slot(), "commitment", block.SlotCommitmentID(), "latestFinalizedSlot", block.ProtocolBlock().LatestFinalizedSlot, "version", block.ProtocolBlock().ProtocolVersion) + node.Protocol.LogTrace("issued block", "blockID", block.ID(), "slot", block.ID().Slot(), "commitment", block.SlotCommitmentID(), "latestFinalizedSlot", block.ProtocolBlock().Header.LatestFinalizedSlot, "version", block.ProtocolBlock().Header.ProtocolVersion) return block } @@ -361,31 +365,31 @@ func (i *BlockIssuer) IssueBlockAndAwaitEvent(ctx context.Context, block *model. } } -func (i *BlockIssuer) AttachBlock(ctx context.Context, iotaBlock *iotago.ProtocolBlock, node *Node, optIssuerAccount ...Account) (iotago.BlockID, error) { +func (i *BlockIssuer) AttachBlock(ctx context.Context, iotaBlock *iotago.Block, node *Node, optIssuerAccount ...Account) (iotago.BlockID, error) { // if anything changes, need to make a new signature var resign bool - apiForVersion, err := node.Protocol.APIForVersion(iotaBlock.ProtocolVersion) + apiForVersion, err := node.Protocol.APIForVersion(iotaBlock.Header.ProtocolVersion) if err != nil { - return iotago.EmptyBlockID, ierrors.Wrapf(ErrBlockAttacherInvalidBlock, "protocolVersion invalid: %d", iotaBlock.ProtocolVersion) + return iotago.EmptyBlockID, ierrors.Wrapf(ErrBlockAttacherInvalidBlock, "protocolVersion invalid: %d", iotaBlock.Header.ProtocolVersion) } protoParams := apiForVersion.ProtocolParameters() - if iotaBlock.NetworkID == 0 { - iotaBlock.NetworkID = protoParams.NetworkID() + if iotaBlock.Header.NetworkID == 0 { + iotaBlock.Header.NetworkID = protoParams.NetworkID() resign = true } - if iotaBlock.SlotCommitmentID == iotago.EmptyCommitmentID { - iotaBlock.SlotCommitmentID = node.Protocol.Engines.Main.Get().Storage.Settings().LatestCommitment().Commitment().MustID() - iotaBlock.LatestFinalizedSlot = node.Protocol.Engines.Main.Get().Storage.Settings().LatestFinalizedSlot() + if iotaBlock.Header.SlotCommitmentID == iotago.EmptyCommitmentID { + iotaBlock.Header.SlotCommitmentID = node.Protocol.Engines.Main.Get().Storage.Settings().LatestCommitment().Commitment().MustID() + iotaBlock.Header.LatestFinalizedSlot = node.Protocol.Engines.Main.Get().Storage.Settings().LatestFinalizedSlot() resign = true } - switch innerBlock := iotaBlock.Block.(type) { - case *iotago.BasicBlock: - switch payload := innerBlock.Payload.(type) { + switch blockBody := iotaBlock.Body.(type) { + case *iotago.BasicBlockBody: + switch payload := blockBody.Payload.(type) { case *iotago.SignedTransaction: if payload.Transaction.NetworkID != protoParams.NetworkID() { return iotago.EmptyBlockID, ierrors.Wrapf(ErrBlockAttacherInvalidBlock, "invalid payload, error: wrong networkID: %d", payload.Transaction.NetworkID) @@ -393,39 +397,46 @@ func (i *BlockIssuer) AttachBlock(ctx context.Context, iotaBlock *iotago.Protoco } if len(iotaBlock.Parents()) == 0 { - references, referencesErr := i.getReferences(ctx, innerBlock.Payload, node) + references, referencesErr := i.getReferencesBasicBlock(ctx, node) if referencesErr != nil { return iotago.EmptyBlockID, ierrors.Wrapf(ErrBlockAttacherAttachingNotPossible, "tipselection failed, error: %w", referencesErr) } - innerBlock.StrongParents = references[iotago.StrongParentType] - innerBlock.WeakParents = references[iotago.WeakParentType] - innerBlock.ShallowLikeParents = references[iotago.ShallowLikeParentType] + blockBody.StrongParents = references[iotago.StrongParentType] + blockBody.WeakParents = references[iotago.WeakParentType] + blockBody.ShallowLikeParents = references[iotago.ShallowLikeParentType] resign = true } - case *iotago.ValidationBlock: - //nolint:revive,staticcheck //temporarily disable + case *iotago.ValidationBlockBody: if len(iotaBlock.Parents()) == 0 { - // TODO: implement tipselection for validator blocks + references, referencesErr := i.getReferencesValidationBlock(ctx, node) + if referencesErr != nil { + return iotago.EmptyBlockID, ierrors.Wrapf(ErrBlockAttacherAttachingNotPossible, "tipselection failed, error: %w", referencesErr) + } + + blockBody.StrongParents = references[iotago.StrongParentType] + blockBody.WeakParents = references[iotago.WeakParentType] + blockBody.ShallowLikeParents = references[iotago.ShallowLikeParentType] + resign = true } } references := make(model.ParentReferences) - references[iotago.StrongParentType] = iotaBlock.Block.StrongParentIDs().RemoveDupsAndSort() - references[iotago.WeakParentType] = iotaBlock.Block.WeakParentIDs().RemoveDupsAndSort() - references[iotago.ShallowLikeParentType] = iotaBlock.Block.ShallowLikeParentIDs().RemoveDupsAndSort() - if iotaBlock.IssuingTime.Equal(time.Unix(0, 0)) { - iotaBlock.IssuingTime = time.Now().UTC() + references[iotago.StrongParentType] = iotaBlock.Body.StrongParentIDs().RemoveDupsAndSort() + references[iotago.WeakParentType] = iotaBlock.Body.WeakParentIDs().RemoveDupsAndSort() + references[iotago.ShallowLikeParentType] = iotaBlock.Body.ShallowLikeParentIDs().RemoveDupsAndSort() + if iotaBlock.Header.IssuingTime.Equal(time.Unix(0, 0)) { + iotaBlock.Header.IssuingTime = time.Now().UTC() resign = true } - if err = i.validateReferences(iotaBlock.IssuingTime, iotaBlock.SlotCommitmentID.Slot(), references, node); err != nil { + if err = i.validateReferences(iotaBlock.Header.IssuingTime, iotaBlock.Header.SlotCommitmentID.Slot(), references, node); err != nil { return iotago.EmptyBlockID, ierrors.Wrapf(ErrBlockAttacherAttachingNotPossible, "invalid block references, error: %w", err) } - if basicBlock, isBasicBlock := iotaBlock.Block.(*iotago.BasicBlock); isBasicBlock && basicBlock.MaxBurnedMana == 0 { - rmcSlot, err := safemath.SafeSub(apiForVersion.TimeProvider().SlotFromTime(iotaBlock.IssuingTime), apiForVersion.ProtocolParameters().MaxCommittableAge()) + if basicBlock, isBasicBlock := iotaBlock.Body.(*iotago.BasicBlockBody); isBasicBlock && basicBlock.MaxBurnedMana == 0 { + rmcSlot, err := safemath.SafeSub(apiForVersion.TimeProvider().SlotFromTime(iotaBlock.Header.IssuingTime), apiForVersion.ProtocolParameters().MaxCommittableAge()) if err != nil { rmcSlot = 0 } @@ -442,10 +453,10 @@ func (i *BlockIssuer) AttachBlock(ctx context.Context, iotaBlock *iotago.Protoco resign = true } - if iotaBlock.IssuerID.Empty() || resign { + if iotaBlock.Header.IssuerID.Empty() || resign { if i.optsIncompleteBlockAccepted && len(optIssuerAccount) > 0 { issuerAccount := optIssuerAccount[0] - iotaBlock.IssuerID = issuerAccount.ID() + iotaBlock.Header.IssuerID = issuerAccount.ID() signature, signatureErr := iotaBlock.Sign(iotago.NewAddressKeysForEd25519Address(issuerAccount.Address().(*iotago.Ed25519Address), issuerAccount.PrivateKey())) if signatureErr != nil { @@ -468,7 +479,7 @@ func (i *BlockIssuer) AttachBlock(ctx context.Context, iotaBlock *iotago.Protoco return iotago.EmptyBlockID, ierrors.Wrap(err, "error serializing block to model block") } - if !i.optsRateSetterEnabled || node.Protocol.Engines.Main.Get().Scheduler.IsBlockIssuerReady(modelBlock.ProtocolBlock().IssuerID) { + if !i.optsRateSetterEnabled || node.Protocol.Engines.Main.Get().Scheduler.IsBlockIssuerReady(modelBlock.ProtocolBlock().Header.IssuerID) { i.events.BlockConstructed.Trigger(modelBlock) if err = i.IssueBlockAndAwaitEvent(ctx, modelBlock, node, node.Protocol.Events.Engine.BlockDAG.BlockAttached); err != nil { @@ -539,13 +550,22 @@ func (i *BlockIssuer) getAddressableCommitment(currentAPI iotago.API, blockIssui return commitment, nil } -func (i *BlockIssuer) getReferences(ctx context.Context, p iotago.Payload, node *Node, strongParentsCountOpt ...int) (model.ParentReferences, error) { - strongParentsCount := iotago.BlockMaxParents +func (i *BlockIssuer) getReferencesBasicBlock(ctx context.Context, node *Node, strongParentsCountOpt ...int) (model.ParentReferences, error) { + strongParentsCount := iotago.BasicBlockMaxParents + if len(strongParentsCountOpt) > 0 && strongParentsCountOpt[0] > 0 { + strongParentsCount = strongParentsCountOpt[0] + } + + return i.getReferencesWithRetry(ctx, strongParentsCount, node) +} + +func (i *BlockIssuer) getReferencesValidationBlock(ctx context.Context, node *Node, strongParentsCountOpt ...int) (model.ParentReferences, error) { + strongParentsCount := iotago.ValidationBlockMaxParents if len(strongParentsCountOpt) > 0 && strongParentsCountOpt[0] > 0 { strongParentsCount = strongParentsCountOpt[0] } - return i.getReferencesWithRetry(ctx, p, strongParentsCount, node) + return i.getReferencesWithRetry(ctx, strongParentsCount, node) } func (i *BlockIssuer) validateReferences(issuingTime time.Time, slotCommitmentIndex iotago.SlotIndex, references model.ParentReferences, node *Node) error { @@ -589,7 +609,7 @@ func (i *BlockIssuer) CopyIdentityFromBlockIssuer(otherBlockIssuer *BlockIssuer) // getReferencesWithRetry tries to get references for the given payload. If it fails, it will retry at regular intervals until // the timeout is reached. -func (i *BlockIssuer) getReferencesWithRetry(ctx context.Context, _ iotago.Payload, parentsCount int, node *Node) (references model.ParentReferences, err error) { +func (i *BlockIssuer) getReferencesWithRetry(ctx context.Context, parentsCount int, node *Node) (references model.ParentReferences, err error) { timeout := time.NewTimer(i.optsTipSelectionTimeout) interval := time.NewTicker(i.optsTipSelectionRetryInterval) defer timeutil.CleanupTimer(timeout) diff --git a/pkg/testsuite/mock/hdwallet.go b/pkg/testsuite/mock/hdwallet.go deleted file mode 100644 index a4e1ceb4e..000000000 --- a/pkg/testsuite/mock/hdwallet.go +++ /dev/null @@ -1,138 +0,0 @@ -package mock - -import ( - "crypto/ed25519" - "fmt" - - "github.com/wollac/iota-crypto-demo/pkg/bip32path" - "github.com/wollac/iota-crypto-demo/pkg/slip10" - "github.com/wollac/iota-crypto-demo/pkg/slip10/eddsa" - - "github.com/iotaledger/hive.go/ierrors" - "github.com/iotaledger/iota-core/pkg/protocol/engine/utxoledger" - iotago "github.com/iotaledger/iota.go/v4" -) - -const ( - pathString = "44'/4218'/0'/%d'" -) - -type HDWallet struct { - name string - seed []byte - index uint64 - utxo []*utxoledger.Output -} - -func NewHDWallet(name string, seed []byte, index uint64) *HDWallet { - return &HDWallet{ - name: name, - seed: seed, - index: index, - utxo: make([]*utxoledger.Output, 0), - } -} - -func (hd *HDWallet) BookSpents(spentOutputs []*utxoledger.Output) { - for _, spent := range spentOutputs { - hd.BookSpent(spent) - } -} - -func (hd *HDWallet) BookSpent(spentOutput *utxoledger.Output) { - newUtxo := make([]*utxoledger.Output, 0) - for _, u := range hd.utxo { - if u.OutputID() == spentOutput.OutputID() { - fmt.Printf("%s spent %s\n", hd.name, u.OutputID().ToHex()) - - continue - } - newUtxo = append(newUtxo, u) - } - hd.utxo = newUtxo -} - -func (hd *HDWallet) Name() string { - return hd.name -} - -func (hd *HDWallet) Balance() iotago.BaseToken { - var balance iotago.BaseToken - for _, u := range hd.utxo { - balance += u.BaseTokenAmount() - } - - return balance -} - -func (hd *HDWallet) BookOutput(output *utxoledger.Output) { - if output != nil { - fmt.Printf("%s book %s\n", hd.name, output.OutputID().ToHex()) - hd.utxo = append(hd.utxo, output) - } -} - -// KeyPair calculates an ed25519 key pair by using slip10. -func (hd *HDWallet) KeyPair() (ed25519.PrivateKey, ed25519.PublicKey) { - path, err := bip32path.ParsePath(fmt.Sprintf(pathString, hd.index)) - if err != nil { - panic(err) - } - - curve := eddsa.Ed25519() - key, err := slip10.DeriveKeyFromPath(hd.seed, curve, path) - if err != nil { - panic(err) - } - - pubKey, privKey := key.Key.(eddsa.Seed).Ed25519Key() - - return ed25519.PrivateKey(privKey), ed25519.PublicKey(pubKey) -} - -func (hd *HDWallet) AddressSigner() iotago.AddressSigner { - privKey, pubKey := hd.KeyPair() - address := iotago.Ed25519AddressFromPubKey(pubKey) - - return iotago.NewInMemoryAddressSigner(iotago.NewAddressKeysForEd25519Address(address, privKey)) -} - -func (hd *HDWallet) Outputs() []*utxoledger.Output { - return hd.utxo -} - -// Address calculates an ed25519 address by using slip10. -func (hd *HDWallet) Address(addressType ...iotago.AddressType) iotago.DirectUnlockableAddress { - _, pubKey := hd.KeyPair() - - addrType := iotago.AddressEd25519 - if len(addressType) > 0 { - addrType = addressType[0] - } - - switch addrType { - case iotago.AddressEd25519: - return iotago.Ed25519AddressFromPubKey(pubKey) - case iotago.AddressImplicitAccountCreation: - return iotago.ImplicitAccountCreationAddressFromPubKey(pubKey) - default: - panic(ierrors.Wrapf(iotago.ErrUnknownAddrType, "type %d", addressType)) - } -} - -func (hd *HDWallet) PrintStatus() { - var status string - status += fmt.Sprintf("Name: %s\n", hd.name) - status += fmt.Sprintf("Address: %s\n", hd.Address().Bech32(iotago.PrefixTestnet)) - status += fmt.Sprintf("Balance: %d\n", hd.Balance()) - status += "Outputs: \n" - for _, u := range hd.utxo { - nativeTokenDescription := "" - nativeTokenFeature := u.Output().FeatureSet().NativeToken() - if nativeTokenFeature != nil { - nativeTokenDescription += fmt.Sprintf("%s: %s, ", nativeTokenFeature.ID.ToHex(), nativeTokenFeature.Amount) - } - status += fmt.Sprintf("\t%s [%s] = %d %v\n", u.OutputID().ToHex(), u.OutputType(), u.BaseTokenAmount(), nativeTokenDescription) - } - fmt.Printf("%s\n", status) -} diff --git a/pkg/testsuite/mock/keymanager.go b/pkg/testsuite/mock/keymanager.go new file mode 100644 index 000000000..236a50551 --- /dev/null +++ b/pkg/testsuite/mock/keymanager.go @@ -0,0 +1,75 @@ +package mock + +import ( + "crypto/ed25519" + "fmt" + + "github.com/wollac/iota-crypto-demo/pkg/bip32path" + "github.com/wollac/iota-crypto-demo/pkg/slip10" + "github.com/wollac/iota-crypto-demo/pkg/slip10/eddsa" + + "github.com/iotaledger/hive.go/ierrors" + iotago "github.com/iotaledger/iota.go/v4" +) + +const ( + pathString = "44'/4218'/0'/%d'" +) + +// KeyManager is a hierarchical deterministic key manager. +type KeyManager struct { + seed []byte + index uint64 +} + +func NewKeyManager(seed []byte, index uint64) *KeyManager { + return &KeyManager{ + seed: seed, + index: index, + } +} + +// KeyPair calculates an ed25519 key pair by using slip10. +func (k *KeyManager) KeyPair() (ed25519.PrivateKey, ed25519.PublicKey) { + path, err := bip32path.ParsePath(fmt.Sprintf(pathString, k.index)) + if err != nil { + panic(err) + } + + curve := eddsa.Ed25519() + key, err := slip10.DeriveKeyFromPath(k.seed, curve, path) + if err != nil { + panic(err) + } + + pubKey, privKey := key.Key.(eddsa.Seed).Ed25519Key() + + return ed25519.PrivateKey(privKey), ed25519.PublicKey(pubKey) +} + +// AddressSigner returns an address signer. +func (k *KeyManager) AddressSigner() iotago.AddressSigner { + privKey, pubKey := k.KeyPair() + + // add both address types for simplicity in tests + ed25519Address := iotago.Ed25519AddressFromPubKey(pubKey) + ed25519AddressKey := iotago.NewAddressKeysForEd25519Address(ed25519Address, privKey) + implicitAccountCreationAddress := iotago.ImplicitAccountCreationAddressFromPubKey(pubKey) + implicitAccountCreationAddressKey := iotago.NewAddressKeysForImplicitAccountCreationAddress(implicitAccountCreationAddress, privKey) + + return iotago.NewInMemoryAddressSigner(ed25519AddressKey, implicitAccountCreationAddressKey) +} + +// Address calculates an address of the specified type. +func (k *KeyManager) Address(addressType iotago.AddressType) iotago.DirectUnlockableAddress { + _, pubKey := k.KeyPair() + + switch addressType { + case iotago.AddressEd25519: + return iotago.Ed25519AddressFromPubKey(pubKey) + case iotago.AddressImplicitAccountCreation: + return iotago.ImplicitAccountCreationAddressFromPubKey(pubKey) + default: + panic(ierrors.Wrapf(iotago.ErrUnknownAddrType, "type %d", addressType)) + } +} diff --git a/pkg/testsuite/mock/node.go b/pkg/testsuite/mock/node.go index a4ad97d5e..241dd375c 100644 --- a/pkg/testsuite/mock/node.go +++ b/pkg/testsuite/mock/node.go @@ -2,7 +2,6 @@ package mock import ( "context" - "crypto/ed25519" "fmt" "sync/atomic" "testing" @@ -29,6 +28,7 @@ import ( "github.com/iotaledger/iota-core/pkg/protocol/engine/mempool" "github.com/iotaledger/iota-core/pkg/protocol/engine/notarization" iotago "github.com/iotaledger/iota.go/v4" + "github.com/iotaledger/iota.go/v4/tpkg" ) // idAliases contains a list of aliases registered for a set of IDs. @@ -48,8 +48,9 @@ func UnregisterIDAliases() { type Node struct { Testing *testing.T - Name string - Validator *BlockIssuer + Name string + Validator *BlockIssuer + KeyManager *KeyManager ctx context.Context ctxCancel context.CancelFunc @@ -75,10 +76,9 @@ type Node struct { } func NewNode(t *testing.T, net *Network, partition string, name string, validator bool) *Node { - pub, priv, err := ed25519.GenerateKey(nil) - if err != nil { - panic(err) - } + seed := tpkg.RandEd25519Seed() + keyManager := NewKeyManager(seed[:], 0) + priv, pub := keyManager.KeyPair() accountID := iotago.AccountID(blake2b.Sum256(pub)) accountID.RegisterAlias(name) @@ -88,7 +88,7 @@ func NewNode(t *testing.T, net *Network, partition string, name string, validato var validatorBlockIssuer *BlockIssuer if validator { - validatorBlockIssuer = NewBlockIssuer(t, name, validator) + validatorBlockIssuer = NewBlockIssuer(t, name, keyManager, accountID, validator) } else { validatorBlockIssuer = nil } @@ -98,7 +98,8 @@ func NewNode(t *testing.T, net *Network, partition string, name string, validato Name: name, - Validator: validatorBlockIssuer, + Validator: validatorBlockIssuer, + KeyManager: keyManager, PeerID: peerID, @@ -307,19 +308,19 @@ func (n *Node) attachEngineLogsWithName(failOnBlockFiltered bool, instance *engi }) events.BlockGadget.BlockPreAccepted.Hook(func(block *blocks.Block) { - fmt.Printf("%s > [%s] Consensus.BlockGadget.BlockPreAccepted: %s %s\n", n.Name, engineName, block.ID(), block.ProtocolBlock().SlotCommitmentID) + fmt.Printf("%s > [%s] Consensus.BlockGadget.BlockPreAccepted: %s %s\n", n.Name, engineName, block.ID(), block.ProtocolBlock().Header.SlotCommitmentID) }) events.BlockGadget.BlockAccepted.Hook(func(block *blocks.Block) { - fmt.Printf("%s > [%s] Consensus.BlockGadget.BlockAccepted: %s @ slot %s committing to %s\n", n.Name, engineName, block.ID(), block.ID().Slot(), block.ProtocolBlock().SlotCommitmentID) + fmt.Printf("%s > [%s] Consensus.BlockGadget.BlockAccepted: %s @ slot %s committing to %s\n", n.Name, engineName, block.ID(), block.ID().Slot(), block.ProtocolBlock().Header.SlotCommitmentID) }) events.BlockGadget.BlockPreConfirmed.Hook(func(block *blocks.Block) { - fmt.Printf("%s > [%s] Consensus.BlockGadget.BlockPreConfirmed: %s %s\n", n.Name, engineName, block.ID(), block.ProtocolBlock().SlotCommitmentID) + fmt.Printf("%s > [%s] Consensus.BlockGadget.BlockPreConfirmed: %s %s\n", n.Name, engineName, block.ID(), block.ProtocolBlock().Header.SlotCommitmentID) }) events.BlockGadget.BlockConfirmed.Hook(func(block *blocks.Block) { - fmt.Printf("%s > [%s] Consensus.BlockGadget.BlockConfirmed: %s %s\n", n.Name, engineName, block.ID(), block.ProtocolBlock().SlotCommitmentID) + fmt.Printf("%s > [%s] Consensus.BlockGadget.BlockConfirmed: %s %s\n", n.Name, engineName, block.ID(), block.ProtocolBlock().Header.SlotCommitmentID) }) events.SlotGadget.SlotFinalized.Hook(func(slot iotago.SlotIndex) { @@ -334,6 +335,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) }) @@ -464,3 +469,11 @@ func (n *Node) AttachedBlocks() []*blocks.Block { return n.attachedBlocks } + +func (n *Node) IssueValidationBlock(ctx context.Context, alias string, opts ...options.Option[ValidatorBlockParams]) *blocks.Block { + if n.Validator == nil { + panic("node is not a validator") + } + + return n.Validator.IssueValidationBlock(ctx, alias, n, opts...) +} diff --git a/pkg/testsuite/mock/utils.go b/pkg/testsuite/mock/utils.go new file mode 100644 index 000000000..9689781dc --- /dev/null +++ b/pkg/testsuite/mock/utils.go @@ -0,0 +1,213 @@ +package mock + +import ( + "github.com/iotaledger/hive.go/runtime/options" + "github.com/iotaledger/iota-core/pkg/protocol/engine/utxoledger" + iotago "github.com/iotaledger/iota.go/v4" + "github.com/iotaledger/iota.go/v4/builder" +) + +const MinIssuerAccountAmount = iotago.BaseToken(372900) +const MinValidatorAccountAmount = iotago.BaseToken(722800) +const AccountConversionManaCost = iotago.Mana(1000000) + +// TransactionBuilder options + +func WithInputs(inputs utxoledger.Outputs) options.Option[builder.TransactionBuilder] { + return func(txBuilder *builder.TransactionBuilder) { + for _, input := range inputs { + switch input.OutputType() { + case iotago.OutputFoundry: + // For foundries we need to unlock the account output + txBuilder.AddInput(&builder.TxInput{ + UnlockTarget: input.Output().UnlockConditionSet().ImmutableAccount().Address, + InputID: input.OutputID(), + Input: input.Output(), + }) + case iotago.OutputAnchor: + // For anchor outputs we need to unlock the state controller + txBuilder.AddInput(&builder.TxInput{ + UnlockTarget: input.Output().UnlockConditionSet().StateControllerAddress().Address, + InputID: input.OutputID(), + Input: input.Output(), + }) + default: + txBuilder.AddInput(&builder.TxInput{ + UnlockTarget: input.Output().UnlockConditionSet().Address().Address, + InputID: input.OutputID(), + Input: input.Output(), + }) + } + } + } +} + +func WithAccountInput(input *utxoledger.Output) options.Option[builder.TransactionBuilder] { + return func(txBuilder *builder.TransactionBuilder) { + switch input.OutputType() { + case iotago.OutputAccount: + address := input.Output().UnlockConditionSet().Address().Address + + txBuilder.AddInput(&builder.TxInput{ + UnlockTarget: address, + InputID: input.OutputID(), + Input: input.Output(), + }) + default: + panic("only OutputAccount can be added as account input") + } + } +} + +func WithAllotments(allotments iotago.Allotments) options.Option[builder.TransactionBuilder] { + return func(txBuilder *builder.TransactionBuilder) { + for _, allotment := range allotments { + txBuilder.IncreaseAllotment(allotment.AccountID, allotment.Mana) + } + } +} + +func WithSlotCreated(creationSlot iotago.SlotIndex) options.Option[builder.TransactionBuilder] { + return func(txBuilder *builder.TransactionBuilder) { + txBuilder.SetCreationSlot(creationSlot) + } +} + +func WithContextInputs(contextInputs iotago.TxEssenceContextInputs) options.Option[builder.TransactionBuilder] { + return func(txBuilder *builder.TransactionBuilder) { + for _, input := range contextInputs { + txBuilder.AddContextInput(input) + } + } +} + +func WithOutputs(outputs iotago.Outputs[iotago.Output]) options.Option[builder.TransactionBuilder] { + return func(txBuilder *builder.TransactionBuilder) { + for _, output := range outputs { + txBuilder.AddOutput(output) + } + } +} + +func WithTaggedDataPayload(payload *iotago.TaggedData) options.Option[builder.TransactionBuilder] { + return func(txBuilder *builder.TransactionBuilder) { + txBuilder.AddTaggedDataPayload(payload) + } +} + +// DelegationOutput options + +func WithDelegatedAmount(delegatedAmount iotago.BaseToken) options.Option[builder.DelegationOutputBuilder] { + return func(delegationBuilder *builder.DelegationOutputBuilder) { + delegationBuilder.DelegatedAmount(delegatedAmount) + } +} + +func WithDelegatedValidatorAddress(validatorAddress *iotago.AccountAddress) options.Option[builder.DelegationOutputBuilder] { + return func(delegationBuilder *builder.DelegationOutputBuilder) { + delegationBuilder.ValidatorAddress(validatorAddress) + } +} + +func WithDelegationStartEpoch(startEpoch iotago.EpochIndex) options.Option[builder.DelegationOutputBuilder] { + return func(delegationBuilder *builder.DelegationOutputBuilder) { + delegationBuilder.StartEpoch(startEpoch) + } +} + +func WithDelegationEndEpoch(endEpoch iotago.EpochIndex) options.Option[builder.DelegationOutputBuilder] { + return func(delegationBuilder *builder.DelegationOutputBuilder) { + delegationBuilder.EndEpoch(endEpoch) + } +} + +func WithDelegationConditions(delegationConditions iotago.DelegationOutputUnlockConditions) options.Option[builder.DelegationOutputBuilder] { + return func(delegationBuilder *builder.DelegationOutputBuilder) { + delegationBuilder.Address(delegationConditions.MustSet().Address().Address) + } +} + +func WithDelegationAmount(amount iotago.BaseToken) options.Option[builder.DelegationOutputBuilder] { + return func(delegationBuilder *builder.DelegationOutputBuilder) { + delegationBuilder.Amount(amount) + } +} + +// BlockIssuer options + +func WithBlockIssuerFeature(keys iotago.BlockIssuerKeys, expirySlot iotago.SlotIndex) options.Option[builder.AccountOutputBuilder] { + return func(accountBuilder *builder.AccountOutputBuilder) { + accountBuilder.BlockIssuer(keys, expirySlot) + } +} + +func WithAddBlockIssuerKey(key iotago.BlockIssuerKey) options.Option[builder.AccountOutputBuilder] { + return func(accountBuilder *builder.AccountOutputBuilder) { + accountBuilder.BlockIssuerTransition().AddKeys(key) + } +} + +func WithBlockIssuerKeys(keys iotago.BlockIssuerKeys) options.Option[builder.AccountOutputBuilder] { + return func(accountBuilder *builder.AccountOutputBuilder) { + accountBuilder.BlockIssuerTransition().Keys(keys) + } +} + +func WithBlockIssuerExpirySlot(expirySlot iotago.SlotIndex) options.Option[builder.AccountOutputBuilder] { + return func(accountBuilder *builder.AccountOutputBuilder) { + accountBuilder.BlockIssuerTransition().ExpirySlot(expirySlot) + } +} + +func WithStakingFeature(amount iotago.BaseToken, fixedCost iotago.Mana, startEpoch iotago.EpochIndex, optEndEpoch ...iotago.EpochIndex) options.Option[builder.AccountOutputBuilder] { + return func(accountBuilder *builder.AccountOutputBuilder) { + accountBuilder.Staking(amount, fixedCost, startEpoch, optEndEpoch...) + } +} + +// Account options + +func WithAccountMana(mana iotago.Mana) options.Option[builder.AccountOutputBuilder] { + return func(accountBuilder *builder.AccountOutputBuilder) { + accountBuilder.Mana(mana) + } +} + +func WithAccountAmount(amount iotago.BaseToken) options.Option[builder.AccountOutputBuilder] { + return func(accountBuilder *builder.AccountOutputBuilder) { + accountBuilder.Amount(amount) + } +} + +func WithAccountIncreasedFoundryCounter(diff uint32) options.Option[builder.AccountOutputBuilder] { + return func(accountBuilder *builder.AccountOutputBuilder) { + accountBuilder.FoundriesToGenerate(diff) + } +} + +func WithAccountImmutableFeatures(features iotago.AccountOutputImmFeatures) options.Option[builder.AccountOutputBuilder] { + return func(accountBuilder *builder.AccountOutputBuilder) { + for _, feature := range features.MustSet() { + switch feature.Type() { + case iotago.FeatureMetadata: + //nolint:forcetypeassert + accountBuilder.ImmutableMetadata(feature.(*iotago.MetadataFeature).Data) + case iotago.FeatureSender: + //nolint:forcetypeassert + accountBuilder.ImmutableSender(feature.(*iotago.SenderFeature).Address) + } + } + } +} + +func WithAccountConditions(conditions iotago.AccountOutputUnlockConditions) options.Option[builder.AccountOutputBuilder] { + return func(accountBuilder *builder.AccountOutputBuilder) { + for _, condition := range conditions.MustSet() { + switch condition.Type() { + case iotago.UnlockConditionAddress: + //nolint:forcetypeassert + accountBuilder.Address(condition.(*iotago.AddressUnlockCondition).Address) + } + } + } +} diff --git a/pkg/testsuite/mock/wallet.go b/pkg/testsuite/mock/wallet.go new file mode 100644 index 000000000..b161ed1af --- /dev/null +++ b/pkg/testsuite/mock/wallet.go @@ -0,0 +1,131 @@ +package mock + +import ( + "crypto/ed25519" + "testing" + + "github.com/iotaledger/hive.go/ierrors" + "github.com/iotaledger/hive.go/lo" + "github.com/iotaledger/iota-core/pkg/protocol/engine/utxoledger" + iotago "github.com/iotaledger/iota.go/v4" + "github.com/iotaledger/iota.go/v4/tpkg" +) + +// Wallet is an object representing a wallet (similar to a FireFly wallet) capable of the following: +// - hierarchical deterministic key management +// - signing transactions +// - signing blocks +// - keeping track of unspent outputs. +type Wallet struct { + Testing *testing.T + + Name string + + Node *Node + + keyManager *KeyManager + + BlockIssuer *BlockIssuer + + outputs map[string]*utxoledger.Output + transactions map[string]*iotago.Transaction +} + +func NewWallet(t *testing.T, name string, node *Node, keyManager ...*KeyManager) *Wallet { + var km *KeyManager + if len(keyManager) == 0 { + randomSeed := tpkg.RandEd25519Seed() + km = NewKeyManager(randomSeed[:], 0) + } else { + km = keyManager[0] + } + + return &Wallet{ + Testing: t, + Name: name, + Node: node, + outputs: make(map[string]*utxoledger.Output), + transactions: make(map[string]*iotago.Transaction), + keyManager: km, + } +} + +func (w *Wallet) SetBlockIssuer(accountID iotago.AccountID) { + w.BlockIssuer = NewBlockIssuer(w.Testing, w.Name, w.keyManager, accountID, false) +} + +func (w *Wallet) SetDefaultNode(node *Node) { + w.Node = node +} + +func (w *Wallet) AddOutput(outputName string, output *utxoledger.Output) { + w.outputs[outputName] = output +} + +func (w *Wallet) Balance() iotago.BaseToken { + var balance iotago.BaseToken + for _, output := range w.outputs { + balance += output.BaseTokenAmount() + } + + return balance +} + +func (w *Wallet) Output(outputName string) *utxoledger.Output { + output, exists := w.outputs[outputName] + if !exists { + panic(ierrors.Errorf("output %s not registered in wallet %s", outputName, w.Name)) + } + + return output +} + +func (w *Wallet) AccountOutput(outputName string) *utxoledger.Output { + output := w.Output(outputName) + if _, ok := output.Output().(*iotago.AccountOutput); !ok { + panic(ierrors.Errorf("output %s is not an account output", outputName)) + } + + return output +} + +func (w *Wallet) Transaction(alias string) *iotago.Transaction { + transaction, exists := w.transactions[alias] + if !exists { + panic(ierrors.Errorf("transaction with given alias does not exist %s", alias)) + } + + return transaction +} + +func (w *Wallet) Transactions(transactionNames ...string) []*iotago.Transaction { + return lo.Map(transactionNames, w.Transaction) +} + +func (w *Wallet) TransactionID(alias string) iotago.TransactionID { + return lo.PanicOnErr(w.Transaction(alias).ID()) +} + +func (w *Wallet) Address() iotago.DirectUnlockableAddress { + address := w.keyManager.Address(iotago.AddressEd25519) + //nolint:forcetypeassert + return address.(*iotago.Ed25519Address) +} + +func (w *Wallet) ImplicitAccountCreationAddress() *iotago.ImplicitAccountCreationAddress { + address := w.keyManager.Address(iotago.AddressImplicitAccountCreation) + //nolint:forcetypeassert + return address.(*iotago.ImplicitAccountCreationAddress) +} + +func (w *Wallet) HasAddress(address iotago.Address) bool { + return address.Equal(w.Address()) || address.Equal(w.ImplicitAccountCreationAddress()) +} + +func (w *Wallet) KeyPair() (ed25519.PrivateKey, ed25519.PublicKey) { + return w.keyManager.KeyPair() +} + +func (w *Wallet) AddressSigner() iotago.AddressSigner { + return w.keyManager.AddressSigner() +} diff --git a/pkg/testsuite/mock/wallet_blocks.go b/pkg/testsuite/mock/wallet_blocks.go new file mode 100644 index 000000000..22fe8bea4 --- /dev/null +++ b/pkg/testsuite/mock/wallet_blocks.go @@ -0,0 +1,12 @@ +package mock + +import ( + "context" + + "github.com/iotaledger/hive.go/runtime/options" + "github.com/iotaledger/iota-core/pkg/protocol/engine/blocks" +) + +func (w *Wallet) IssueBasicBlock(ctx context.Context, blockName string, opts ...options.Option[BasicBlockParams]) *blocks.Block { + return w.BlockIssuer.IssueBasicBlock(ctx, blockName, w.Node, opts...) +} diff --git a/pkg/testsuite/mock/wallet_transactions.go b/pkg/testsuite/mock/wallet_transactions.go new file mode 100644 index 000000000..a939751d1 --- /dev/null +++ b/pkg/testsuite/mock/wallet_transactions.go @@ -0,0 +1,362 @@ +package mock + +import ( + "fmt" + "time" + + "github.com/iotaledger/hive.go/ierrors" + "github.com/iotaledger/hive.go/lo" + "github.com/iotaledger/hive.go/runtime/options" + "github.com/iotaledger/iota-core/pkg/protocol/engine/utxoledger" + iotago "github.com/iotaledger/iota.go/v4" + "github.com/iotaledger/iota.go/v4/builder" + "github.com/iotaledger/iota.go/v4/tpkg" +) + +// Functionality for creating transactions in the mock wallet. + +func (w *Wallet) CreateAccountFromInput(transactionName string, inputName string, recipientWallet *Wallet, creationSlot iotago.SlotIndex, opts ...options.Option[builder.AccountOutputBuilder]) *iotago.SignedTransaction { + input := w.Output(inputName) + + accountOutput := options.Apply(builder.NewAccountOutputBuilder(recipientWallet.Address(), input.BaseTokenAmount()). + Mana(input.StoredMana()), + opts).MustBuild() + + outputStates := iotago.Outputs[iotago.Output]{accountOutput} + + // if amount was set by options, a remainder output needs to be created + if accountOutput.Amount != input.BaseTokenAmount() { + remainderOutput := &iotago.BasicOutput{ + Amount: input.BaseTokenAmount() - accountOutput.Amount, + Mana: input.StoredMana() - accountOutput.Mana, + Conditions: iotago.BasicOutputUnlockConditions{ + &iotago.AddressUnlockCondition{Address: recipientWallet.Address()}, + }, + Features: iotago.BasicOutputFeatures{}, + } + outputStates = append(outputStates, remainderOutput) + } + + signedTransaction := lo.PanicOnErr(w.createSignedTransactionWithOptions( + transactionName, + WithContextInputs(iotago.TxEssenceContextInputs{ + &iotago.CommitmentInput{ + CommitmentID: w.Node.Protocol.Engines.Main.Get().Storage.Settings().LatestCommitment().Commitment().MustID(), + }, + }), + WithInputs(utxoledger.Outputs{input}), + WithOutputs(outputStates), + WithSlotCreated(creationSlot), + )) + + // register the outputs in the recipient wallet (so wallet doesn't have to scan for outputs on its addresses) + recipientWallet.registerOutputs(transactionName, signedTransaction.Transaction) + + return signedTransaction +} + +// CreateDelegationFromInput creates a new DelegationOutput with given options from an input. If the remainder Output +// is not created, then StoredMana from the input is not passed and can potentially be burned. +// In order not to burn it, it needs to be assigned manually in another output in the transaction. +func (w *Wallet) CreateDelegationFromInput(transactionName string, inputName string, creationSlot iotago.SlotIndex, opts ...options.Option[builder.DelegationOutputBuilder]) *iotago.SignedTransaction { + input := w.Output(inputName) + + delegationOutput := options.Apply(builder.NewDelegationOutputBuilder(&iotago.AccountAddress{}, w.Address(), input.BaseTokenAmount()). + DelegatedAmount(input.BaseTokenAmount()), + opts).MustBuild() + + if delegationOutput.ValidatorAddress.AccountID() == iotago.EmptyAccountID || + delegationOutput.DelegatedAmount == 0 || + delegationOutput.StartEpoch == 0 { + panic(fmt.Sprintf("delegation output created incorrectly %+v", delegationOutput)) + } + + outputStates := iotago.Outputs[iotago.Output]{delegationOutput} + + // if options set an Amount, a remainder output needs to be created + if delegationOutput.Amount != input.BaseTokenAmount() { + outputStates = append(outputStates, &iotago.BasicOutput{ + Amount: input.BaseTokenAmount() - delegationOutput.Amount, + Mana: input.StoredMana(), + Conditions: iotago.BasicOutputUnlockConditions{ + &iotago.AddressUnlockCondition{Address: w.Address()}, + }, + Features: iotago.BasicOutputFeatures{}, + }) + } + + // create the signed transaction + signedTransaction := lo.PanicOnErr(w.createSignedTransactionWithOptions( + transactionName, + WithContextInputs(iotago.TxEssenceContextInputs{ + &iotago.CommitmentInput{ + CommitmentID: w.Node.Protocol.Engines.Main.Get().Storage.Settings().LatestCommitment().Commitment().MustID(), + }, + }), + WithInputs(utxoledger.Outputs{input}), + WithOutputs(outputStates), + WithSlotCreated(creationSlot), + )) + + return signedTransaction +} + +// DelayedClaimingTransition transitions DelegationOutput into delayed claiming state by setting DelegationID and EndEpoch. +func (w *Wallet) DelayedClaimingTransition(transactionName string, inputName string, creationSlot iotago.SlotIndex, delegationEndEpoch iotago.EpochIndex) *iotago.SignedTransaction { + input := w.Output(inputName) + if input.OutputType() != iotago.OutputDelegation { + panic(ierrors.Errorf("%s is not a delegation output, cannot transition to delayed claiming state", inputName)) + } + + prevOutput, ok := input.Output().Clone().(*iotago.DelegationOutput) + if !ok { + panic(ierrors.Errorf("cloned output %s is not a delegation output, cannot transition to delayed claiming state", inputName)) + } + + delegationBuilder := builder.NewDelegationOutputBuilderFromPrevious(prevOutput).EndEpoch(delegationEndEpoch) + if prevOutput.DelegationID == iotago.EmptyDelegationID() { + delegationBuilder.DelegationID(iotago.DelegationIDFromOutputID(input.OutputID())) + } + + delegationOutput := delegationBuilder.MustBuild() + + signedTransaction := lo.PanicOnErr(w.createSignedTransactionWithOptions( + transactionName, + WithContextInputs(iotago.TxEssenceContextInputs{ + &iotago.CommitmentInput{ + CommitmentID: w.Node.Protocol.Engines.Main.Get().Storage.Settings().LatestCommitment().Commitment().MustID(), + }, + }), + WithInputs(utxoledger.Outputs{input}), + WithOutputs(iotago.Outputs[iotago.Output]{delegationOutput}), + WithSlotCreated(creationSlot), + )) + + return signedTransaction +} + +func (w *Wallet) TransitionAccount(transactionName string, inputName string, opts ...options.Option[builder.AccountOutputBuilder]) *iotago.SignedTransaction { + input, exists := w.outputs[inputName] + if !exists { + panic(fmt.Sprintf("account with alias %s does not exist", inputName)) + } + + accountOutput, ok := input.Output().Clone().(*iotago.AccountOutput) + if !ok { + panic(fmt.Sprintf("output with alias %s is not *iotago.AccountOutput", inputName)) + } + + accountBuilder := builder.NewAccountOutputBuilderFromPrevious(accountOutput) + accountOutput = options.Apply(accountBuilder, opts).MustBuild() + + signedTransaction := lo.PanicOnErr(w.createSignedTransactionWithOptions( + transactionName, + WithAccountInput(input), + WithContextInputs(iotago.TxEssenceContextInputs{ + &iotago.BlockIssuanceCreditInput{ + AccountID: accountOutput.AccountID, + }, + &iotago.CommitmentInput{ + CommitmentID: w.Node.Protocol.Engines.Main.Get().Storage.Settings().LatestCommitment().Commitment().MustID(), + }, + }), + WithOutputs(iotago.Outputs[iotago.Output]{accountOutput}), + )) + + return signedTransaction +} + +func (w *Wallet) DestroyAccount(transactionName string, inputName string, creationSlot iotago.SlotIndex) *iotago.SignedTransaction { + input := w.Output(inputName) + inputAccount, ok := input.Output().(*iotago.AccountOutput) + if !ok { + panic(fmt.Sprintf("output with alias %s is not *iotago.AccountOutput", inputName)) + } + + destructionOutputs := iotago.Outputs[iotago.Output]{&iotago.BasicOutput{ + Amount: input.BaseTokenAmount(), + Mana: input.StoredMana(), + Conditions: iotago.BasicOutputUnlockConditions{ + &iotago.AddressUnlockCondition{Address: w.Address()}, + }, + Features: iotago.BasicOutputFeatures{}, + }} + + signedTransaction := lo.PanicOnErr(w.createSignedTransactionWithOptions( + transactionName, + WithContextInputs(iotago.TxEssenceContextInputs{ + &iotago.BlockIssuanceCreditInput{ + AccountID: inputAccount.AccountID, + }, + &iotago.CommitmentInput{ + CommitmentID: w.Node.Protocol.Engines.Main.Get().Storage.Settings().LatestCommitment().Commitment().MustID(), + }, + }), + WithAccountInput(input), + WithOutputs(destructionOutputs), + WithSlotCreated(creationSlot), + )) + + return signedTransaction +} + +// CreateImplicitAccountFromInput creates an implicit account output. +func (w *Wallet) CreateImplicitAccountFromInput(transactionName string, inputName string, recipientWallet *Wallet) *iotago.SignedTransaction { + input := w.Output(inputName) + + implicitAccountOutput := &iotago.BasicOutput{ + Amount: MinIssuerAccountAmount, + Mana: AccountConversionManaCost, + Conditions: iotago.BasicOutputUnlockConditions{ + &iotago.AddressUnlockCondition{Address: recipientWallet.ImplicitAccountCreationAddress()}, + }, + Features: iotago.BasicOutputFeatures{}, + } + + remainderBasicOutput := &iotago.BasicOutput{ + Amount: input.BaseTokenAmount() - MinIssuerAccountAmount, + Mana: input.StoredMana() - AccountConversionManaCost, + Conditions: iotago.BasicOutputUnlockConditions{ + &iotago.AddressUnlockCondition{Address: input.Output().UnlockConditionSet().Address().Address}, + }, + Features: iotago.BasicOutputFeatures{}, + } + + signedTransaction := lo.PanicOnErr(w.createSignedTransactionWithOptions( + transactionName, + WithInputs(utxoledger.Outputs{input}), + WithOutputs(iotago.Outputs[iotago.Output]{implicitAccountOutput, remainderBasicOutput}), + )) + + // register the outputs in the recipient wallet (so wallet doesn't have to scan for outputs on its addresses) + recipientWallet.registerOutputs(transactionName, signedTransaction.Transaction) + + // register the implicit account as a block issuer in the wallet + implicitAccountID := iotago.AccountIDFromOutputID(recipientWallet.Output(fmt.Sprintf("%s:0", transactionName)).OutputID()) + recipientWallet.SetBlockIssuer(implicitAccountID) + + return signedTransaction +} + +func (w *Wallet) TransitionImplicitAccountToAccountOutput(transactionName string, inputName string, creationSlot iotago.SlotIndex, opts ...options.Option[builder.AccountOutputBuilder]) *iotago.SignedTransaction { + input := w.Output(inputName) + implicitAccountID := iotago.AccountIDFromOutputID(input.OutputID()) + + basicOutput, isBasic := input.Output().(*iotago.BasicOutput) + if !isBasic { + panic(fmt.Sprintf("output with alias %s is not *iotago.BasicOutput", inputName)) + } + if basicOutput.UnlockConditionSet().Address().Address.Type() != iotago.AddressImplicitAccountCreation { + panic(fmt.Sprintf("output with alias %s is not an implicit account", inputName)) + } + + accountOutput := options.Apply(builder.NewAccountOutputBuilder(w.Address(), MinIssuerAccountAmount). + AccountID(iotago.AccountIDFromOutputID(input.OutputID())), + opts).MustBuild() + + signedTransaction := lo.PanicOnErr(w.createSignedTransactionWithOptions( + transactionName, + WithContextInputs(iotago.TxEssenceContextInputs{ + &iotago.BlockIssuanceCreditInput{ + AccountID: implicitAccountID, + }, + &iotago.CommitmentInput{ + CommitmentID: w.Node.Protocol.Engines.Main.Get().Storage.Settings().LatestCommitment().Commitment().MustID(), + }, + }), + WithInputs(utxoledger.Outputs{input}), + WithOutputs(iotago.Outputs[iotago.Output]{accountOutput}), + WithSlotCreated(creationSlot), + func(txBuilder *builder.TransactionBuilder) { + txBuilder.AllotAllMana(creationSlot, implicitAccountID) + }, + )) + + return signedTransaction +} + +func (w *Wallet) CreateBasicOutputsEquallyFromInputs(transactionName string, outputCount int, inputNames ...string) *iotago.SignedTransaction { + inputStates := make([]*utxoledger.Output, 0, len(inputNames)) + totalInputAmounts := iotago.BaseToken(0) + totalInputStoredMana := iotago.Mana(0) + + for _, inputName := range inputNames { + output := w.Output(inputName) + inputStates = append(inputStates, output) + totalInputAmounts += output.BaseTokenAmount() + totalInputStoredMana += output.StoredMana() + } + + manaAmount := totalInputStoredMana / iotago.Mana(outputCount) + remainderMana := totalInputStoredMana + + tokenAmount := totalInputAmounts / iotago.BaseToken(outputCount) + remainderFunds := totalInputAmounts + + outputStates := make(iotago.Outputs[iotago.Output], 0, outputCount) + for i := 0; i < outputCount; i++ { + if i+1 == outputCount { + tokenAmount = remainderFunds + manaAmount = remainderMana + } + remainderFunds -= tokenAmount + remainderMana -= manaAmount + + outputStates = append(outputStates, &iotago.BasicOutput{ + Amount: tokenAmount, + Mana: manaAmount, + Conditions: iotago.BasicOutputUnlockConditions{ + &iotago.AddressUnlockCondition{Address: w.Address()}, + }, + Features: iotago.BasicOutputFeatures{}, + }) + } + + signedTransaction := lo.PanicOnErr(w.createSignedTransactionWithOptions( + transactionName, + WithInputs(inputStates), + WithOutputs(outputStates), + )) + + return signedTransaction +} + +func (w *Wallet) createSignedTransactionWithOptions(transactionName string, opts ...options.Option[builder.TransactionBuilder]) (*iotago.SignedTransaction, error) { + currentAPI := w.Node.Protocol.CommittedAPI() + + txBuilder := builder.NewTransactionBuilder(currentAPI) + txBuilder.WithTransactionCapabilities(iotago.TransactionCapabilitiesBitMaskWithCapabilities(iotago.WithTransactionCanDoAnything())) + // Always add a random payload to randomize transaction ID. + randomPayload := tpkg.Rand12ByteArray() + txBuilder.AddTaggedDataPayload(&iotago.TaggedData{Tag: randomPayload[:], Data: randomPayload[:]}) + + addrSigner := w.AddressSigner() + signedTransaction, err := options.Apply(txBuilder, opts).Build(addrSigner) + + // register the outputs in the wallet + w.registerOutputs(transactionName, signedTransaction.Transaction) + + return signedTransaction, err +} + +func (w *Wallet) registerOutputs(transactionName string, transaction *iotago.Transaction) { + currentAPI := w.Node.Protocol.CommittedAPI() + (lo.PanicOnErr(transaction.ID())).RegisterAlias(transactionName) + w.transactions[transactionName] = transaction + + for outputID, output := range lo.PanicOnErr(transaction.OutputsSet()) { + // register the output if it belongs to this wallet + addressUC := output.UnlockConditionSet().Address() + stateControllerUC := output.UnlockConditionSet().StateControllerAddress() + if addressUC != nil && w.HasAddress(addressUC.Address) || stateControllerUC != nil && w.HasAddress(stateControllerUC.Address) { + clonedOutput := output.Clone() + actualOutputID := iotago.OutputIDFromTransactionIDAndIndex(lo.PanicOnErr(transaction.ID()), outputID.Index()) + if clonedOutput.Type() == iotago.OutputAccount { + if accountOutput, ok := clonedOutput.(*iotago.AccountOutput); ok && accountOutput.AccountID == iotago.EmptyAccountID { + accountOutput.AccountID = iotago.AccountIDFromOutputID(actualOutputID) + } + } + w.outputs[fmt.Sprintf("%s:%d", transactionName, outputID.Index())] = utxoledger.CreateOutput(w.Node.Protocol, actualOutputID, iotago.EmptyBlockID, currentAPI.TimeProvider().SlotFromTime(time.Now()), clonedOutput, lo.PanicOnErr(iotago.OutputIDProofFromTransaction(transaction, outputID.Index()))) + } + } +} 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/protocol/snapshotcreator/options.go b/pkg/testsuite/snapshotcreator/options.go similarity index 71% rename from pkg/protocol/snapshotcreator/options.go rename to pkg/testsuite/snapshotcreator/options.go index f1da75f43..85aa1f439 100644 --- a/pkg/protocol/snapshotcreator/options.go +++ b/pkg/testsuite/snapshotcreator/options.go @@ -6,6 +6,9 @@ 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" + "github.com/iotaledger/iota-core/pkg/testsuite/mock" iotago "github.com/iotaledger/iota.go/v4" ) @@ -20,8 +23,8 @@ type Options struct { // RootBlocks define the initial blocks to which new blocks can attach to. RootBlocks map[iotago.BlockID]iotago.CommitmentID - // GenesisSeed defines the seed used to generate keypair that can spend Genesis outputs. - GenesisSeed []byte + // GenesisKeyManager defines the key manager used to generate keypair that can spend Genesis outputs. + GenesisKeyManager *mock.KeyManager // Accounts defines the accounts that are created in the ledger as part of the Genesis. Accounts []AccountDetails @@ -29,19 +32,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 } @@ -73,10 +84,10 @@ func WithRootBlocks(rootBlocks map[iotago.BlockID]iotago.CommitmentID) options.O } } -// WithGenesisSeed defines the seed used to generate keypair that can spend Genesis outputs. -func WithGenesisSeed(genesisSeed []byte) options.Option[Options] { +// WithGenesisKeyManager defines the seed used to generate keypair that can spend Genesis outputs. +func WithGenesisKeyManager(keyManager *mock.KeyManager) options.Option[Options] { return func(m *Options) { - m.GenesisSeed = genesisSeed + m.GenesisKeyManager = keyManager } } diff --git a/pkg/protocol/snapshotcreator/snapshotcreator.go b/pkg/testsuite/snapshotcreator/snapshotcreator.go similarity index 85% rename from pkg/protocol/snapshotcreator/snapshotcreator.go rename to pkg/testsuite/snapshotcreator/snapshotcreator.go index 2976f803f..ed47d8156 100644 --- a/pkg/protocol/snapshotcreator/snapshotcreator.go +++ b/pkg/testsuite/snapshotcreator/snapshotcreator.go @@ -10,9 +10,9 @@ import ( "github.com/iotaledger/hive.go/log" "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" @@ -71,20 +71,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{}, }) } } @@ -100,10 +107,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(), @@ -127,7 +135,7 @@ func CreateSnapshot(opts ...options.Option[Options]) error { }, iotago.BaseToken(0)) var genesisTransactionOutputs iotago.TxEssenceOutputs - genesisOutput, err := createGenesisOutput(api, opt.ProtocolParameters.TokenSupply()-totalAccountAmount-totalBasicOutputAmount, opt.GenesisSeed) + genesisOutput, err := createGenesisOutput(api, opt.ProtocolParameters.TokenSupply()-totalAccountAmount-totalBasicOutputAmount, opt.GenesisKeyManager) if err != nil { return ierrors.Wrap(err, "failed to create genesis outputs") } @@ -181,10 +189,9 @@ func CreateSnapshot(opts ...options.Option[Options]) error { return engineInstance.WriteSnapshot(opt.FilePath) } -func createGenesisOutput(api iotago.API, genesisTokenAmount iotago.BaseToken, genesisSeed []byte) (iotago.Output, error) { +func createGenesisOutput(api iotago.API, genesisTokenAmount iotago.BaseToken, genesisKeyManager *mock.KeyManager) (iotago.Output, error) { if genesisTokenAmount > 0 { - genesisWallet := mock.NewHDWallet("genesis", genesisSeed, 0) - output := createOutput(genesisWallet.Address(), genesisTokenAmount, 0) + output := createOutput(genesisKeyManager.Address(iotago.AddressEd25519), genesisTokenAmount, iotago.Mana(genesisTokenAmount)) if _, err := api.StorageScoreStructure().CoversMinDeposit(output, genesisTokenAmount); err != nil { return nil, ierrors.Wrap(err, "min rent not covered by Genesis output with index 0") @@ -244,8 +251,7 @@ func createAccount(accountID iotago.AccountID, address iotago.Address, tokenAmou Mana: mana, AccountID: accountID, Conditions: iotago.AccountOutputUnlockConditions{ - &iotago.StateControllerAddressUnlockCondition{Address: address}, - &iotago.GovernorAddressUnlockCondition{Address: address}, + &iotago.AddressUnlockCondition{Address: address}, }, Features: iotago.AccountOutputFeatures{ &iotago.BlockIssuerFeature{ diff --git a/pkg/testsuite/sybilprotection.go b/pkg/testsuite/sybilprotection.go index 65a28a7b8..1000c7879 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.Engines.Main.Get().SybilProtection.SeatManager().Committee(slot).Accounts().IDs() + accounts := lo.Return1(node.Protocol.Engines.Main.Get().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.Engines.Main.Get().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.go b/pkg/testsuite/testsuite.go index 7ae3ec678..9b3a0d16e 100644 --- a/pkg/testsuite/testsuite.go +++ b/pkg/testsuite/testsuite.go @@ -19,26 +19,23 @@ import ( "github.com/iotaledger/iota-core/pkg/protocol" "github.com/iotaledger/iota-core/pkg/protocol/engine/blocks" "github.com/iotaledger/iota-core/pkg/protocol/engine/utxoledger" - "github.com/iotaledger/iota-core/pkg/protocol/snapshotcreator" "github.com/iotaledger/iota-core/pkg/protocol/sybilprotection/sybilprotectionv1" "github.com/iotaledger/iota-core/pkg/storage/utils" "github.com/iotaledger/iota-core/pkg/testsuite/mock" + "github.com/iotaledger/iota-core/pkg/testsuite/snapshotcreator" iotago "github.com/iotaledger/iota.go/v4" "github.com/iotaledger/iota.go/v4/tpkg" ) -const MinIssuerAccountAmount = iotago.BaseToken(372900) -const MinValidatorAccountAmount = iotago.BaseToken(722800) - type TestSuite struct { Testing *testing.T fakeTesting *testing.T network *mock.Network - Directory *utils.Directory - nodes *orderedmap.OrderedMap[string, *mock.Node] - blockIssuers *orderedmap.OrderedMap[string, *mock.BlockIssuer] - running bool + Directory *utils.Directory + nodes *orderedmap.OrderedMap[string, *mock.Node] + wallets *orderedmap.OrderedMap[string, *mock.Wallet] + running bool snapshotPath string blocks *shrinkingmap.ShrinkingMap[string, *blocks.Block] @@ -54,19 +51,19 @@ type TestSuite struct { uniqueBlockTimeCounter atomic.Int64 automaticTransactionIssuingCounters shrinkingmap.ShrinkingMap[string, int] mutex syncutils.RWMutex - TransactionFramework *TransactionFramework - genesisSeed [32]byte + genesisKeyManager *mock.KeyManager } func NewTestSuite(testingT *testing.T, opts ...options.Option[TestSuite]) *TestSuite { + genesisSeed := tpkg.RandEd25519Seed() return options.Apply(&TestSuite{ Testing: testingT, fakeTesting: &testing.T{}, - genesisSeed: tpkg.RandEd25519Seed(), + genesisKeyManager: mock.NewKeyManager(genesisSeed[:], 0), network: mock.NewNetwork(), Directory: utils.NewDirectory(testingT.TempDir()), nodes: orderedmap.New[string, *mock.Node](), - blockIssuers: orderedmap.New[string, *mock.BlockIssuer](), + wallets: orderedmap.New[string, *mock.Wallet](), blocks: shrinkingmap.New[string, *blocks.Block](), automaticTransactionIssuingCounters: *shrinkingmap.New[string, int](), @@ -119,7 +116,7 @@ func NewTestSuite(testingT *testing.T, opts ...options.Option[TestSuite]) *TestS t.ProtocolParameterOptions = append(defaultProtocolParameters, t.ProtocolParameterOptions...) t.API = iotago.V3API(iotago.NewV3ProtocolParameters(t.ProtocolParameterOptions...)) - genesisBlock := blocks.NewRootBlock(iotago.EmptyBlockID, iotago.NewEmptyCommitment(t.API.ProtocolParameters().Version()).MustID(), time.Unix(t.API.ProtocolParameters().TimeProvider().GenesisUnixTime(), 0)) + genesisBlock := blocks.NewRootBlock(iotago.EmptyBlockID, iotago.NewEmptyCommitment(t.API.ProtocolParameters().Version()).MustID(), time.Unix(t.API.ProtocolParameters().GenesisUnixTimestamp(), 0)) t.RegisterBlock("Genesis", genesisBlock) t.snapshotPath = t.Directory.Path("genesis_snapshot.bin") @@ -153,10 +150,7 @@ func (t *TestSuite) AccountOutput(alias string) *utxoledger.Output { t.mutex.RLock() defer t.mutex.RUnlock() - output, exist := t.TransactionFramework.states[alias] - if !exist { - panic(fmt.Sprintf("account %s not registered", alias)) - } + output := t.DefaultWallet().Output(alias) if _, ok := output.Output().(*iotago.AccountOutput); !ok { panic(fmt.Sprintf("output %s is not an account", alias)) @@ -255,6 +249,18 @@ func (t *TestSuite) Node(name string) *mock.Node { return node } +func (t *TestSuite) Wallet(name string) *mock.Wallet { + t.mutex.RLock() + defer t.mutex.RUnlock() + + wallet, exist := t.wallets.Get(name) + if !exist { + panic(fmt.Sprintf("wallet %s does not exist", name)) + } + + return wallet +} + func (t *TestSuite) Nodes(names ...string) []*mock.Node { if len(names) == 0 { t.mutex.RLock() @@ -320,7 +326,7 @@ func (t *TestSuite) addNodeToPartition(name string, partition string, validator node := mock.NewNode(t.Testing, t.network, partition, name, validator) t.nodes.Set(name, node) - amount := MinValidatorAccountAmount + amount := mock.MinValidatorAccountAmount if len(optAmount) > 0 { amount = optAmount[0] } @@ -349,7 +355,11 @@ func (t *TestSuite) AddValidatorNodeToPartition(name string, partition string, o } func (t *TestSuite) AddValidatorNode(name string, optAmount ...iotago.BaseToken) *mock.Node { - return t.addNodeToPartition(name, mock.NetworkMainPartition, true, optAmount...) + node := t.addNodeToPartition(name, mock.NetworkMainPartition, true, optAmount...) + // create a wallet for each validator node which uses the validator account as a block issuer + t.addWallet(name, node, node.Validator.AccountID, node.KeyManager) + + return node } func (t *TestSuite) AddNodeToPartition(name string, partition string, optAmount ...iotago.BaseToken) *mock.Node { @@ -364,9 +374,10 @@ func (t *TestSuite) RemoveNode(name string) { t.nodes.Delete(name) } -func (t *TestSuite) AddBasicBlockIssuer(name string, blockIssuanceCredits ...iotago.BlockIssuanceCredits) *mock.BlockIssuer { - newBlockIssuer := mock.NewBlockIssuer(t.Testing, name, false) - t.blockIssuers.Set(name, newBlockIssuer) +// AddGenesisWallet adds a wallet to the test suite with a block issuer in the genesis snapshot and access to the genesis seed. +// If no block issuance credits are provided, the wallet will be assigned half of the maximum block issuance credits. +func (t *TestSuite) AddGenesisWallet(name string, node *mock.Node, blockIssuanceCredits ...iotago.BlockIssuanceCredits) *mock.Wallet { + newWallet := t.addWallet(name, node, iotago.EmptyAccountID, t.genesisKeyManager) var bic iotago.BlockIssuanceCredits if len(blockIssuanceCredits) == 0 { bic = iotago.MaxBlockIssuanceCredits / 2 @@ -375,24 +386,34 @@ func (t *TestSuite) AddBasicBlockIssuer(name string, blockIssuanceCredits ...iot } accountDetails := snapshotcreator.AccountDetails{ - Address: iotago.Ed25519AddressFromPubKey(newBlockIssuer.PublicKey), - Amount: MinIssuerAccountAmount, - Mana: iotago.Mana(MinIssuerAccountAmount), - IssuerKey: iotago.Ed25519PublicKeyBlockIssuerKeyFromPublicKey(ed25519.PublicKey(newBlockIssuer.PublicKey)), + Address: iotago.Ed25519AddressFromPubKey(newWallet.BlockIssuer.PublicKey), + Amount: mock.MinIssuerAccountAmount, + Mana: iotago.Mana(mock.MinIssuerAccountAmount), + IssuerKey: iotago.Ed25519PublicKeyBlockIssuerKeyFromPublicKey(ed25519.PublicKey(newWallet.BlockIssuer.PublicKey)), ExpirySlot: iotago.MaxSlotIndex, BlockIssuanceCredits: bic, } t.optsAccounts = append(t.optsAccounts, accountDetails) - return newBlockIssuer + return newWallet } -func (t *TestSuite) DefaultBasicBlockIssuer() *mock.BlockIssuer { - defaultBasicBlockIssuer, exists := t.blockIssuers.Get("default") - require.True(t.Testing, exists, "default block issuer not found") +func (t *TestSuite) addWallet(name string, node *mock.Node, accountID iotago.AccountID, keyManager *mock.KeyManager) *mock.Wallet { + newWallet := mock.NewWallet(t.Testing, name, node, keyManager) + newWallet.SetBlockIssuer(accountID) + t.wallets.Set(name, newWallet) - return defaultBasicBlockIssuer + return newWallet +} + +func (t *TestSuite) DefaultWallet() *mock.Wallet { + defaultWallet, exists := t.wallets.Get("default") + if !exists { + return nil + } + + return defaultWallet } func (t *TestSuite) Run(failOnBlockFiltered bool, nodesOptions ...map[string][]options.Option[protocol.Protocol]) { @@ -401,11 +422,10 @@ func (t *TestSuite) Run(failOnBlockFiltered bool, nodesOptions ...map[string][]o // Create accounts for any block issuer nodes added before starting the network. if t.optsAccounts != nil { - wallet := mock.NewHDWallet("genesis", t.genesisSeed[:], 0) t.optsSnapshotOptions = append(t.optsSnapshotOptions, snapshotcreator.WithAccounts(lo.Map(t.optsAccounts, func(accountDetails snapshotcreator.AccountDetails) snapshotcreator.AccountDetails { - // if no custom address is assigned to the account, assign an address generated from GenesisSeed + // if no custom address is assigned to the account, assign an address generated from GenesisKeyManager if accountDetails.Address == nil { - accountDetails.Address = wallet.Address() + accountDetails.Address = t.genesisKeyManager.Address(iotago.AddressEd25519) } if accountDetails.AccountID.Empty() { @@ -422,7 +442,7 @@ func (t *TestSuite) Run(failOnBlockFiltered bool, nodesOptions ...map[string][]o })...)) } - err := snapshotcreator.CreateSnapshot(append([]options.Option[snapshotcreator.Options]{snapshotcreator.WithGenesisSeed(t.genesisSeed[:])}, t.optsSnapshotOptions...)...) + err := snapshotcreator.CreateSnapshot(append([]options.Option[snapshotcreator.Options]{snapshotcreator.WithGenesisKeyManager(t.genesisKeyManager)}, t.optsSnapshotOptions...)...) if err != nil { panic(fmt.Sprintf("failed to create snapshot: %s", err)) } @@ -443,8 +463,13 @@ func (t *TestSuite) Run(failOnBlockFiltered bool, nodesOptions ...map[string][]o node.Initialize(failOnBlockFiltered, baseOpts...) - if t.TransactionFramework == nil { - t.TransactionFramework = NewTransactionFramework(node.Protocol, t.genesisSeed[:]) + if defaultWallet := t.DefaultWallet(); defaultWallet != nil { + if err := node.Protocol.Engines.Main.Get().Ledger.ForEachUnspentOutput(func(output *utxoledger.Output) bool { + defaultWallet.AddOutput(fmt.Sprintf("Genesis:%d", output.OutputID().Index()), output) + return true + }); err != nil { + panic(err) + } } return true @@ -473,7 +498,7 @@ func (t *TestSuite) BlockIssuersForNodes(nodes []*mock.Node) []*mock.BlockIssuer if node.IsValidator() { blockIssuers = append(blockIssuers, node.Validator) } else { - blockIssuers = append(blockIssuers, t.DefaultBasicBlockIssuer()) + blockIssuers = append(blockIssuers, t.DefaultWallet().BlockIssuer) } } diff --git a/pkg/testsuite/testsuite_issue_blocks.go b/pkg/testsuite/testsuite_issue_blocks.go index 73f578ea9..62df70ec6 100644 --- a/pkg/testsuite/testsuite_issue_blocks.go +++ b/pkg/testsuite/testsuite_issue_blocks.go @@ -49,29 +49,19 @@ func (t *TestSuite) limitParentsCountInBlockOptions(blockOpts []options.Option[m return blockOpts } -func (t *TestSuite) RegisterBlock(alias string, block *blocks.Block) { +func (t *TestSuite) RegisterBlock(blockName string, block *blocks.Block) { t.mutex.Lock() defer t.mutex.Unlock() - t.registerBlock(alias, block) + t.registerBlock(blockName, block) } -func (t *TestSuite) registerBlock(alias string, block *blocks.Block) { - t.blocks.Set(alias, block) - block.ID().RegisterAlias(alias) +func (t *TestSuite) registerBlock(blockName string, block *blocks.Block) { + t.blocks.Set(blockName, block) + block.ID().RegisterAlias(blockName) } -func (t *TestSuite) CreateBasicBlock(alias string, blockIssuer *mock.BlockIssuer, node *mock.Node, blockOpts ...options.Option[mock.BasicBlockParams]) { - t.mutex.Lock() - defer t.mutex.Unlock() - - block, err := blockIssuer.CreateBasicBlock(context.Background(), alias, node, blockOpts...) - require.NoError(t.Testing, err) - - t.registerBlock(alias, block) -} - -func (t *TestSuite) IssueValidationBlockAtSlot(alias string, slot iotago.SlotIndex, slotCommitment *iotago.Commitment, node *mock.Node, parents ...iotago.BlockID) *blocks.Block { +func (t *TestSuite) IssueValidationBlockAtSlot(blockName string, slot iotago.SlotIndex, slotCommitment *iotago.Commitment, node *mock.Node, parents ...iotago.BlockID) *blocks.Block { t.AssertBlocksExist(t.Blocks(lo.Map(parents, func(id iotago.BlockID) string { return id.Alias() })...), true, node) t.mutex.Lock() @@ -83,48 +73,48 @@ func (t *TestSuite) IssueValidationBlockAtSlot(alias string, slot iotago.SlotInd 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())) require.True(t.Testing, node.IsValidator(), "node: %s: is not a validator node", node.Name) - block := node.Validator.IssueValidationBlock(context.Background(), alias, node, mock.WithValidationBlockHeaderOptions(mock.WithIssuingTime(issuingTime), mock.WithSlotCommitment(slotCommitment), mock.WithStrongParents(parents...))) + block := node.Validator.IssueValidationBlock(context.Background(), blockName, node, mock.WithValidationBlockHeaderOptions(mock.WithIssuingTime(issuingTime), mock.WithSlotCommitment(slotCommitment), mock.WithStrongParents(parents...))) - t.registerBlock(alias, block) + t.registerBlock(blockName, block) return block } -func (t *TestSuite) IssueExistingBlock(alias string, blockIssuer *mock.BlockIssuer, node *mock.Node) { +func (t *TestSuite) IssueExistingBlock(blockName string, wallet *mock.Wallet) { t.mutex.Lock() defer t.mutex.Unlock() - block, exists := t.blocks.Get(alias) + block, exists := t.blocks.Get(blockName) require.True(t.Testing, exists) require.NotNil(t.Testing, block) - require.NoError(t.Testing, blockIssuer.IssueBlock(block.ModelBlock(), node)) + require.NoError(t.Testing, wallet.BlockIssuer.IssueBlock(block.ModelBlock(), wallet.Node)) } -func (t *TestSuite) IssueValidationBlockWithOptions(alias string, blockIssuer *mock.BlockIssuer, node *mock.Node, blockOpts ...options.Option[mock.ValidatorBlockParams]) *blocks.Block { +func (t *TestSuite) IssueValidationBlockWithOptions(blockName string, node *mock.Node, blockOpts ...options.Option[mock.ValidatorBlockParams]) *blocks.Block { t.mutex.Lock() defer t.mutex.Unlock() - block := blockIssuer.IssueValidationBlock(context.Background(), alias, node, blockOpts...) + block := node.IssueValidationBlock(context.Background(), blockName, blockOpts...) - t.registerBlock(alias, block) + t.registerBlock(blockName, block) return block } -func (t *TestSuite) IssueBasicBlockWithOptions(alias string, blockIssuer *mock.BlockIssuer, node *mock.Node, blockOpts ...options.Option[mock.BasicBlockParams]) *blocks.Block { +func (t *TestSuite) IssueBasicBlockWithOptions(blockName string, wallet *mock.Wallet, blockOpts ...options.Option[mock.BasicBlockParams]) *blocks.Block { t.mutex.Lock() defer t.mutex.Unlock() - block := blockIssuer.IssueBasicBlock(context.Background(), alias, node, blockOpts...) + block := wallet.IssueBasicBlock(context.Background(), blockName, blockOpts...) - t.registerBlock(alias, block) + t.registerBlock(blockName, block) return block } -func (t *TestSuite) IssueBasicBlockAtSlotWithOptions(alias string, slot iotago.SlotIndex, slotCommitment *iotago.Commitment, blockIssuer *mock.BlockIssuer, node *mock.Node, payload iotago.Payload, blockOpts ...options.Option[mock.BlockHeaderParams]) *blocks.Block { - t.assertParentsExistFromBlockOptions(blockOpts, node) +func (t *TestSuite) IssueBasicBlockAtSlotWithOptions(blockName string, slot iotago.SlotIndex, wallet *mock.Wallet, payload iotago.Payload, blockOpts ...options.Option[mock.BlockHeaderParams]) *blocks.Block { + t.assertParentsExistFromBlockOptions(blockOpts, wallet.Node) t.mutex.Lock() defer t.mutex.Unlock() @@ -132,29 +122,29 @@ func (t *TestSuite) IssueBasicBlockAtSlotWithOptions(alias string, slot iotago.S 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())) + require.Truef(t.Testing, issuingTime.Before(time.Now()), "wallet: %s: issued block (%s, slot: %d) is in the current (%s, slot: %d) or future slot", wallet.Name, issuingTime, slot, time.Now(), timeProvider.SlotFromTime(time.Now())) - block := blockIssuer.IssueBasicBlock(context.Background(), alias, node, mock.WithBasicBlockHeader(append(blockOpts, mock.WithIssuingTime(issuingTime), mock.WithSlotCommitment(slotCommitment))...), mock.WithPayload(payload)) + block := wallet.IssueBasicBlock(context.Background(), blockName, mock.WithBasicBlockHeader(append(blockOpts, mock.WithIssuingTime(issuingTime))...), mock.WithPayload(payload)) - t.registerBlock(alias, block) + t.registerBlock(blockName, block) return block } -func (t *TestSuite) IssuePayloadWithOptions(alias string, blockIssuer *mock.BlockIssuer, node *mock.Node, payload iotago.Payload, blockHeaderOpts ...options.Option[mock.BlockHeaderParams]) *blocks.Block { - t.assertParentsExistFromBlockOptions(blockHeaderOpts, node) +func (t *TestSuite) IssuePayloadWithOptions(blockName string, wallet *mock.Wallet, payload iotago.Payload, blockHeaderOpts ...options.Option[mock.BlockHeaderParams]) *blocks.Block { + t.assertParentsExistFromBlockOptions(blockHeaderOpts, wallet.Node) t.mutex.Lock() defer t.mutex.Unlock() - block := blockIssuer.IssueBasicBlock(context.Background(), alias, node, mock.WithPayload(payload), mock.WithBasicBlockHeader(blockHeaderOpts...)) + block := wallet.IssueBasicBlock(context.Background(), blockName, mock.WithPayload(payload), mock.WithBasicBlockHeader(blockHeaderOpts...)) - t.registerBlock(alias, block) + t.registerBlock(blockName, block) return block } -func (t *TestSuite) IssueValidationBlock(alias string, node *mock.Node, blockHeaderOpts ...options.Option[mock.BlockHeaderParams]) *blocks.Block { +func (t *TestSuite) IssueValidationBlock(blockName string, node *mock.Node, blockHeaderOpts ...options.Option[mock.BlockHeaderParams]) *blocks.Block { t.assertParentsExistFromBlockOptions(blockHeaderOpts, node) require.Truef(t.Testing, node.IsValidator(), "node: %s: is not a validator node", node.Name) @@ -162,22 +152,36 @@ func (t *TestSuite) IssueValidationBlock(alias string, node *mock.Node, blockHea t.mutex.Lock() defer t.mutex.Unlock() - block := node.Validator.IssueValidationBlock(context.Background(), alias, node, mock.WithValidationBlockHeaderOptions(blockHeaderOpts...)) + block := node.Validator.IssueValidationBlock(context.Background(), blockName, node, mock.WithValidationBlockHeaderOptions(blockHeaderOpts...)) - t.registerBlock(alias, block) + t.registerBlock(blockName, block) return block } +func (t *TestSuite) IssueCandidacyAnnouncementInSlot(alias string, slot iotago.SlotIndex, parentsPrefixAlias string, wallet *mock.Wallet, 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()), "wallet: %s: issued block (%s, slot: %d) is in the current (%s, slot: %d) or future slot", wallet.Name, issuingTime, slot, time.Now(), timeProvider.SlotFromTime(time.Now())) + + return t.IssuePayloadWithOptions( + alias, + wallet, + &iotago.CandidacyAnnouncement{}, + append(issuingOptions, + mock.WithStrongParents(t.BlockIDsWithPrefix(parentsPrefixAlias)...), + mock.WithIssuingTime(issuingTime), + )..., + ) +} -func (t *TestSuite) IssueBlockRowInSlot(prefix string, slot iotago.SlotIndex, row int, parentsPrefixAlias string, nodes []*mock.Node, issuingOptions map[string][]options.Option[mock.BlockHeaderParams]) []*blocks.Block { - blockIssuers := t.BlockIssuersForNodes(nodes) +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)) - strongParents := t.BlockIDsWithPrefix(parentsPrefixAlias) + strongParents := t.BlockIDsWithPrefix(parentsPrefix) issuingOptionsCopy := lo.MergeMaps(make(map[string][]options.Option[mock.BlockHeaderParams]), issuingOptions) - for index, node := range nodes { - blockAlias := fmt.Sprintf("%s%d.%d-%s", prefix, slot, row, node.Name) + for _, node := range nodes { + blockName := fmt.Sprintf("%s%d.%d-%s", prefix, slot, row, node.Name) issuingOptionsCopy[node.Name] = append(issuingOptionsCopy[node.Name], mock.WithStrongParents(strongParents...)) timeProvider := t.API.TimeProvider() @@ -185,31 +189,32 @@ 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 blockIssuers[index].Validator { + // Only issue validator blocks if account has staking feature and is part of committee. + if node.Validator != nil && lo.Return1(node.Protocol.Engines.Main.Get().SybilProtection.SeatManager().CommitteeInSlot(slot)).HasAccount(node.Validator.AccountID) { blockHeaderOptions := append(issuingOptionsCopy[node.Name], mock.WithIssuingTime(issuingTime)) t.assertParentsCommitmentExistFromBlockOptions(blockHeaderOptions, node) t.assertParentsExistFromBlockOptions(blockHeaderOptions, node) - b = t.IssueValidationBlockWithOptions(blockAlias, blockIssuers[index], node, mock.WithValidationBlockHeaderOptions(blockHeaderOptions...), mock.WithHighestSupportedVersion(node.HighestSupportedVersion()), mock.WithProtocolParametersHash(node.ProtocolParametersHash())) + b = t.IssueValidationBlockWithOptions(blockName, node, mock.WithValidationBlockHeaderOptions(blockHeaderOptions...), mock.WithHighestSupportedVersion(node.HighestSupportedVersion()), mock.WithProtocolParametersHash(node.ProtocolParametersHash())) } else { txCount := t.automaticTransactionIssuingCounters.Compute(node.Partition, func(currentValue int, exists bool) int { return currentValue + 1 }) - inputAlias := fmt.Sprintf("automaticSpent-%d:0", txCount-1) - txAlias := fmt.Sprintf("automaticSpent-%d", txCount) + inputName := fmt.Sprintf("automaticSpent-%d:0", txCount-1) + txName := fmt.Sprintf("automaticSpent-%d", txCount) if txCount == 1 { - inputAlias = "Genesis:0" + inputName = "Genesis:0" } - tx, err := t.TransactionFramework.CreateSimpleTransaction(txAlias, 1, inputAlias) - require.NoError(t.Testing, err) + tx := t.DefaultWallet().CreateBasicOutputsEquallyFromInputs(txName, 1, inputName) - issuingOptionsCopy[node.Name] = t.limitParentsCountInBlockOptions(issuingOptionsCopy[node.Name], iotago.BlockMaxParents) + issuingOptionsCopy[node.Name] = t.limitParentsCountInBlockOptions(issuingOptionsCopy[node.Name], iotago.BasicBlockMaxParents) blockHeaderOptions := append(issuingOptionsCopy[node.Name], mock.WithIssuingTime(issuingTime)) t.assertParentsCommitmentExistFromBlockOptions(blockHeaderOptions, node) t.assertParentsExistFromBlockOptions(blockHeaderOptions, node) - b = t.IssueBasicBlockWithOptions(blockAlias, blockIssuers[index], node, mock.WithPayload(tx), mock.WithBasicBlockHeader(blockHeaderOptions...)) + t.DefaultWallet().SetDefaultNode(node) + b = t.IssueBasicBlockWithOptions(blockName, t.DefaultWallet(), mock.WithPayload(tx), mock.WithBasicBlockHeader(blockHeaderOptions...)) } blocksIssued = append(blocksIssued, b) } @@ -217,32 +222,32 @@ func (t *TestSuite) IssueBlockRowInSlot(prefix string, slot iotago.SlotIndex, ro return blocksIssued } -func (t *TestSuite) IssueBlockRowsInSlot(prefix string, slot iotago.SlotIndex, rows int, initialParentsPrefixAlias string, nodes []*mock.Node, issuingOptions map[string][]options.Option[mock.BlockHeaderParams]) (allBlocksIssued []*blocks.Block, lastBlockRow []*blocks.Block) { +func (t *TestSuite) IssueBlockRowsInSlot(prefix string, slot iotago.SlotIndex, rows int, initialParentsPrefix string, nodes []*mock.Node, issuingOptions map[string][]options.Option[mock.BlockHeaderParams]) (allBlocksIssued []*blocks.Block, lastBlockRow []*blocks.Block) { var blocksIssued, lastBlockRowIssued []*blocks.Block - parentsPrefixAlias := initialParentsPrefixAlias + parentsPrefix := initialParentsPrefix for row := 0; row < rows; row++ { if row > 0 { - parentsPrefixAlias = fmt.Sprintf("%s%d.%d", prefix, slot, row-1) + parentsPrefix = fmt.Sprintf("%s%d.%d", prefix, slot, row-1) } - lastBlockRowIssued = t.IssueBlockRowInSlot(prefix, slot, row, parentsPrefixAlias, nodes, issuingOptions) + lastBlockRowIssued = t.IssueBlockRowInSlot(prefix, slot, row, parentsPrefix, nodes, issuingOptions) blocksIssued = append(blocksIssued, lastBlockRowIssued...) } return blocksIssued, lastBlockRowIssued } -func (t *TestSuite) IssueBlocksAtSlots(prefix string, slots []iotago.SlotIndex, rowsPerSlot int, initialParentsPrefixAlias string, nodes []*mock.Node, waitForSlotsCommitted bool, issuingOptions map[string][]options.Option[mock.BlockHeaderParams]) (allBlocksIssued []*blocks.Block, lastBlockRow []*blocks.Block) { +func (t *TestSuite) IssueBlocksAtSlots(prefix string, slots []iotago.SlotIndex, rowsPerSlot int, initialParentsPrefix string, nodes []*mock.Node, waitForSlotsCommitted bool, issuingOptions map[string][]options.Option[mock.BlockHeaderParams]) (allBlocksIssued []*blocks.Block, lastBlockRow []*blocks.Block) { var blocksIssued, lastBlockRowIssued []*blocks.Block - parentsPrefixAlias := initialParentsPrefixAlias + parentsPrefix := initialParentsPrefix for i, slot := range slots { if i > 0 { - parentsPrefixAlias = fmt.Sprintf("%s%d.%d", prefix, slots[i-1], rowsPerSlot-1) + parentsPrefix = fmt.Sprintf("%s%d.%d", prefix, slots[i-1], rowsPerSlot-1) } - blocksInSlot, lastRowInSlot := t.IssueBlockRowsInSlot(prefix, slot, rowsPerSlot, parentsPrefixAlias, nodes, issuingOptions) + blocksInSlot, lastRowInSlot := t.IssueBlockRowsInSlot(prefix, slot, rowsPerSlot, parentsPrefix, nodes, issuingOptions) blocksIssued = append(blocksIssued, blocksInSlot...) lastBlockRowIssued = lastRowInSlot @@ -258,8 +263,8 @@ func (t *TestSuite) IssueBlocksAtSlots(prefix string, slots []iotago.SlotIndex, return blocksIssued, lastBlockRowIssued } -func (t *TestSuite) IssueBlocksAtEpoch(prefix string, epoch iotago.EpochIndex, rowsPerSlot int, initialParentsPrefixAlias string, nodes []*mock.Node, waitForSlotsCommitted bool, issuingOptions map[string][]options.Option[mock.BlockHeaderParams]) (allBlocksIssued []*blocks.Block, lastBlockRow []*blocks.Block) { - return t.IssueBlocksAtSlots(prefix, t.SlotsForEpoch(epoch), rowsPerSlot, initialParentsPrefixAlias, nodes, waitForSlotsCommitted, issuingOptions) +func (t *TestSuite) IssueBlocksAtEpoch(prefix string, epoch iotago.EpochIndex, rowsPerSlot int, initialParentsPrefix string, nodes []*mock.Node, waitForSlotsCommitted bool, issuingOptions map[string][]options.Option[mock.BlockHeaderParams]) (allBlocksIssued []*blocks.Block, lastBlockRow []*blocks.Block) { + return t.IssueBlocksAtSlots(prefix, t.SlotsForEpoch(epoch), rowsPerSlot, initialParentsPrefix, nodes, waitForSlotsCommitted, issuingOptions) } func (t *TestSuite) SlotsForEpoch(epoch iotago.EpochIndex) []iotago.SlotIndex { @@ -295,17 +300,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) - blockAlias := fmt.Sprintf("chain-%s-%d-%s", parent.ID().Alias(), chainIndex, node.Name) - tip = t.IssueValidationBlockAtSlot(blockAlias, nextBlockSlot, node.Protocol.Engines.Main.Get().Storage.Settings().LatestCommitment().Commitment(), node, tip.ID()) + committeeAtBlockSlot, exists := node.Protocol.Engines.Main.Get().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.Engines.Main.Get().Storage.Settings().LatestCommitment().Commitment(), node, tip.ID()) + } } // acceptance of nextBlockSlot for _, node := range activeValidators { - blockAlias := fmt.Sprintf("chain-%s-%d-%s", parent.ID().Alias(), chainIndex+1, node.Name) - tip = t.IssueValidationBlockAtSlot(blockAlias, nextBlockSlot, node.Protocol.Engines.Main.Get().Storage.Settings().LatestCommitment().Commitment(), node, tip.ID()) + committeeAtBlockSlot, exists := node.Protocol.Engines.Main.Get().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.Engines.Main.Get().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/testsuite/testsuite_options.go b/pkg/testsuite/testsuite_options.go index eec758168..6f467834e 100644 --- a/pkg/testsuite/testsuite_options.go +++ b/pkg/testsuite/testsuite_options.go @@ -5,7 +5,7 @@ import ( "time" "github.com/iotaledger/hive.go/runtime/options" - "github.com/iotaledger/iota-core/pkg/protocol/snapshotcreator" + "github.com/iotaledger/iota-core/pkg/testsuite/snapshotcreator" iotago "github.com/iotaledger/iota.go/v4" ) diff --git a/pkg/testsuite/transactions.go b/pkg/testsuite/transactions.go index 6a4502feb..fac8e8cc6 100644 --- a/pkg/testsuite/transactions.go +++ b/pkg/testsuite/transactions.go @@ -141,7 +141,7 @@ func (t *TestSuite) AssertTransactionInCacheConflicts(transactionConflicts map[* return ierrors.Errorf("AssertTransactionInCacheConflicts: %s: block %s does not exist", node.Name, transactionID) } - expectedConflictIDs := ds.NewSet(lo.Map(conflictAliases, t.TransactionFramework.TransactionID)...) + expectedConflictIDs := ds.NewSet(lo.Map(conflictAliases, t.DefaultWallet().TransactionID)...) actualConflictIDs := transactionFromCache.ConflictIDs() if expectedConflictIDs.Size() != actualConflictIDs.Size() { diff --git a/pkg/testsuite/transactions_framework.go b/pkg/testsuite/transactions_framework.go deleted file mode 100644 index 82c6c2a68..000000000 --- a/pkg/testsuite/transactions_framework.go +++ /dev/null @@ -1,603 +0,0 @@ -package testsuite - -import ( - "fmt" - "time" - - "github.com/iotaledger/hive.go/ierrors" - "github.com/iotaledger/hive.go/lo" - "github.com/iotaledger/hive.go/runtime/options" - "github.com/iotaledger/iota-core/pkg/protocol" - "github.com/iotaledger/iota-core/pkg/protocol/engine/utxoledger" - "github.com/iotaledger/iota-core/pkg/testsuite/mock" - iotago "github.com/iotaledger/iota.go/v4" - "github.com/iotaledger/iota.go/v4/builder" - "github.com/iotaledger/iota.go/v4/tpkg" -) - -type TransactionFramework struct { - apiProvider iotago.APIProvider - - wallet *mock.HDWallet - states map[string]*utxoledger.Output - signedTransactions map[string]*iotago.SignedTransaction - transactions map[string]*iotago.Transaction -} - -func NewTransactionFramework(protocol *protocol.Protocol, genesisSeed []byte) *TransactionFramework { - tf := &TransactionFramework{ - apiProvider: protocol, - states: make(map[string]*utxoledger.Output), - signedTransactions: make(map[string]*iotago.SignedTransaction), - transactions: make(map[string]*iotago.Transaction), - - wallet: mock.NewHDWallet("genesis", genesisSeed, 0), - } - - if err := protocol.Engines.Main.Get().Ledger.ForEachUnspentOutput(func(output *utxoledger.Output) bool { - tf.states[fmt.Sprintf("Genesis:%d", output.OutputID().Index())] = output - return true - }); err != nil { - panic(err) - } - - if len(tf.states) == 0 { - panic("no genesis outputs found") - } - - return tf -} - -func (t *TransactionFramework) RegisterTransaction(alias string, transaction *iotago.Transaction) { - currentAPI := t.apiProvider.CommittedAPI() - (lo.PanicOnErr(transaction.ID())).RegisterAlias(alias) - - t.transactions[alias] = transaction - - for outputID, output := range lo.PanicOnErr(transaction.OutputsSet()) { - clonedOutput := output.Clone() - actualOutputID := iotago.OutputIDFromTransactionIDAndIndex(lo.PanicOnErr(transaction.ID()), outputID.Index()) - if clonedOutput.Type() == iotago.OutputAccount { - if accountOutput, ok := clonedOutput.(*iotago.AccountOutput); ok && accountOutput.AccountID == iotago.EmptyAccountID { - accountOutput.AccountID = iotago.AccountIDFromOutputID(actualOutputID) - } - } - - t.states[fmt.Sprintf("%s:%d", alias, outputID.Index())] = utxoledger.CreateOutput(t.apiProvider, actualOutputID, iotago.EmptyBlockID, currentAPI.TimeProvider().SlotFromTime(time.Now()), clonedOutput, lo.PanicOnErr(iotago.OutputIDProofFromTransaction(transaction, outputID.Index()))) - } -} - -func (t *TransactionFramework) RegisterSignedTransaction(alias string, signedTransaction *iotago.SignedTransaction) { - (lo.PanicOnErr(signedTransaction.ID())).RegisterAlias(alias) - - t.signedTransactions[alias] = signedTransaction -} - -func (t *TransactionFramework) CreateSignedTransactionWithOptions(alias string, signingWallets []*mock.HDWallet, opts ...options.Option[builder.TransactionBuilder]) (*iotago.SignedTransaction, error) { - currentAPI := t.apiProvider.CommittedAPI() - - walletKeys := make([]iotago.AddressKeys, 0, len(signingWallets)*2) - for _, wallet := range signingWallets { - inputPrivateKey, _ := wallet.KeyPair() - // add address keys for both types of directly unlockable addresses to simplify the TransactionFramework - //nolint:forcetypeassert - walletKeys = append(walletKeys, iotago.NewAddressKeysForEd25519Address(wallet.Address(iotago.AddressEd25519).(*iotago.Ed25519Address), inputPrivateKey)) - //nolint:forcetypeassert - walletKeys = append(walletKeys, iotago.NewAddressKeysForImplicitAccountCreationAddress(wallet.Address(iotago.AddressImplicitAccountCreation).(*iotago.ImplicitAccountCreationAddress), inputPrivateKey)) - } - - txBuilder := builder.NewTransactionBuilder(currentAPI) - txBuilder.WithTransactionCapabilities(iotago.TransactionCapabilitiesBitMaskWithCapabilities(iotago.WithTransactionCanDoAnything())) - // Always add a random payload to randomize transaction ID. - randomPayload := tpkg.Rand12ByteArray() - txBuilder.AddTaggedDataPayload(&iotago.TaggedData{Tag: randomPayload[:], Data: randomPayload[:]}) - - signedTransaction, err := options.Apply(txBuilder, opts).Build(iotago.NewInMemoryAddressSigner(walletKeys...)) - if err == nil { - t.RegisterSignedTransaction(alias, signedTransaction) - t.RegisterTransaction(alias, signedTransaction.Transaction) - } - - return signedTransaction, err -} - -func (t *TransactionFramework) CreateSimpleTransaction(alias string, outputCount int, inputAliases ...string) (*iotago.SignedTransaction, error) { - inputStates, outputStates, signingWallets := t.CreateBasicOutputsEqually(outputCount, inputAliases...) - - return t.CreateSignedTransactionWithOptions(alias, signingWallets, WithInputs(inputStates), WithOutputs(outputStates)) -} - -func (t *TransactionFramework) CreateBasicOutputsEqually(outputCount int, inputAliases ...string) (consumedInputs utxoledger.Outputs, outputs iotago.Outputs[iotago.Output], signingWallets []*mock.HDWallet) { - inputStates := make([]*utxoledger.Output, 0, len(inputAliases)) - totalInputAmounts := iotago.BaseToken(0) - totalInputStoredMana := iotago.Mana(0) - - for _, inputAlias := range inputAliases { - output := t.Output(inputAlias) - inputStates = append(inputStates, output) - totalInputAmounts += output.BaseTokenAmount() - totalInputStoredMana += output.StoredMana() - } - - manaAmount := totalInputStoredMana / iotago.Mana(outputCount) - remainderMana := totalInputStoredMana - - tokenAmount := totalInputAmounts / iotago.BaseToken(outputCount) - remainderFunds := totalInputAmounts - - outputStates := make(iotago.Outputs[iotago.Output], 0, outputCount) - for i := 0; i < outputCount; i++ { - if i+1 == outputCount { - tokenAmount = remainderFunds - manaAmount = remainderMana - } - remainderFunds -= tokenAmount - remainderMana -= manaAmount - - outputStates = append(outputStates, &iotago.BasicOutput{ - Amount: tokenAmount, - Mana: manaAmount, - Conditions: iotago.BasicOutputUnlockConditions{ - &iotago.AddressUnlockCondition{Address: t.DefaultAddress()}, - }, - Features: iotago.BasicOutputFeatures{}, - }) - } - - return inputStates, outputStates, []*mock.HDWallet{t.wallet} -} - -func (t *TransactionFramework) CreateBasicOutputs(amountDistribution []iotago.BaseToken, manaDistribution []iotago.Mana, inputAliases ...string) (consumedInputs utxoledger.Outputs, outputs iotago.Outputs[iotago.Output], signingWallets []*mock.HDWallet) { - if len(amountDistribution) != len(manaDistribution) { - panic("amount and mana distributions should have the same length") - } - - inputStates := make([]*utxoledger.Output, 0, len(inputAliases)) - totalInputAmounts := iotago.BaseToken(0) - totalInputStoredMana := iotago.Mana(0) - - for _, inputAlias := range inputAliases { - output := t.Output(inputAlias) - inputStates = append(inputStates, output) - totalInputAmounts += output.BaseTokenAmount() - totalInputStoredMana += output.StoredMana() - } - - if lo.Sum(amountDistribution...) != totalInputAmounts { - panic("amount on input and output side must be equal") - } - - outputStates := make(iotago.Outputs[iotago.Output], 0, len(amountDistribution)) - for idx, outputAmount := range amountDistribution { - outputStates = append(outputStates, &iotago.BasicOutput{ - Amount: outputAmount, - Mana: manaDistribution[idx], - Conditions: iotago.BasicOutputUnlockConditions{ - &iotago.AddressUnlockCondition{Address: t.DefaultAddress()}, - }, - Features: iotago.BasicOutputFeatures{}, - }) - } - - return inputStates, outputStates, []*mock.HDWallet{t.wallet} -} - -func (t *TransactionFramework) CreateAccountFromInput(inputAlias string, opts ...options.Option[builder.AccountOutputBuilder]) (utxoledger.Outputs, iotago.Outputs[iotago.Output], []*mock.HDWallet) { - input := t.Output(inputAlias) - - accountOutput := options.Apply(builder.NewAccountOutputBuilder(t.DefaultAddress(), t.DefaultAddress(), input.BaseTokenAmount()). - Mana(input.StoredMana()). - StateController(t.DefaultAddress()). - Governor(t.DefaultAddress()), - opts).MustBuild() - - outputStates := iotago.Outputs[iotago.Output]{accountOutput} - - // if amount was set by options, a remainder output needs to be created - if accountOutput.Amount != input.BaseTokenAmount() { - outputStates = append(outputStates, &iotago.BasicOutput{ - Amount: input.BaseTokenAmount() - accountOutput.Amount, - Mana: input.StoredMana() - accountOutput.Mana, - Conditions: iotago.BasicOutputUnlockConditions{ - &iotago.AddressUnlockCondition{Address: t.DefaultAddress()}, - }, - Features: iotago.BasicOutputFeatures{}, - }) - } - - return utxoledger.Outputs{input}, outputStates, []*mock.HDWallet{t.wallet} -} - -// CreateImplicitAccountFromInput creates an implicit account output. -func (t *TransactionFramework) CreateImplicitAccountFromInput(inputAlias string) (utxoledger.Outputs, iotago.Outputs[iotago.Output], *iotago.ImplicitAccountCreationAddress, []*mock.HDWallet) { - input := t.Output(inputAlias) - - //nolint:forcetypeassert - implicitAccountAddress := t.DefaultAddress(iotago.AddressImplicitAccountCreation).(*iotago.ImplicitAccountCreationAddress) - - basicOutput := &iotago.BasicOutput{ - Amount: input.BaseTokenAmount(), - Mana: input.StoredMana(), - Conditions: iotago.BasicOutputUnlockConditions{ - &iotago.AddressUnlockCondition{Address: implicitAccountAddress}, - }, - Features: iotago.BasicOutputFeatures{}, - } - - return utxoledger.Outputs{input}, iotago.Outputs[iotago.Output]{basicOutput}, implicitAccountAddress, []*mock.HDWallet{t.wallet} -} - -// CreateDelegationFromInput creates a new DelegationOutput with given options from an input. If the remainder Output -// is not created, then StoredMana from the input is not passed and can potentially be burned. -// In order not to burn it, it needs to be assigned manually in another output in the transaction. -func (t *TransactionFramework) CreateDelegationFromInput(inputAlias string, opts ...options.Option[builder.DelegationOutputBuilder]) (utxoledger.Outputs, iotago.Outputs[iotago.Output], []*mock.HDWallet) { - input := t.Output(inputAlias) - - delegationOutput := options.Apply(builder.NewDelegationOutputBuilder(&iotago.AccountAddress{}, t.DefaultAddress(), input.BaseTokenAmount()). - DelegatedAmount(input.BaseTokenAmount()), - opts).MustBuild() - - if delegationOutput.ValidatorAddress.AccountID() == iotago.EmptyAccountID || - delegationOutput.DelegatedAmount == 0 || - delegationOutput.StartEpoch == 0 { - panic(fmt.Sprintf("delegation output created incorrectly %+v", delegationOutput)) - } - - outputStates := iotago.Outputs[iotago.Output]{delegationOutput} - - // if options set an Amount, a remainder output needs to be created - if delegationOutput.Amount != input.BaseTokenAmount() { - outputStates = append(outputStates, &iotago.BasicOutput{ - Amount: input.BaseTokenAmount() - delegationOutput.Amount, - Mana: input.StoredMana(), - Conditions: iotago.BasicOutputUnlockConditions{ - &iotago.AddressUnlockCondition{Address: t.DefaultAddress()}, - }, - Features: iotago.BasicOutputFeatures{}, - }) - } - - return utxoledger.Outputs{input}, outputStates, []*mock.HDWallet{t.wallet} -} - -// DelayedClaimingTransition transitions DelegationOutput into delayed claiming state by setting DelegationID and EndEpoch. -func (t *TransactionFramework) DelayedClaimingTransition(inputAlias string, delegationEndEpoch iotago.EpochIndex) (utxoledger.Outputs, iotago.Outputs[iotago.Output], []*mock.HDWallet) { - input := t.Output(inputAlias) - if input.OutputType() != iotago.OutputDelegation { - panic(ierrors.Errorf("%s is not a delegation output, cannot transition to delayed claiming state", inputAlias)) - } - - prevOutput, ok := input.Output().Clone().(*iotago.DelegationOutput) - if !ok { - panic(ierrors.Errorf("cloned output %s is not a delegation output, cannot transition to delayed claiming state", inputAlias)) - } - - delegationBuilder := builder.NewDelegationOutputBuilderFromPrevious(prevOutput).EndEpoch(delegationEndEpoch) - if prevOutput.DelegationID == iotago.EmptyDelegationID() { - delegationBuilder.DelegationID(iotago.DelegationIDFromOutputID(input.OutputID())) - } - - return utxoledger.Outputs{input}, iotago.Outputs[iotago.Output]{delegationBuilder.MustBuild()}, []*mock.HDWallet{t.wallet} -} - -func (t *TransactionFramework) DestroyAccount(alias string) (consumedInputs *utxoledger.Output, outputs iotago.Outputs[iotago.Output], signingWallets []*mock.HDWallet) { - output := t.Output(alias) - - outputStates := iotago.Outputs[iotago.Output]{&iotago.BasicOutput{ - Amount: output.BaseTokenAmount(), - Mana: output.StoredMana(), - Conditions: iotago.BasicOutputUnlockConditions{ - &iotago.AddressUnlockCondition{Address: t.DefaultAddress()}, - }, - Features: iotago.BasicOutputFeatures{}, - }} - - return output, outputStates, []*mock.HDWallet{t.wallet} -} - -func (t *TransactionFramework) TransitionAccount(alias string, opts ...options.Option[builder.AccountOutputBuilder]) (consumedInput *utxoledger.Output, outputs iotago.Outputs[iotago.Output], signingWallets []*mock.HDWallet) { - output, exists := t.states[alias] - if !exists { - panic(fmt.Sprintf("account with alias %s does not exist", alias)) - } - - accountOutput, ok := output.Output().Clone().(*iotago.AccountOutput) - if !ok { - panic(fmt.Sprintf("output with alias %s is not *iotago.AccountOutput", alias)) - } - - accountBuilder := builder.NewAccountOutputBuilderFromPrevious(accountOutput) - accountOutput = options.Apply(accountBuilder, opts).MustBuild() - - return output, iotago.Outputs[iotago.Output]{accountOutput}, []*mock.HDWallet{t.wallet} -} - -func (t *TransactionFramework) TransitionImplicitAccountToAccountOutput(alias string, opts ...options.Option[builder.AccountOutputBuilder]) (consumedInput utxoledger.Outputs, outputs iotago.Outputs[iotago.Output], signingWallets []*mock.HDWallet) { - input, exists := t.states[alias] - if !exists { - panic(fmt.Sprintf("output with alias %s does not exist", alias)) - } - - basicOutput, isBasic := input.Output().(*iotago.BasicOutput) - if !isBasic { - panic(fmt.Sprintf("output with alias %s is not *iotago.BasicOutput", alias)) - } - if basicOutput.UnlockConditionSet().Address().Address.Type() != iotago.AddressImplicitAccountCreation { - panic(fmt.Sprintf("output with alias %s is not an implicit account", alias)) - } - - accountOutput := options.Apply(builder.NewAccountOutputBuilder(t.DefaultAddress(), t.DefaultAddress(), input.BaseTokenAmount()). - Mana(input.StoredMana()). - AccountID(iotago.AccountIDFromOutputID(input.OutputID())), - opts).MustBuild() - - return utxoledger.Outputs{input}, iotago.Outputs[iotago.Output]{accountOutput}, []*mock.HDWallet{t.wallet} -} - -func (t *TransactionFramework) Output(alias string) *utxoledger.Output { - output, exists := t.states[alias] - if !exists { - panic(ierrors.Errorf("output with given alias does not exist %s", alias)) - } - - return output -} - -func (t *TransactionFramework) OutputID(alias string) iotago.OutputID { - return t.Output(alias).OutputID() -} - -func (t *TransactionFramework) SignedTransaction(alias string) *iotago.SignedTransaction { - transaction, exists := t.signedTransactions[alias] - if !exists { - panic(ierrors.Errorf("transaction with given alias does not exist %s", alias)) - } - - return transaction -} - -func (t *TransactionFramework) SignedTransactionID(alias string) iotago.SignedTransactionID { - return lo.PanicOnErr(t.SignedTransaction(alias).ID()) -} - -func (t *TransactionFramework) SignedTransactions(aliases ...string) []*iotago.SignedTransaction { - return lo.Map(aliases, t.SignedTransaction) -} - -func (t *TransactionFramework) SignedTransactionIDs(aliases ...string) []iotago.SignedTransactionID { - return lo.Map(aliases, t.SignedTransactionID) -} - -func (t *TransactionFramework) Transaction(alias string) *iotago.Transaction { - transaction, exists := t.transactions[alias] - if !exists { - panic(ierrors.Errorf("transaction with given alias does not exist %s", alias)) - } - - return transaction -} - -func (t *TransactionFramework) TransactionID(alias string) iotago.TransactionID { - return lo.PanicOnErr(t.Transaction(alias).ID()) -} - -func (t *TransactionFramework) Transactions(aliases ...string) []*iotago.Transaction { - return lo.Map(aliases, t.Transaction) -} - -func (t *TransactionFramework) TransactionIDs(aliases ...string) []iotago.TransactionID { - return lo.Map(aliases, t.TransactionID) -} - -func (t *TransactionFramework) DefaultAddress(addressType ...iotago.AddressType) iotago.Address { - return t.wallet.Address(addressType...) -} - -// DelegationOutput options - -func WithDelegatedAmount(delegatedAmount iotago.BaseToken) options.Option[builder.DelegationOutputBuilder] { - return func(delegationBuilder *builder.DelegationOutputBuilder) { - delegationBuilder.DelegatedAmount(delegatedAmount) - } -} - -func WithDelegatedValidatorAddress(validatorAddress *iotago.AccountAddress) options.Option[builder.DelegationOutputBuilder] { - return func(delegationBuilder *builder.DelegationOutputBuilder) { - delegationBuilder.ValidatorAddress(validatorAddress) - } -} - -func WithDelegationStartEpoch(startEpoch iotago.EpochIndex) options.Option[builder.DelegationOutputBuilder] { - return func(delegationBuilder *builder.DelegationOutputBuilder) { - delegationBuilder.StartEpoch(startEpoch) - } -} - -func WithDelegationEndEpoch(endEpoch iotago.EpochIndex) options.Option[builder.DelegationOutputBuilder] { - return func(delegationBuilder *builder.DelegationOutputBuilder) { - delegationBuilder.EndEpoch(endEpoch) - } -} - -func WithDelegationConditions(delegationConditions iotago.DelegationOutputUnlockConditions) options.Option[builder.DelegationOutputBuilder] { - return func(delegationBuilder *builder.DelegationOutputBuilder) { - delegationBuilder.Address(delegationConditions.MustSet().Address().Address) - } -} - -func WithDelegationAmount(amount iotago.BaseToken) options.Option[builder.DelegationOutputBuilder] { - return func(delegationBuilder *builder.DelegationOutputBuilder) { - delegationBuilder.Amount(amount) - } -} - -// BlockIssuer options - -func WithBlockIssuerFeature(keys iotago.BlockIssuerKeys, expirySlot iotago.SlotIndex) options.Option[builder.AccountOutputBuilder] { - return func(accountBuilder *builder.AccountOutputBuilder) { - accountBuilder.BlockIssuer(keys, expirySlot) - } -} - -func WithAddBlockIssuerKey(key iotago.BlockIssuerKey) options.Option[builder.AccountOutputBuilder] { - return func(accountBuilder *builder.AccountOutputBuilder) { - transition := accountBuilder.GovernanceTransition() - transition.BlockIssuerTransition().AddKeys(key) - } -} - -func WithBlockIssuerKeys(keys iotago.BlockIssuerKeys) options.Option[builder.AccountOutputBuilder] { - return func(accountBuilder *builder.AccountOutputBuilder) { - transition := accountBuilder.GovernanceTransition() - transition.BlockIssuerTransition().Keys(keys) - } -} - -func WithBlockIssuerExpirySlot(expirySlot iotago.SlotIndex) options.Option[builder.AccountOutputBuilder] { - return func(accountBuilder *builder.AccountOutputBuilder) { - transition := accountBuilder.GovernanceTransition() - transition.BlockIssuerTransition().ExpirySlot(expirySlot) - } -} - -func WithStakingFeature(amount iotago.BaseToken, fixedCost iotago.Mana, startEpoch iotago.EpochIndex, optEndEpoch ...iotago.EpochIndex) options.Option[builder.AccountOutputBuilder] { - return func(accountBuilder *builder.AccountOutputBuilder) { - accountBuilder.Staking(amount, fixedCost, startEpoch, optEndEpoch...) - } -} - -// Account options - -func WithAccountMana(mana iotago.Mana) options.Option[builder.AccountOutputBuilder] { - return func(accountBuilder *builder.AccountOutputBuilder) { - accountBuilder.Mana(mana) - } -} - -func WithAccountAmount(amount iotago.BaseToken) options.Option[builder.AccountOutputBuilder] { - return func(accountBuilder *builder.AccountOutputBuilder) { - accountBuilder.Amount(amount) - } -} - -func WithAccountIncreasedFoundryCounter(diff uint32) options.Option[builder.AccountOutputBuilder] { - return func(accountBuilder *builder.AccountOutputBuilder) { - accountBuilder.FoundriesToGenerate(diff) - } -} - -func WithAccountImmutableFeatures(features iotago.AccountOutputImmFeatures) options.Option[builder.AccountOutputBuilder] { - return func(accountBuilder *builder.AccountOutputBuilder) { - for _, feature := range features.MustSet() { - switch feature.Type() { - case iotago.FeatureMetadata: - //nolint:forcetypeassert - accountBuilder.ImmutableMetadata(feature.(*iotago.MetadataFeature).Data) - case iotago.FeatureSender: - //nolint:forcetypeassert - accountBuilder.ImmutableSender(feature.(*iotago.SenderFeature).Address) - } - } - } -} - -func WithAccountConditions(conditions iotago.AccountOutputUnlockConditions) options.Option[builder.AccountOutputBuilder] { - return func(accountBuilder *builder.AccountOutputBuilder) { - for _, condition := range conditions.MustSet() { - switch condition.Type() { - case iotago.UnlockConditionStateControllerAddress: - //nolint:forcetypeassert - accountBuilder.StateController(condition.(*iotago.StateControllerAddressUnlockCondition).Address) - case iotago.UnlockConditionGovernorAddress: - //nolint:forcetypeassert - accountBuilder.Governor(condition.(*iotago.GovernorAddressUnlockCondition).Address) - } - } - } -} - -// TransactionBuilder options - -func WithInputs(inputs utxoledger.Outputs) options.Option[builder.TransactionBuilder] { - return func(txBuilder *builder.TransactionBuilder) { - for _, input := range inputs { - switch input.OutputType() { - case iotago.OutputFoundry: - // For foundries we need to unlock the alias - txBuilder.AddInput(&builder.TxInput{ - UnlockTarget: input.Output().UnlockConditionSet().ImmutableAccount().Address, - InputID: input.OutputID(), - Input: input.Output(), - }) - case iotago.OutputAccount: - // For alias we need to unlock the state controller - txBuilder.AddInput(&builder.TxInput{ - UnlockTarget: input.Output().UnlockConditionSet().StateControllerAddress().Address, - InputID: input.OutputID(), - Input: input.Output(), - }) - default: - txBuilder.AddInput(&builder.TxInput{ - UnlockTarget: input.Output().UnlockConditionSet().Address().Address, - InputID: input.OutputID(), - Input: input.Output(), - }) - } - } - } -} - -func WithAccountInput(input *utxoledger.Output, governorTransition bool) options.Option[builder.TransactionBuilder] { - return func(txBuilder *builder.TransactionBuilder) { - switch input.OutputType() { - case iotago.OutputAccount: - address := input.Output().UnlockConditionSet().StateControllerAddress().Address - if governorTransition { - address = input.Output().UnlockConditionSet().GovernorAddress().Address - } - txBuilder.AddInput(&builder.TxInput{ - UnlockTarget: address, - InputID: input.OutputID(), - Input: input.Output(), - }) - default: - panic("only OutputAccount can be added as account input") - } - } -} - -func WithAllotments(allotments iotago.Allotments) options.Option[builder.TransactionBuilder] { - return func(txBuilder *builder.TransactionBuilder) { - for _, allotment := range allotments { - txBuilder.IncreaseAllotment(allotment.AccountID, allotment.Value) - } - } -} - -func WithSlotCreated(creationSlot iotago.SlotIndex) options.Option[builder.TransactionBuilder] { - return func(txBuilder *builder.TransactionBuilder) { - txBuilder.SetCreationSlot(creationSlot) - } -} - -func WithContextInputs(contextInputs iotago.TxEssenceContextInputs) options.Option[builder.TransactionBuilder] { - return func(txBuilder *builder.TransactionBuilder) { - for _, input := range contextInputs { - txBuilder.AddContextInput(input) - } - } -} - -func WithOutputs(outputs iotago.Outputs[iotago.Output]) options.Option[builder.TransactionBuilder] { - return func(txBuilder *builder.TransactionBuilder) { - for _, output := range outputs { - txBuilder.AddOutput(output) - } - } -} - -func WithTaggedDataPayload(payload *iotago.TaggedData) options.Option[builder.TransactionBuilder] { - return func(txBuilder *builder.TransactionBuilder) { - txBuilder.AddTaggedDataPayload(payload) - } -} 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/pkg/utils/rand.go b/pkg/utils/rand.go index b6ff8e63a..9e35787f2 100644 --- a/pkg/utils/rand.go +++ b/pkg/utils/rand.go @@ -111,10 +111,17 @@ func RandNFTID() iotago.NFTID { } func RandAccountID() iotago.AccountID { - alias := iotago.AccountID{} - copy(alias[:], RandBytes(iotago.AccountIDLength)) + accountID := iotago.AccountID{} + copy(accountID[:], RandBytes(iotago.AccountIDLength)) - return alias + return accountID +} + +func RandAnchorID() iotago.AnchorID { + anchorID := iotago.AnchorID{} + copy(anchorID[:], RandBytes(iotago.AnchorIDLength)) + + return anchorID } func RandSlotIndex() iotago.SlotIndex { @@ -146,7 +153,7 @@ func RandAddress(addressType iotago.AddressType) iotago.Address { } func RandOutputType() iotago.OutputType { - outputTypes := []iotago.OutputType{iotago.OutputBasic, iotago.OutputAccount, iotago.OutputFoundry, iotago.OutputNFT, iotago.OutputDelegation} + outputTypes := []iotago.OutputType{iotago.OutputBasic, iotago.OutputAccount, iotago.OutputAnchor, iotago.OutputFoundry, iotago.OutputNFT, iotago.OutputDelegation} return outputTypes[RandomIntn(len(outputTypes)-1)] } @@ -181,12 +188,27 @@ func RandOutputOnAddressWithAmount(outputType iotago.OutputType, address iotago. }, Features: iotago.BasicOutputFeatures{}, } + case iotago.OutputAccount: //nolint:forcetypeassert // we already checked the type iotaOutput = &iotago.AccountOutput{ Amount: amount, AccountID: RandAccountID(), Conditions: iotago.AccountOutputUnlockConditions{ + &iotago.AddressUnlockCondition{ + Address: address, + }, + }, + Features: iotago.AccountOutputFeatures{}, + ImmutableFeatures: iotago.AccountOutputImmFeatures{}, + } + + case iotago.OutputAnchor: + //nolint:forcetypeassert // we already checked the type + iotaOutput = &iotago.AnchorOutput{ + Amount: amount, + AnchorID: RandAnchorID(), + Conditions: iotago.AnchorOutputUnlockConditions{ &iotago.StateControllerAddressUnlockCondition{ Address: address, }, @@ -195,9 +217,10 @@ func RandOutputOnAddressWithAmount(outputType iotago.OutputType, address iotago. }, }, StateMetadata: make([]byte, 0), - Features: iotago.AccountOutputFeatures{}, - ImmutableFeatures: iotago.AccountOutputImmFeatures{}, + Features: iotago.AnchorOutputFeatures{}, + ImmutableFeatures: iotago.AnchorOutputImmFeatures{}, } + case iotago.OutputFoundry: if address.Type() != iotago.AddressAccount { panic("not an alias address") @@ -221,6 +244,7 @@ func RandOutputOnAddressWithAmount(outputType iotago.OutputType, address iotago. Features: iotago.FoundryOutputFeatures{}, ImmutableFeatures: iotago.FoundryOutputImmFeatures{}, } + case iotago.OutputNFT: //nolint:forcetypeassert // we already checked the type iotaOutput = &iotago.NFTOutput{ @@ -234,6 +258,7 @@ func RandOutputOnAddressWithAmount(outputType iotago.OutputType, address iotago. Features: iotago.NFTOutputFeatures{}, ImmutableFeatures: iotago.NFTOutputImmFeatures{}, } + case iotago.OutputDelegation: //nolint:forcetypeassert // we already checked the type iotaOutput = &iotago.DelegationOutput{ @@ -249,6 +274,7 @@ func RandOutputOnAddressWithAmount(outputType iotago.OutputType, address iotago. }, }, } + default: panic("unhandled output type") } 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..3cfd3f017 100644 --- a/tools/docker-network/docker-compose.yml +++ b/tools/docker-network/docker-compose.yml @@ -4,205 +4,295 @@ 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: + - "8050:14265/tcp" # REST-API + - "8051:8081/tcp" # Dashboard + - "6051:6061/tcp" # pprof + - "9059:9029/tcp" # INX + volumes: + - ./docker-network.snapshot:/app/data/snapshot.bin + - ./config.json:/app/config.json:ro command: > ${COMMON_CONFIG} ${MANUALPEERING_CONFIG} --p2p.identityPrivateKey=08735375679f3d8031353e94282ed1d65119e5c288fe56d6639d9184a3f978fee8febfedff11cc376daea0f59c395ae2e9a870a25ac4e36093000fbf4d0e8f18 - --validator.enabled=true - --validator.ignoreBootstrapped=true - --validator.account=0x907c02e9302e0f0571f10f885594e56d8c54ff0708ab7a39bc1b74d396b93b12 - --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: + - "8060:14265/tcp" # REST-API + - "8061:8081/tcp" # Dashboard + - "6061:6061/tcp" # pprof + - "9069: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} --p2p.identityPrivateKey=ba771419c52132a0dfb2521ed18667813f398da159010a55a0a482af939affb92d3338789ad4a07a7631b91791deb11f82ed5dc612822f24275e9f7a313b691f - --validator.enabled=true - --validator.account=0x375358f92cc94750669598b0aaa55a6ff73310b90710e1fad524c0f911be0fea - --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: + - "8070:14265/tcp" # REST-API + - "8071:8081/tcp" # Dashboard + - "6071:6061/tcp" # pprof + - "9079: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} --p2p.identityPrivateKey=a6261ac049755675ff1437654ca9f83b305055f01ff08c4f039209ef5a4a7d96d06fb61df77a8815209a8f4d204226dee593e50d0ec897ec440a2c1fbde77656 - --validator.enabled=true - --validator.account=0x6aee704f25558e8aa7630fed0121da53074188abc423b3c5810f80be4936eb6e - --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: + - "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 - 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: + - "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: - - "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:9011 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 + networks: + - iota-core + ports: + - "8088:8091/tcp" # Faucet Frontend environment: - "FAUCET_PRV_KEY=de52b9964dda96564e9fab362ab16c2669c715c6a2a853bece8a25fc58c599755b938327ea463e0c323c0fd44f6fc1843ed94daecc6909c6043d06b7152e4737" - command: - - "--inx.address=validator-1:9029" - - "--faucet.bindAddress=inx-faucet:8091" - ports: - - "8088:8091/tcp" # faucet frontend + command: > + --inx.address=node-1-validator:9029 + --faucet.bindAddress=inx-faucet:8091 + --faucet.rateLimit.enabled=false + + inx-validator-1: + image: iotaledger/inx-validator:1.0-alpha + stop_grace_period: 1m + restart: unless-stopped + depends_on: + node-1-validator: + condition: service_started networks: - iota-core + environment: + - "VALIDATOR_PRV_KEY=443a988ea61797651217de1f4662d4d6da11fd78e67f94511453bf6576045a05293dc170d9a59474e6d81cfba7f7d924c09b25d7166bcfba606e53114d0a758b" + command: > + --logger.level=debug + --inx.address=node-1-validator:9029 + --validator.ignoreBootstrapped=true + --validator.accountAddress=rms1pzg8cqhfxqhq7pt37y8cs4v5u4kcc48lquy2k73ehsdhf5ukhya3y5rx2w6 - prometheus: - image: prom/prometheus:latest - profiles: - - grafana - ports: - - "9090:9090" - command: - - --config.file=/run/secrets/prometheus.yml - secrets: - - prometheus.yml + inx-validator-2: + image: iotaledger/inx-validator:1.0-alpha + stop_grace_period: 1m + restart: unless-stopped + depends_on: + node-2-validator: + condition: service_started networks: - iota-core - depends_on: - - validator-1 - - grafana: - image: grafana/grafana:9.5.6 - profiles: - - grafana environment: - - GF_ALERTING_ENABLED=true - - GF_UNIFIED_ALERTING_ENABLED=false - - GF_DASHBOARDS_DEFAULT_HOME_DASHBOARD_PATH=/etc/grafana/provisioning/dashboards/local_dashboard.json + - "VALIDATOR_PRV_KEY=3a5d39f8b60367a17fd54dac2a32c172c8e1fd6cf74ce65f1e13edba565f281705c1de274451db8de8182d64c6ee0dca3ae0c9077e0b4330c976976171d79064" + command: > + --logger.level=debug + --inx.address=node-2-validator:9029 + --validator.accountAddress=rms1pqm4xk8e9ny5w5rxjkvtp249tfhlwvcshyr3pc0665jvp7g3hc875k538hl + + inx-validator-3: + image: iotaledger/inx-validator:1.0-alpha + stop_grace_period: 1m + restart: unless-stopped + depends_on: + node-3-validator: + condition: service_started networks: - iota-core - ports: - - "3000:3000" - volumes: - - grafana_data:/var/lib/grafana - - ./grafana/:/etc/grafana/:ro + environment: + - "VALIDATOR_PRV_KEY=db39d2fde6301d313b108dc9db1ee724d0f405f6fde966bd776365bc5f4a5fb31e4b21eb51dcddf65c20db1065e1f1514658b23a3ddbf48d30c0efc926a9a648" + command: > + --logger.level=debug + --inx.address=node-3-validator:9029 + --validator.accountAddress=rms1pp4wuuz0y42caz48vv876qfpmffswsvg40zz8v79sy8cp0jfxm4kunflcgt # 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..6cac8241e 100755 --- a/tools/docker-network/run.sh +++ b/tools/docker-network/run.sh @@ -6,20 +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]' - exit 0 + echo 'Call with ./run [replicas=1|2|3|...] [monitoring=0|1] [feature=0|1]' + exit 0 fi REPLICAS=${1:-1} -GRAFANA=${2:-0} -FEATURE=${3:-0} - -DOCKER_COMPOSE_FILE=docker-compose.yml -if [ $FEATURE -ne 0 ] -then - DOCKER_COMPOSE_FILE=docker-compose-feature.yml -fi - +MONITORING=${2:-0} export DOCKER_BUILDKIT=1 export COMPOSE_DOCKER_CLI_BUILD=1 @@ -29,51 +21,53 @@ echo "Build iota-core" export DOCKER_BUILD_CONTEXT="../../" export DOCKERFILE_PATH="./Dockerfile.dev" -if [[ "$WITH_GO_WORK" -eq 1 ]] -then +if [[ "$WITH_GO_WORK" -eq 1 ]]; then export DOCKER_BUILD_CONTEXT="../../../" export DOCKERFILE_PATH="./iota-core/Dockerfile.dev" fi # Allow docker compose to build and cache an image echo $DOCKER_BUILD_CONTEXT $DOCKERFILE_PATH -docker compose -f $DOCKER_COMPOSE_FILE build --build-arg WITH_GO_WORK=${WITH_GO_WORK:-0} --build-arg DOCKER_BUILD_CONTEXT=${DOCKER_BUILD_CONTEXT} --build-arg DOCKERFILE_PATH=${DOCKERFILE_PATH} +docker compose build --build-arg WITH_GO_WORK=${WITH_GO_WORK:-0} --build-arg DOCKER_BUILD_CONTEXT=${DOCKER_BUILD_CONTEXT} --build-arg DOCKERFILE_PATH=${DOCKERFILE_PATH} -docker compose pull inx-indexer inx-blockissuer inx-faucet +docker compose pull inx-indexer inx-blockissuer inx-faucet inx-validator-1 # check exit code of builder -if [ $? -ne 0 ] -then +if [ $? -ne 0 ]; then echo "Building failed. Please fix and try again!" exit 1 fi # create snapshot file echo "Create snapshot" -if [ $FEATURE -ne 0 ] -then - pushd ../genesis-snapshot; go run -tags=rocksdb . --config feature -else - pushd ../genesis-snapshot; go run -tags=rocksdb . --config docker --seed 7R1itJx5hVuo9w9hjg5cwKFmek4HMSoBDgJZN8hKGxih -fi -popd -mv ../genesis-snapshot/*.snapshot . -chmod o+r *.snapshot +# Run Go command in Docker container +docker run --rm \ + --user $(id -u) \ + -v "$(realpath $(pwd)/../../):/workspace" \ + -v "${HOME}/.cache/go-build:/go-cache" \ + -v "${HOME}/go/pkg/mod:/go-mod-cache" \ + -e GOCACHE="/go-cache" \ + -e GOMODCACHE="/go-mod-cache" \ + -w "/workspace/tools/genesis-snapshot" \ + golang:1.21 go run -tags=rocksdb . --config docker --seed 7R1itJx5hVuo9w9hjg5cwKFmek4HMSoBDgJZN8hKGxih + +# Move and set permissions for the .snapshot file +mv -f ../genesis-snapshot/*.snapshot . +chmod o+r *.snapshot -echo "Run iota-core network with ${DOCKER_COMPOSE_FILE}" +echo "Run iota-core network" # IOTA_CORE_PEER_REPLICAS is used in docker-compose.yml to determine how many replicas to create 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 ] -then - PROFILES+=("grafana") +if [ $MONITORING -ne 0 ]; then + PROFILES+=("monitoring") fi export COMPOSE_PROFILES=$(join , ${PROFILES[@]}) -docker compose -f $DOCKER_COMPOSE_FILE up +docker compose up echo "Clean up docker resources" docker compose down -v diff --git a/tools/evil-spammer/.gitignore b/tools/evil-spammer/.gitignore deleted file mode 100644 index 301d5d62d..000000000 --- a/tools/evil-spammer/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.log -*.json -*.dat \ No newline at end of file diff --git a/tools/evil-spammer/accountwallet/commands.go b/tools/evil-spammer/accountwallet/commands.go deleted file mode 100644 index 37a7843cb..000000000 --- a/tools/evil-spammer/accountwallet/commands.go +++ /dev/null @@ -1,40 +0,0 @@ -package accountwallet - -import ( - "fmt" - - "github.com/iotaledger/hive.go/ierrors" - iotago "github.com/iotaledger/iota.go/v4" -) - -func (a *AccountWallet) CreateAccount(params *CreateAccountParams) (iotago.AccountID, error) { - implicitAccountOutput, privateKey, err := a.getFunds(params.Amount, iotago.AddressImplicitAccountCreation) - if err != nil { - return iotago.EmptyAccountID, ierrors.Wrap(err, "Failed to create account") - } - - accountID := a.registerAccount(params.Alias, implicitAccountOutput.OutputID, a.latestUsedIndex, privateKey) - - fmt.Printf("Created account %s with %d tokens\n", accountID.ToHex(), params.Amount) - - return accountID, nil -} - -func (a *AccountWallet) DestroyAccount(params *DestroyAccountParams) error { - return a.destroyAccount(params.AccountAlias) -} - -func (a *AccountWallet) ListAccount() error { - fmt.Printf("%-10s \t%-33s\n\n", "Alias", "AccountID") - for _, accData := range a.accountsAliases { - fmt.Printf("%-10s \t", accData.Alias) - fmt.Printf("%-33s ", accData.Account.ID().ToHex()) - fmt.Printf("\n") - } - - return nil -} - -func (a *AccountWallet) AllotToAccount(params *AllotAccountParams) error { - return nil -} diff --git a/tools/evil-spammer/accountwallet/config.go b/tools/evil-spammer/accountwallet/config.go deleted file mode 100644 index a363cb8ad..000000000 --- a/tools/evil-spammer/accountwallet/config.go +++ /dev/null @@ -1,219 +0,0 @@ -package accountwallet - -import ( - "encoding/json" - "os" - - "github.com/iotaledger/hive.go/ds/types" - "github.com/iotaledger/iota-core/tools/evil-spammer/models" -) - -// commands - -type AccountOperation int - -const ( - OperationCreateAccount AccountOperation = iota - OperationConvertAccount - OperationDestroyAccound - OperationAllotAccount - OperationDelegateAccount - OperationStakeAccount - OperationListAccounts - OperationUpdateAccount - - CmdNameCreateAccount = "create" - CmdNameConvertAccount = "convert" - CmdNameDestroyAccount = "destroy" - CmdNameAllotAccount = "allot" - CmdNameDelegateAccount = "delegate" - CmdNameStakeAccount = "stake" - CmdNameListAccounts = "list" - CmdNameUpdateAccount = "update" -) - -func (a AccountOperation) String() string { - return []string{ - CmdNameCreateAccount, - CmdNameConvertAccount, - CmdNameDestroyAccount, - CmdNameAllotAccount, - CmdNameDelegateAccount, - CmdNameStakeAccount, - CmdNameListAccounts, - CmdNameUpdateAccount, - }[a] -} - -func AvailableCommands(cmd string) bool { - availableCommands := map[string]types.Empty{ - CmdNameCreateAccount: types.Void, - CmdNameConvertAccount: types.Void, - CmdNameDestroyAccount: types.Void, - CmdNameAllotAccount: types.Void, - CmdNameDelegateAccount: types.Void, - CmdNameStakeAccount: types.Void, - CmdNameListAccounts: types.Void, - CmdNameUpdateAccount: types.Void, - } - - _, ok := availableCommands[cmd] - return ok -} - -type Configuration struct { - BindAddress string `json:"bindAddress,omitempty"` - AccountStatesFile string `json:"accountStatesFile,omitempty"` - GenesisSeed string `json:"genesisSeed,omitempty"` - BlockIssuerPrivateKey string `json:"blockIssuerPrivateKey,omitempty"` - AccountID string `json:"accountID,omitempty"` -} - -var accountConfigFile = "config.json" - -var ( - dockerAccountConfigJSON = `{ - "bindAddress": "http://localhost:8080", - "accountStatesFile": "wallet.dat", - "genesisSeed": "7R1itJx5hVuo9w9hjg5cwKFmek4HMSoBDgJZN8hKGxih", - "blockIssuerPrivateKey": "db39d2fde6301d313b108dc9db1ee724d0f405f6fde966bd776365bc5f4a5fb31e4b21eb51dcddf65c20db1065e1f1514658b23a3ddbf48d30c0efc926a9a648", - "accountID": "0x6aee704f25558e8aa7630fed0121da53074188abc423b3c5810f80be4936eb6e"}` -) - -// LoadConfiguration loads the config file. -func LoadConfiguration() *Configuration { - // open config file - config := new(Configuration) - file, err := os.Open(accountConfigFile) - if err != nil { - if !os.IsNotExist(err) { - panic(err) - } - - //nolint:gosec // users should be able to read the file - if err = os.WriteFile(accountConfigFile, []byte(dockerAccountConfigJSON), 0o644); err != nil { - panic(err) - } - if file, err = os.Open(accountConfigFile); err != nil { - panic(err) - } - } - defer file.Close() - - // decode config file - if err = json.NewDecoder(file).Decode(config); err != nil { - panic(err) - } - - return config -} - -func SaveConfiguration(config *Configuration) { - // open config file - file, err := os.Open(accountConfigFile) - if err != nil { - panic(err) - } - defer file.Close() - - jsonConfigs, err := json.MarshalIndent(config, "", " ") - - if err != nil { - log.Errorf("failed to write configs to file %s", err) - } - - //nolint:gosec // users should be able to read the file - if err = os.WriteFile(accountConfigFile, jsonConfigs, 0o644); err != nil { - panic(err) - } -} - -type AccountSubcommands interface { - Type() AccountOperation -} - -type CreateAccountParams struct { - Alias string - Amount uint64 - NoBIF bool - Implicit bool -} - -func (c *CreateAccountParams) Type() AccountOperation { - return OperationCreateAccount -} - -type DestroyAccountParams struct { - AccountAlias string - ExpirySlot uint64 -} - -func (d *DestroyAccountParams) Type() AccountOperation { - return OperationDestroyAccound -} - -type AllotAccountParams struct { - Amount uint64 - To string - From string // if not set we use faucet -} - -func (a *AllotAccountParams) Type() AccountOperation { - return OperationAllotAccount -} - -type ConvertAccountParams struct { - AccountAlias string -} - -func (d *ConvertAccountParams) Type() AccountOperation { - return OperationConvertAccount -} - -type DelegateAccountParams struct { - Amount uint64 - To string - From string // if not set we use faucet -} - -func (a *DelegateAccountParams) Type() AccountOperation { - return OperationDelegateAccount -} - -type StakeAccountParams struct { - Alias string - Amount uint64 - FixedCost uint64 - StartEpoch uint64 - EndEpoch uint64 -} - -func (a *StakeAccountParams) Type() AccountOperation { - return OperationStakeAccount -} - -type UpdateAccountParams struct { - Alias string - BlockIssuerKey string - Mana uint64 - Amount uint64 - ExpirySlot uint64 -} - -func (a *UpdateAccountParams) Type() AccountOperation { - return OperationUpdateAccount -} - -type NoAccountParams struct { - Operation AccountOperation -} - -func (a *NoAccountParams) Type() AccountOperation { - return a.Operation -} - -type StateData struct { - Seed string `serix:"0,mapKey=seed,lengthPrefixType=uint8"` - LastUsedIndex uint64 `serix:"1,mapKey=lastUsedIndex"` - AccountsData []*models.AccountState `serix:"2,mapKey=accounts,lengthPrefixType=uint8"` -} diff --git a/tools/evil-spammer/accountwallet/faucet.go b/tools/evil-spammer/accountwallet/faucet.go deleted file mode 100644 index 6f67fdb8f..000000000 --- a/tools/evil-spammer/accountwallet/faucet.go +++ /dev/null @@ -1,284 +0,0 @@ -package accountwallet - -import ( - "context" - "fmt" - "sync" - "time" - - "github.com/mr-tron/base58" - - "github.com/iotaledger/hive.go/core/safemath" - "github.com/iotaledger/hive.go/ierrors" - "github.com/iotaledger/hive.go/lo" - "github.com/iotaledger/iota-core/pkg/blockhandler" - "github.com/iotaledger/iota-core/pkg/testsuite/mock" - "github.com/iotaledger/iota-core/tools/evil-spammer/models" - iotago "github.com/iotaledger/iota.go/v4" - "github.com/iotaledger/iota.go/v4/builder" - "github.com/iotaledger/iota.go/v4/nodeclient" - "github.com/iotaledger/iota.go/v4/nodeclient/apimodels" -) - -const ( - FaucetAccountAlias = "faucet" -) - -func (a *AccountWallet) RequestBlockBuiltData(clt *nodeclient.Client, issuerID iotago.AccountID) (*apimodels.CongestionResponse, *apimodels.IssuanceBlockHeaderResponse, iotago.Version, error) { - congestionResp, err := clt.Congestion(context.Background(), issuerID) - if err != nil { - return nil, nil, 0, ierrors.Wrapf(err, "failed to get congestion data for issuer %s", issuerID.ToHex()) - } - - issuerResp, err := clt.BlockIssuance(context.Background(), congestionResp.Slot) - if err != nil { - return nil, nil, 0, ierrors.Wrap(err, "failed to get block issuance data") - } - - version := clt.APIForSlot(congestionResp.Slot).Version() - - return congestionResp, issuerResp, version, nil -} - -func (a *AccountWallet) RequestFaucetFunds(clt models.Client, receiveAddr iotago.Address, amount iotago.BaseToken) (*models.Output, error) { - congestionResp, issuerResp, version, err := a.RequestBlockBuiltData(clt.Client(), a.faucet.account.ID()) - if err != nil { - return nil, ierrors.Wrapf(err, "failed to get block built data for issuer %s", a.faucet.account.ID().ToHex()) - } - - signedTx, err := a.faucet.prepareFaucetRequest(receiveAddr, amount, congestionResp.ReferenceManaCost) - if err != nil { - log.Errorf("failed to prepare faucet request: %s", err) - - return nil, err - } - - blkID, err := a.PostWithBlock(clt, signedTx, a.faucet.account, congestionResp, issuerResp, version) - if err != nil { - log.Errorf("failed to create block: %s", err) - - return nil, err - } - fmt.Println("block sent:", blkID.ToHex()) - - // set remainder output to be reused by the Faucet wallet - a.faucet.unspentOutput = &models.Output{ - OutputID: iotago.OutputIDFromTransactionIDAndIndex(lo.PanicOnErr(signedTx.Transaction.ID()), 1), - Address: a.faucet.genesisHdWallet.Address(iotago.AddressEd25519).(*iotago.Ed25519Address), - Index: 0, - Balance: signedTx.Transaction.Outputs[1].BaseTokenAmount(), - OutputStruct: signedTx.Transaction.Outputs[1], - } - - return &models.Output{ - OutputID: iotago.OutputIDFromTransactionIDAndIndex(lo.PanicOnErr(signedTx.Transaction.ID()), 0), - Address: receiveAddr, - Index: 0, - Balance: signedTx.Transaction.Outputs[0].BaseTokenAmount(), - OutputStruct: signedTx.Transaction.Outputs[0], - }, nil -} - -func (a *AccountWallet) PostWithBlock(clt models.Client, payload iotago.Payload, issuer blockhandler.Account, congestionResp *apimodels.CongestionResponse, issuerResp *apimodels.IssuanceBlockHeaderResponse, version iotago.Version) (iotago.BlockID, error) { - signedBlock, err := a.CreateBlock(payload, issuer, congestionResp, issuerResp, version) - if err != nil { - log.Errorf("failed to create block: %s", err) - - return iotago.EmptyBlockID, err - } - - blockID, err := clt.PostBlock(signedBlock) - if err != nil { - log.Errorf("failed to post block: %s", err) - - return iotago.EmptyBlockID, err - } - - return blockID, nil -} - -func (a *AccountWallet) CreateBlock(payload iotago.Payload, issuer blockhandler.Account, congestionResp *apimodels.CongestionResponse, issuerResp *apimodels.IssuanceBlockHeaderResponse, version iotago.Version) (*iotago.ProtocolBlock, error) { - issuingTime := time.Now() - issuingSlot := a.client.LatestAPI().TimeProvider().SlotFromTime(issuingTime) - apiForSlot := a.client.APIForSlot(issuingSlot) - - blockBuilder := builder.NewBasicBlockBuilder(apiForSlot) - - commitmentID, err := issuerResp.Commitment.ID() - if err != nil { - return nil, ierrors.Wrap(err, "failed to get commitment id") - } - - blockBuilder.ProtocolVersion(version) - blockBuilder.SlotCommitmentID(commitmentID) - blockBuilder.LatestFinalizedSlot(issuerResp.LatestFinalizedSlot) - blockBuilder.IssuingTime(time.Now()) - blockBuilder.StrongParents(issuerResp.StrongParents) - blockBuilder.WeakParents(issuerResp.WeakParents) - blockBuilder.ShallowLikeParents(issuerResp.ShallowLikeParents) - - blockBuilder.Payload(payload) - blockBuilder.CalculateAndSetMaxBurnedMana(congestionResp.ReferenceManaCost) - blockBuilder.Sign(issuer.ID(), issuer.PrivateKey()) - - blk, err := blockBuilder.Build() - if err != nil { - return nil, ierrors.Errorf("failed to build block: %w", err) - } - - return blk, nil -} - -type faucetParams struct { - faucetPrivateKey string - faucetAccountID string - genesisSeed string -} - -type faucet struct { - unspentOutput *models.Output - account blockhandler.Account - genesisHdWallet *mock.HDWallet - - clt models.Client - - sync.Mutex -} - -func newFaucet(clt models.Client, faucetParams *faucetParams) (*faucet, error) { - genesisSeed, err := base58.Decode(faucetParams.genesisSeed) - if err != nil { - log.Warnf("failed to decode base58 seed, using the default one: %v", err) - } - faucetAddr := mock.NewHDWallet("", genesisSeed, 0).Address(iotago.AddressEd25519) - - f := &faucet{ - clt: clt, - account: blockhandler.AccountFromParams(faucetParams.faucetAccountID, faucetParams.faucetPrivateKey), - genesisHdWallet: mock.NewHDWallet("", genesisSeed, 0), - } - - faucetUnspentOutput, faucetUnspentOutputID, faucetAmount, err := f.getGenesisOutputFromIndexer(clt, faucetAddr) - if err != nil { - return nil, ierrors.Wrap(err, "failed to get faucet output from indexer") - } - - f.unspentOutput = &models.Output{ - Address: faucetAddr.(*iotago.Ed25519Address), - Index: 0, - OutputID: faucetUnspentOutputID, - Balance: faucetAmount, - OutputStruct: faucetUnspentOutput, - } - - return f, nil -} - -func (f *faucet) getGenesisOutputFromIndexer(clt models.Client, faucetAddr iotago.DirectUnlockableAddress) (iotago.Output, iotago.OutputID, iotago.BaseToken, error) { - indexer, err := clt.Indexer() - if err != nil { - log.Errorf("account wallet failed due to failure in connecting to indexer") - - return nil, iotago.EmptyOutputID, 0, ierrors.Wrapf(err, "failed to get indexer from client") - } - - results, err := indexer.Outputs(context.Background(), &apimodels.BasicOutputsQuery{ - AddressBech32: faucetAddr.Bech32(iotago.PrefixTestnet), - }) - if err != nil { - return nil, iotago.EmptyOutputID, 0, ierrors.Wrap(err, "failed to prepare faucet unspent outputs indexer request") - } - - var ( - faucetUnspentOutput iotago.Output - faucetUnspentOutputID iotago.OutputID - faucetAmount iotago.BaseToken - ) - for results.Next() { - unspents, err := results.Outputs(context.TODO()) - if err != nil { - return nil, iotago.EmptyOutputID, 0, ierrors.Wrap(err, "failed to get faucet unspent outputs") - } - - faucetUnspentOutput = unspents[0] - faucetAmount = faucetUnspentOutput.BaseTokenAmount() - faucetUnspentOutputID = lo.Return1(results.Response.Items.OutputIDs())[0] - } - - return faucetUnspentOutput, faucetUnspentOutputID, faucetAmount, nil -} - -func (f *faucet) prepareFaucetRequest(receiveAddr iotago.Address, amount iotago.BaseToken, rmc iotago.Mana) (*iotago.SignedTransaction, error) { - remainderAmount, err := safemath.SafeSub(f.unspentOutput.Balance, amount) - if err != nil { - panic(err) - } - - txBuilder, remainderIndex, err := f.createFaucetTransactionNoManaHandling(receiveAddr, amount, remainderAmount) - if err != nil { - return nil, err - } - - rmcAllotedTxBuilder := txBuilder.Clone() - // faucet will allot exact mana to be burnt, rest of the mana is alloted to faucet output remainder - rmcAllotedTxBuilder.AllotRequiredManaAndStoreRemainingManaInOutput(txBuilder.CreationSlot(), rmc, f.account.ID(), remainderIndex) - - var signedTx *iotago.SignedTransaction - signedTx, err = rmcAllotedTxBuilder.Build(f.genesisHdWallet.AddressSigner()) - if err != nil { - log.Infof("WARN: failed to build tx with min required mana allotted, genesis potential mana was not enough, fallback to faucet account") - txBuilder.AllotAllMana(txBuilder.CreationSlot(), f.account.ID()) - if signedTx, err = txBuilder.Build(f.genesisHdWallet.AddressSigner()); err != nil { - return nil, ierrors.Wrapf(err, "failed to build transaction with all mana allotted, after not having enough mana required based on RMC") - } - } - - return signedTx, nil -} - -func (f *faucet) createFaucetTransactionNoManaHandling(receiveAddr iotago.Address, amount iotago.BaseToken, remainderAmount iotago.BaseToken) (*builder.TransactionBuilder, int, error) { - currentTime := time.Now() - currentSlot := f.clt.LatestAPI().TimeProvider().SlotFromTime(currentTime) - - apiForSlot := f.clt.APIForSlot(currentSlot) - txBuilder := builder.NewTransactionBuilder(apiForSlot) - - txBuilder.AddInput(&builder.TxInput{ - UnlockTarget: f.genesisHdWallet.Address(iotago.AddressEd25519).(*iotago.Ed25519Address), - InputID: f.unspentOutput.OutputID, - Input: f.unspentOutput.OutputStruct, - }) - - switch receiveAddr.(type) { - case *iotago.Ed25519Address: - txBuilder.AddOutput(&iotago.BasicOutput{ - Amount: amount, - Conditions: iotago.BasicOutputUnlockConditions{ - &iotago.AddressUnlockCondition{Address: receiveAddr}, - }, - }) - case *iotago.ImplicitAccountCreationAddress: - log.Infof("creating account %s", receiveAddr) - accOutputBuilder := builder.NewAccountOutputBuilder(receiveAddr, receiveAddr, amount) - output, err := accOutputBuilder.Build() - if err != nil { - log.Errorf("failed to build account output: %s", err) - - return nil, 0, err - } - txBuilder.AddOutput(output) - } - - // remainder output - remainderIndex := 1 - txBuilder.AddOutput(&iotago.BasicOutput{ - Amount: remainderAmount, - Conditions: iotago.BasicOutputUnlockConditions{ - &iotago.AddressUnlockCondition{Address: f.genesisHdWallet.Address(iotago.AddressEd25519).(*iotago.Ed25519Address)}, - }, - }) - txBuilder.AddTaggedDataPayload(&iotago.TaggedData{Tag: []byte("Faucet funds"), Data: []byte("to addr" + receiveAddr.String())}) - txBuilder.SetCreationSlot(currentSlot) - - return txBuilder, remainderIndex, nil -} diff --git a/tools/evil-spammer/accountwallet/options.go b/tools/evil-spammer/accountwallet/options.go deleted file mode 100644 index 51b28fcd7..000000000 --- a/tools/evil-spammer/accountwallet/options.go +++ /dev/null @@ -1,24 +0,0 @@ -package accountwallet - -import ( - "github.com/iotaledger/hive.go/runtime/options" -) - -// WithClientURL sets the client bind address. -func WithClientURL(url string) options.Option[AccountWallet] { - return func(w *AccountWallet) { - w.optsClientBindAddress = url - } -} - -func WithAccountStatesFile(fileName string) options.Option[AccountWallet] { - return func(w *AccountWallet) { - w.optsAccountStatesFile = fileName - } -} - -func WithFaucetAccountParams(params *faucetParams) options.Option[AccountWallet] { - return func(w *AccountWallet) { - w.optsFaucetParams = params - } -} diff --git a/tools/evil-spammer/accountwallet/wallet.go b/tools/evil-spammer/accountwallet/wallet.go deleted file mode 100644 index af8903e3a..000000000 --- a/tools/evil-spammer/accountwallet/wallet.go +++ /dev/null @@ -1,343 +0,0 @@ -package accountwallet - -import ( - "crypto/ed25519" - "os" - "time" - - "github.com/mr-tron/base58" - - "github.com/iotaledger/hive.go/ierrors" - "github.com/iotaledger/hive.go/runtime/options" - "github.com/iotaledger/hive.go/runtime/timeutil" - "github.com/iotaledger/iota-core/pkg/blockhandler" - "github.com/iotaledger/iota-core/pkg/testsuite/mock" - "github.com/iotaledger/iota-core/tools/evil-spammer/logger" - "github.com/iotaledger/iota-core/tools/evil-spammer/models" - iotago "github.com/iotaledger/iota.go/v4" - "github.com/iotaledger/iota.go/v4/builder" - "github.com/iotaledger/iota.go/v4/tpkg" -) - -var log = logger.New("AccountWallet") - -func Run(config *Configuration) (*AccountWallet, error) { - var opts []options.Option[AccountWallet] - if config.BindAddress != "" { - opts = append(opts, WithClientURL(config.BindAddress)) - } - if config.AccountStatesFile != "" { - opts = append(opts, WithAccountStatesFile(config.AccountStatesFile)) - } - - opts = append(opts, WithFaucetAccountParams(&faucetParams{ - genesisSeed: config.GenesisSeed, - faucetPrivateKey: config.BlockIssuerPrivateKey, - faucetAccountID: config.AccountID, - })) - - wallet, err := NewAccountWallet(opts...) - if err != nil { - return nil, ierrors.Wrap(err, "failed to create wallet") - } - - // load wallet - err = wallet.fromAccountStateFile() - if err != nil { - return nil, ierrors.Wrap(err, "failed to load wallet from file") - } - - return wallet, nil -} - -func SaveState(w *AccountWallet) error { - return w.toAccountStateFile() -} - -type AccountWallet struct { - faucet *faucet - seed [32]byte - - accountsAliases map[string]*models.AccountData - - //accountsStatus map[string]models.AccountMetadata - latestUsedIndex uint64 - - client *models.WebClient - - optsClientBindAddress string - optsAccountStatesFile string - optsFaucetParams *faucetParams - optsRequestTimeout time.Duration - optsRequestTicker time.Duration -} - -func NewAccountWallet(opts ...options.Option[AccountWallet]) (*AccountWallet, error) { - var initErr error - return options.Apply(&AccountWallet{ - accountsAliases: make(map[string]*models.AccountData), - seed: tpkg.RandEd25519Seed(), - optsRequestTimeout: time.Second * 120, - optsRequestTicker: time.Second * 5, - }, opts, func(w *AccountWallet) { - w.client, initErr = models.NewWebClient(w.optsClientBindAddress) - if initErr != nil { - return - } - - var f *faucet - f, initErr = newFaucet(w.client, w.optsFaucetParams) - if initErr != nil { - return - } - - w.faucet = f - w.accountsAliases[FaucetAccountAlias] = &models.AccountData{ - Alias: FaucetAccountAlias, - Status: models.AccountReady, - OutputID: iotago.EmptyOutputID, - Index: 0, - Account: w.faucet.account, - } - }), initErr -} - -// toAccountStateFile write account states to file. -func (a *AccountWallet) toAccountStateFile() error { - accounts := make([]*models.AccountState, 0) - - for _, acc := range a.accountsAliases { - accounts = append(accounts, models.AccountStateFromAccountData(acc)) - } - - stateBytes, err := a.client.LatestAPI().Encode(&StateData{ - Seed: base58.Encode(a.seed[:]), - LastUsedIndex: a.latestUsedIndex, - AccountsData: accounts, - }) - if err != nil { - return ierrors.Wrap(err, "failed to encode state") - } - - //nolint:gosec // users should be able to read the file - if err = os.WriteFile(a.optsAccountStatesFile, stateBytes, 0o644); err != nil { - return ierrors.Wrap(err, "failed to write account states to file") - } - - return nil -} - -func (a *AccountWallet) fromAccountStateFile() error { - walletStateBytes, err := os.ReadFile(a.optsAccountStatesFile) - if err != nil { - if !os.IsNotExist(err) { - return ierrors.Wrap(err, "failed to read file") - } - return nil - } - - var data StateData - _, err = a.client.LatestAPI().Decode(walletStateBytes, &data) - if err != nil { - return ierrors.Wrap(err, "failed to decode from file") - } - - // copy seeds - decodedSeeds, err := base58.Decode(data.Seed) - if err != nil { - return ierrors.Wrap(err, "failed to decode seed") - } - copy(a.seed[:], decodedSeeds) - - // set latest used index - a.latestUsedIndex = data.LastUsedIndex - - // account data - for _, acc := range data.AccountsData { - a.accountsAliases[acc.Alias] = acc.ToAccountData() - if acc.Alias == FaucetAccountAlias { - a.accountsAliases[acc.Alias].Status = models.AccountReady - } - } - - return nil -} - -func (a *AccountWallet) registerAccount(alias string, outputID iotago.OutputID, index uint64, privKey ed25519.PrivateKey) iotago.AccountID { - accountID := iotago.AccountIDFromOutputID(outputID) - account := blockhandler.NewEd25519Account(accountID, privKey) - - a.accountsAliases[alias] = &models.AccountData{ - Alias: alias, - Account: account, - Status: models.AccountPending, - OutputID: outputID, - Index: index, - } - - return accountID -} - -func (a *AccountWallet) updateAccountStatus(alias string, status models.AccountStatus) (*models.AccountData, bool) { - accData, exists := a.accountsAliases[alias] - if !exists { - return nil, false - } - - if accData.Status == status { - return accData, false - } - - accData.Status = status - a.accountsAliases[alias] = accData - - return accData, true -} - -func (a *AccountWallet) GetReadyAccount(alias string) (*models.AccountData, error) { - accData, exists := a.accountsAliases[alias] - if !exists { - return nil, ierrors.Errorf("account with alias %s does not exist", alias) - } - - // check if account is ready (to be included in a commitment) - ready := a.isAccountReady(accData) - if !ready { - return nil, ierrors.Errorf("account with alias %s is not ready", alias) - } - - accData, _ = a.updateAccountStatus(alias, models.AccountReady) - - return accData, nil -} - -func (a *AccountWallet) GetAccount(alias string) (*models.AccountData, error) { - accData, exists := a.accountsAliases[alias] - if !exists { - return nil, ierrors.Errorf("account with alias %s does not exist", alias) - } - - return accData, nil -} - -func (a *AccountWallet) isAccountReady(accData *models.AccountData) bool { - if accData.Status == models.AccountReady { - return true - } - - creationSlot := accData.OutputID.CreationSlot() - - // wait for the account to be committed - log.Infof("Waiting for account %s to be committed within slot %d...", accData.Alias, creationSlot) - err := a.retry(func() (bool, error) { - resp, err := a.client.GetBlockIssuance() - if err != nil { - return false, err - } - - if resp.Commitment.Slot >= creationSlot { - log.Infof("Slot %d commited, account %s is ready to use", creationSlot, accData.Alias) - return true, nil - } - - return false, nil - }) - - if err != nil { - log.Errorf("failed to get commitment details while waiting %s: %s", accData.Alias, err) - return false - } - - return true -} - -func (a *AccountWallet) getFunds(amount uint64, addressType iotago.AddressType) (*models.Output, ed25519.PrivateKey, error) { - hdWallet := mock.NewHDWallet("", a.seed[:], a.latestUsedIndex+1) - privKey, _ := hdWallet.KeyPair() - receiverAddr := hdWallet.Address(addressType) - createdOutput, err := a.RequestFaucetFunds(a.client, receiverAddr, iotago.BaseToken(amount)) - if err != nil { - return nil, nil, ierrors.Wrap(err, "failed to request funds from Faucet") - } - - a.latestUsedIndex++ - createdOutput.Index = a.latestUsedIndex - - return createdOutput, privKey, nil -} - -func (a *AccountWallet) destroyAccount(alias string) error { - accData, err := a.GetAccount(alias) - if err != nil { - return err - } - hdWallet := mock.NewHDWallet("", a.seed[:], accData.Index) - - issuingTime := time.Now() - issuingSlot := a.client.LatestAPI().TimeProvider().SlotFromTime(issuingTime) - apiForSlot := a.client.APIForSlot(issuingSlot) - - // get output from node - // From TIP42: Indexers and node plugins shall map the account address of the output derived with Account ID to the regular address -> output mapping table, so that given an Account Address, its most recent unspent account output can be retrieved. - // TODO: use correct outputID - accountOutput := a.client.GetOutput(accData.OutputID) - - txBuilder := builder.NewTransactionBuilder(apiForSlot) - txBuilder.AddInput(&builder.TxInput{ - UnlockTarget: a.accountsAliases[alias].Account.ID().ToAddress(), - InputID: accData.OutputID, - Input: accountOutput, - }) - - // send all tokens to faucet - txBuilder.AddOutput(&iotago.BasicOutput{ - Amount: accountOutput.BaseTokenAmount(), - Conditions: iotago.BasicOutputUnlockConditions{ - &iotago.AddressUnlockCondition{Address: a.faucet.genesisHdWallet.Address(iotago.AddressEd25519).(*iotago.Ed25519Address)}, - }, - }) - - tx, err := txBuilder.Build(hdWallet.AddressSigner()) - if err != nil { - return ierrors.Wrapf(err, "failed to build transaction for account alias destruction %s", alias) - } - - congestionResp, issuerResp, version, err := a.RequestBlockBuiltData(a.client.Client(), a.faucet.account.ID()) - if err != nil { - return ierrors.Wrap(err, "failed to request block built data for the faucet account") - } - - blockID, err := a.PostWithBlock(a.client, tx, a.faucet.account, congestionResp, issuerResp, version) - if err != nil { - return ierrors.Wrapf(err, "failed to post block with ID %s", blockID) - } - - // remove account from wallet - delete(a.accountsAliases, alias) - - log.Infof("Account %s has been destroyed", alias) - return nil -} - -func (a *AccountWallet) retry(requestFunc func() (bool, error)) error { - timeout := time.NewTimer(a.optsRequestTimeout) - interval := time.NewTicker(a.optsRequestTicker) - defer timeutil.CleanupTimer(timeout) - defer timeutil.CleanupTicker(interval) - - for { - done, err := requestFunc() - if err != nil { - return err - } - if done { - return nil - } - select { - case <-interval.C: - continue - case <-timeout.C: - return ierrors.New("timeout while trying to request") - } - } -} diff --git a/tools/evil-spammer/basic_config.json b/tools/evil-spammer/basic_config.json new file mode 100644 index 000000000..f66dc61d4 --- /dev/null +++ b/tools/evil-spammer/basic_config.json @@ -0,0 +1,3 @@ +{ + "lastFaucetUnspentOutputId": "" +} \ No newline at end of file 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/evil-spammer b/tools/evil-spammer/evil-spammer new file mode 100755 index 000000000..56c2f550b Binary files /dev/null and b/tools/evil-spammer/evil-spammer differ diff --git a/tools/evil-spammer/evil-spammer.log b/tools/evil-spammer/evil-spammer.log new file mode 100644 index 000000000..e7eb8052b --- /dev/null +++ b/tools/evil-spammer/evil-spammer.log @@ -0,0 +1,124 @@ +2023-08-22T12:57:28+02:00 INFO main script basic +2023-08-22T12:57:57+02:00 INFO main script basic +2023-08-22T12:57:57+02:00 INFO main Start spamming with rate: 50, time unit: 1s, and spamming type: blk. +2023-08-22T12:57:57+02:00 INFO Spammer Start spamming transactions with 50 rate +2023-08-22T12:57:59+02:00 DEBUG Spammer Last sent block, ID: 0x038fde33b595eb37148ad31d1c59a7b3c8e5c1f98692a023480f6ff3676c1cba0b00000000000000; blkCount: 100 +2023-08-22T12:58:01+02:00 DEBUG Spammer Last sent block, ID: 0x581f270df155a2389f5176d7102c249546fcefa3218ca6c98790b3d0dd5276500b00000000000000; blkCount: 200 +2023-08-22T12:58:03+02:00 DEBUG Spammer Last sent block, ID: 0x4d6ba47ae5264c7cb6af06e5489cae87c5259923866e928c546ac68b881ee3690c00000000000000; blkCount: 300 +2023-08-22T12:58:05+02:00 DEBUG Spammer Last sent block, ID: 0x9f613651bc5e1726aa0de90ede8843cb913e7f689f3f42df99e0e4f5f206ed0c0c00000000000000; blkCount: 400 +2023-08-22T12:58:07+02:00 DEBUG Spammer Last sent block, ID: 0x4a4f77c0bb64f7ea321f46a6746b8a47408a9f455eed9d691819fc385530e1ae0c00000000000000; blkCount: 500 +2023-08-22T12:58:09+02:00 DEBUG Spammer Last sent block, ID: 0xd7b12163b5fc6866805911cd09743cd8fcf824c25a3d692b34a6e59f25a553260c00000000000000; blkCount: 600 +2023-08-22T12:58:11+02:00 DEBUG Spammer Last sent block, ID: 0x6bc1fcdf3b49ace2f22ba5532d55fb0ba57026b5bef2ec70d758844b31ee14160c00000000000000; blkCount: 700 +2023-08-22T12:58:13+02:00 DEBUG Spammer Last sent block, ID: 0xd1e53bbf829c70bdf70b7649ae6de7b6cfb26992487b44813c34696a54887dba0d00000000000000; blkCount: 800 +2023-08-22T12:58:15+02:00 DEBUG Spammer Last sent block, ID: 0x095ad5be3c60f933f576a7012f32cc808095315b606e78b0e1e01bb4a3be49660d00000000000000; blkCount: 900 +2023-08-22T12:58:17+02:00 DEBUG Spammer Last sent block, ID: 0x7b7a7b29c50eb430c7195916dfcafdf1c50ee9b74fb5c869f3bbadf5b66c09960d00000000000000; blkCount: 1000 +2023-08-22T12:58:19+02:00 DEBUG Spammer Last sent block, ID: 0x391735e660e4ac09df9932688d0492535c62a4045ead9ac525d9ae5f86ac655a0d00000000000000; blkCount: 1100 +2023-08-22T12:58:21+02:00 DEBUG Spammer Last sent block, ID: 0x3d446e6519983bb3ff6967c86bfb14bf695da43ad189e868deb3dc12195878600d00000000000000; blkCount: 1200 +2023-08-22T12:58:23+02:00 DEBUG Spammer Last sent block, ID: 0xbd3a87f40ef5d987d9f30c19c6728242f56999249adf645850d4d10d074890350e00000000000000; blkCount: 1300 +2023-08-22T12:58:25+02:00 DEBUG Spammer Last sent block, ID: 0x374512f1dfe4f5ff31bcf72a4e9036e0246ffd3b44dc534eb1bf87acca3d67de0e00000000000000; blkCount: 1400 +2023-08-22T12:58:27+02:00 INFO Spammer Blocks issued so far: 1497, errors encountered: 0 +2023-08-22T12:58:27+02:00 DEBUG Spammer Last sent block, ID: 0x3473bf9eb8b04f565879a6c7fb091a3d73e09e110af7c2d68478501300ad229c0e00000000000000; blkCount: 1500 +2023-08-22T12:58:29+02:00 DEBUG Spammer Last sent block, ID: 0x627c14f01657c67601b140a8f157b26ed40f30ec055f237e2258a31c0359bc9c0e00000000000000; blkCount: 1600 +2023-08-22T12:58:31+02:00 DEBUG Spammer Last sent block, ID: 0x4dba3ab6f8025e9629b212e3d9edd4967d6f761bdc264abb15048baefa577d6b0e00000000000000; blkCount: 1700 +2023-08-22T12:58:33+02:00 DEBUG Spammer Last sent block, ID: 0x60e31ed2737ff545bd09a9593111b3be17613613178b066a00872e8539c1e9fd0f00000000000000; blkCount: 1800 +2023-08-22T12:58:35+02:00 DEBUG Spammer Last sent block, ID: 0xdb6da90c1eac3ce53f085734b8b0e3a863d24f66258d9128ddb883911fb8f77c0f00000000000000; blkCount: 1900 +2023-08-22T12:58:37+02:00 DEBUG Spammer Last sent block, ID: 0xb2ea9c50467a897a08bb07d5758afa2033c6ec028da04194ae1507a431c8ea320f00000000000000; blkCount: 2000 +2023-08-22T12:58:39+02:00 DEBUG Spammer Last sent block, ID: 0xe452fb56386b2c2ecec9d95c07f9717ce64c68b45ec084801421f7fea3282fc40f00000000000000; blkCount: 2100 +2023-08-22T12:58:41+02:00 DEBUG Spammer Last sent block, ID: 0x5a542c7c6cc5eb3ebcb2e1e6c8e5bded4c986310d7942e0ee9ba180befc872010f00000000000000; blkCount: 2200 +2023-08-22T12:58:43+02:00 DEBUG Spammer Last sent block, ID: 0x03ea628b29ec60057b0ca10419fa2463d9246b73bfe0381cdb63e88e97e62bc01000000000000000; blkCount: 2300 +2023-08-22T12:58:45+02:00 DEBUG Spammer Last sent block, ID: 0x84da431409a82198a809c94c9b26bd5ccf411ce60c6b8d4fe687cab1c4650aeb1000000000000000; blkCount: 2400 +2023-08-22T12:58:47+02:00 DEBUG Spammer Last sent block, ID: 0x04fd13cad6cd54be7ffc8e8699d8f4ca20b71beed218a4ce795137797839b7f11000000000000000; blkCount: 2500 +2023-08-22T12:58:49+02:00 DEBUG Spammer Last sent block, ID: 0xa5e79321d2f7ad779d65219ddd99d73804262db4f63ed4f4b28d48a442fc99321000000000000000; blkCount: 2600 +2023-08-22T12:58:51+02:00 DEBUG Spammer Last sent block, ID: 0x28cf7e42c0e3b6a9e9183f08670f80769a94b2c47e816bd3ba002d3ae6a720991000000000000000; blkCount: 2700 +2023-08-22T12:58:53+02:00 DEBUG Spammer Last sent block, ID: 0x9484f3879e5206d1b94849e6d56581f043c333ec96f549015b51cbce675eaf041100000000000000; blkCount: 2800 +2023-08-22T12:58:55+02:00 DEBUG Spammer Last sent block, ID: 0x87adf4ee8179dbaff787a8c259d81f2710a47afa40b38908acaccad10e4827e21100000000000000; blkCount: 2900 +2023-08-22T12:58:57+02:00 INFO Spammer Blocks issued so far: 2998, errors encountered: 0 +2023-08-22T12:58:57+02:00 DEBUG Spammer Last sent block, ID: 0xb2a3793334fd8fc7aaf3e9ed378d3313f0311ac89dc43303f6e643b64580e54f1100000000000000; blkCount: 3000 +2023-08-22T12:58:59+02:00 DEBUG Spammer Last sent block, ID: 0x4a21e097631832b64021a237e30371a720ad2ba99b9c466a2b532b3939c462261100000000000000; blkCount: 3100 +2023-08-22T12:59:01+02:00 DEBUG Spammer Last sent block, ID: 0x15bcf02b9296108f46bc5843bf89a39ad667c32c30bc4e1b557606a2bd9a86761100000000000000; blkCount: 3200 +2023-08-22T12:59:03+02:00 DEBUG Spammer Last sent block, ID: 0x9ba8092361455c0e648f7ac1853b2fc3b326a955736239088d9a7bb55fcfadc21200000000000000; blkCount: 3300 +2023-08-22T12:59:05+02:00 DEBUG Spammer Last sent block, ID: 0x0214efbf409bc75696e9e2f7580e6e85d9d93a599a49e1a1269b725a07007de01200000000000000; blkCount: 3400 +2023-08-22T12:59:07+02:00 DEBUG Spammer Last sent block, ID: 0x54c0eb18720eb7b20a1c8b95059a4f697de46d233d88fb000ca623707d8b369f1200000000000000; blkCount: 3500 +2023-08-22T12:59:09+02:00 DEBUG Spammer Last sent block, ID: 0xdc06e60ad327994ce73e586d7d0c766611c8b708bd98fa384af9f00b3d48103b1200000000000000; blkCount: 3600 +2023-08-22T12:59:11+02:00 DEBUG Spammer Last sent block, ID: 0x61c47ac220cd5a7af6f2181edd503f15555e3e9127ab2baa9a2aa8eae6d061261200000000000000; blkCount: 3700 +2023-08-22T12:59:13+02:00 DEBUG Spammer Last sent block, ID: 0xdd0ca64c3b35febaa2960613f4ce6a4c5be2736c62a0611f132982d5c1ec8dbe1300000000000000; blkCount: 3800 +2023-08-22T12:59:15+02:00 DEBUG Spammer Last sent block, ID: 0x7c6136a0352b6d0088197405145b9b437e07aa192199bb6a6a0b63b9e3f6b9901300000000000000; blkCount: 3900 +2023-08-22T12:59:17+02:00 DEBUG Spammer Last sent block, ID: 0x68e32a9226c10667d698879fd5c53b37f2d5eb6b0fd85cc139ef236f5f09b1c01300000000000000; blkCount: 4000 +2023-08-22T12:59:19+02:00 DEBUG Spammer Last sent block, ID: 0x8d6fb677ee6818b677fbb7c6b5dfb45563fce721037163b70159f165047dbd1d1300000000000000; blkCount: 4100 +2023-08-22T12:59:21+02:00 DEBUG Spammer Last sent block, ID: 0xfc6f55fe482e227d69e060bc1f192e6a78b139c03b50ade2268e559ca648a92d1300000000000000; blkCount: 4200 +2023-08-22T12:59:23+02:00 DEBUG Spammer Last sent block, ID: 0x09c8a4142a8897aa7245a54799ae6b8ed597b8ee83fb78b9d24ed4067967850d1400000000000000; blkCount: 4300 +2023-08-22T12:59:25+02:00 DEBUG Spammer Last sent block, ID: 0x990c9c01715fba5bf101d0e0a54affc1e312dae8e6be0e104e5555a2948673761400000000000000; blkCount: 4400 +2023-08-22T12:59:27+02:00 INFO Spammer Blocks issued so far: 4498, errors encountered: 0 +2023-08-22T12:59:27+02:00 DEBUG Spammer Last sent block, ID: 0xf1fc9c635c417063d77a8bc8ba77209cd7e4e21fdd6d421d0a45871d0b8767621400000000000000; blkCount: 4500 +2023-08-22T12:59:29+02:00 DEBUG Spammer Last sent block, ID: 0x395947f8ac9b5cf1797e18ad56b0f18211db8fa670f22b7c52a2c83454d902bb1400000000000000; blkCount: 4600 +2023-08-22T12:59:31+02:00 DEBUG Spammer Last sent block, ID: 0x2d005f35ac3bb8059867e06957ed2e3efd8d5ea1060b3872a19f15b4b392aaad1400000000000000; blkCount: 4700 +2023-08-22T12:59:33+02:00 DEBUG Spammer Last sent block, ID: 0xb831ef0af7bd491a2cb3be968542f757881cd798de7930fb9426b9ca25e23fcb1500000000000000; blkCount: 4800 +2023-08-22T12:59:35+02:00 DEBUG Spammer Last sent block, ID: 0x74979a3e9d11df89e674b60331433e3bdd1f746bd4cf7c0f57e8a3b94037d42f1500000000000000; blkCount: 4900 +2023-08-22T22:55:33+02:00 INFO main script basic +2023-08-22T22:55:33+02:00 INFO main Start spamming with rate: 50, time unit: 1s, and spamming type: blk. +2023-08-22T22:55:33+02:00 INFO Spammer Start spamming transactions with 50 rate +2023-08-22T22:55:35+02:00 DEBUG Spammer Last sent block, ID: 0x5a7baf9bd75205b8b2873ca1141eb3afa8b10d8466557d484e19365f18a1ec5c0a00000000000000; blkCount: 100 +2023-08-22T22:55:37+02:00 DEBUG Spammer Last sent block, ID: 0x43bcf83d0264242523c32dd7df259d963c68cc60ec298c53cdae7d579b21f9cb0a00000000000000; blkCount: 200 +2023-08-22T22:55:39+02:00 DEBUG Spammer Last sent block, ID: 0x31ddef7ba54ab8220fb2fce6cc32adf5728b53e03313be77e812f3004ebe4eb20a00000000000000; blkCount: 300 +2023-08-22T22:55:41+02:00 DEBUG Spammer Last sent block, ID: 0x656d18570480de55ea274ac987f078cc178d1e3149c327278811f7489fd523f40a00000000000000; blkCount: 400 +2023-08-22T22:55:43+02:00 DEBUG Spammer Last sent block, ID: 0x02db8075d14915d4761e45b0facb23abf875d0235bd43a0e96ca708c62e901fa0b00000000000000; blkCount: 500 +2023-08-22T22:55:45+02:00 DEBUG Spammer Last sent block, ID: 0x9b2750b6d93353bf934d85c602915167effc05d44888276c797fc0def6ab95420b00000000000000; blkCount: 600 +2023-08-22T22:55:47+02:00 DEBUG Spammer Last sent block, ID: 0x9c014b3adabbbaf3880cd7b3d2c69c65676a61826cf02ec92c3d468e6ccc59740b00000000000000; blkCount: 700 +2023-08-22T22:55:49+02:00 DEBUG Spammer Last sent block, ID: 0xf207d56fed1a5c15b21d90885f8d976487678487190fad6c0ad68c8ef1094de10b00000000000000; blkCount: 800 +2023-08-22T22:55:51+02:00 DEBUG Spammer Last sent block, ID: 0xa1cfe5bce31cd1c45ead4fa92dc18f539be0488a0d9906cc8f2c0b725e4249f10b00000000000000; blkCount: 900 +2023-08-22T22:55:53+02:00 DEBUG Spammer Last sent block, ID: 0x03ba56c5a16dbd60ccc26dd11a0f1ca845df0e75f72bf81407675d5af331b9920c00000000000000; blkCount: 1000 +2023-08-22T22:55:55+02:00 DEBUG Spammer Last sent block, ID: 0xd2d8dd0d98544d5830467036499fae3a9df1bc83d3fb562ba157fdb0ab46245e0c00000000000000; blkCount: 1100 +2023-08-22T22:55:57+02:00 DEBUG Spammer Last sent block, ID: 0x01fa45d339eb69e8f9e90f57ae36dd3a284985089dbe80b5b2646ff2e0d2fd5f0c00000000000000; blkCount: 1200 +2023-08-22T22:55:59+02:00 DEBUG Spammer Last sent block, ID: 0x587746afab9aa4bcc49b282ff50a27211c550172c108a2f908fdf9a60da073d90c00000000000000; blkCount: 1300 +2023-08-22T22:56:01+02:00 DEBUG Spammer Last sent block, ID: 0xc22baff7a9df2feb63e0ce035b00f7e45186cfea996288bbfce6e8dce0fd0dcf0c00000000000000; blkCount: 1400 +2023-08-22T22:56:03+02:00 INFO Spammer Blocks issued so far: 1499, errors encountered: 0 +2023-08-22T22:56:03+02:00 DEBUG Spammer Last sent block, ID: 0x1005fd0a8d8febd82749f3b5094b871d72b8502e1e69dfb7c5bc47b7a482ca4e0d00000000000000; blkCount: 1500 +2023-08-22T22:56:05+02:00 DEBUG Spammer Last sent block, ID: 0x050a1ab385a20b17b58ff4539db37f75ea896edc8c4eacbc63ac9434c042d8e30d00000000000000; blkCount: 1600 +2023-08-22T22:56:07+02:00 DEBUG Spammer Last sent block, ID: 0x43b998d9db511493a5853ede42dc8b348d43494c664bf473bc3a5f11cf4e39030d00000000000000; blkCount: 1700 +2023-08-22T22:56:09+02:00 DEBUG Spammer Last sent block, ID: 0x6034baab001073612506794a2be8b001962a5b21b8ef893257fe52697c69b9470d00000000000000; blkCount: 1800 +2023-08-22T22:56:11+02:00 DEBUG Spammer Last sent block, ID: 0xb218e94b0323f43f5f80772f8491d1142fa51e5fae50145a2fb1e40cd441ad7d0d00000000000000; blkCount: 1900 +2023-08-22T22:56:13+02:00 DEBUG Spammer Last sent block, ID: 0x50466d10d8c2a866d0b1fef5dddf7912b6853cc64b29149d79550a7832cbe3fe0e00000000000000; blkCount: 2000 +2023-08-22T22:56:15+02:00 DEBUG Spammer Last sent block, ID: 0xc41230d51a43c2d13c72037a1812e16da9ae4dcaeb9ccf902f74e946b1019b920e00000000000000; blkCount: 2100 +2023-08-22T22:56:17+02:00 DEBUG Spammer Last sent block, ID: 0xec37e5c09ef4d577cc851b6d45266fb5dd94780bef9052925cb584ab8d0b0a860e00000000000000; blkCount: 2200 +2023-08-22T22:56:19+02:00 DEBUG Spammer Last sent block, ID: 0xd6b78110cd87a1004a145d5dce943436188aca3e8899738cd4778c950827d2fd0e00000000000000; blkCount: 2300 +2023-08-22T22:56:21+02:00 DEBUG Spammer Last sent block, ID: 0x9d0e565ced4644d70d7ddd6bad38adcea1237808f66fd71b417f0e41784decf30e00000000000000; blkCount: 2400 +2023-08-22T22:56:23+02:00 DEBUG Spammer Last sent block, ID: 0xf94bb3279f53755a768733e10115f009136e60f11bee11ceb56a5d8bfcda1a550f00000000000000; blkCount: 2500 +2023-08-22T22:56:25+02:00 DEBUG Spammer Last sent block, ID: 0xf6f8923f17cb68f87f284d15c9ec3bac565f3482e82dda3a0b8cf4c472eb07060f00000000000000; blkCount: 2600 +2023-08-22T22:56:27+02:00 DEBUG Spammer Last sent block, ID: 0x5991df150383d4304430329f3d264d479c263afad0495f1bf296b14ceb21fd6b0f00000000000000; blkCount: 2700 +2023-08-22T22:56:29+02:00 DEBUG Spammer Last sent block, ID: 0x5cbaedd89f30d955e25d54f48df977ed31c7e2264d1f5b99fc14c10c947f71ac0f00000000000000; blkCount: 2800 +2023-08-22T22:56:31+02:00 DEBUG Spammer Last sent block, ID: 0x4a751575603aeba93f5a04360c943e779ef11b78692ba842e0574257bb8687000f00000000000000; blkCount: 2900 +2023-08-22T22:56:33+02:00 INFO Spammer Blocks issued so far: 2999, errors encountered: 0 +2023-08-22T22:56:33+02:00 DEBUG Spammer Last sent block, ID: 0xa10ee45295acab9c6e928348a6e6afe2c3f867b6714b37270c848f49f74fedc31000000000000000; blkCount: 3000 +2023-08-22T22:56:35+02:00 DEBUG Spammer Last sent block, ID: 0x4c4741afff8bd104ad526ead89664e58fba60042576cdd594f1c9126219a9b1e1000000000000000; blkCount: 3100 +2023-08-22T22:56:37+02:00 DEBUG Spammer Last sent block, ID: 0x1e60b338c64db6482f9b595e676afe5cd69933ed4862ce7418790e7a47d608931000000000000000; blkCount: 3200 +2023-08-22T22:56:39+02:00 DEBUG Spammer Last sent block, ID: 0x829a5ebec21bedbd31934c097f97477453439b734e71ed77a4f5d18a2a037d1a1000000000000000; blkCount: 3300 +2023-08-22T22:56:41+02:00 DEBUG Spammer Last sent block, ID: 0x8411be55c6438d87780a21596ab0cfc8278ff90852e8be760bfe0875c5083e1c1000000000000000; blkCount: 3400 +2023-08-22T22:56:43+02:00 DEBUG Spammer Last sent block, ID: 0x10e0b7f5c12969e8a35af1d71aeae72e3577093c3ada1012b7c83884e4b427781100000000000000; blkCount: 3500 +2023-08-22T22:56:45+02:00 DEBUG Spammer Last sent block, ID: 0xa020e41f09cd80e00973d01aa1271ab1de98a480c11d5a517b99a3fa9c7279d71100000000000000; blkCount: 3600 +2023-08-22T22:56:47+02:00 DEBUG Spammer Last sent block, ID: 0xee22265c46efe359c9a0506347e49e04698436d8e1854243c1878c29577d8fce1100000000000000; blkCount: 3700 +2023-08-22T22:56:49+02:00 DEBUG Spammer Last sent block, ID: 0x0ed13ffd5d651ec68a136465b2c5bfb2e7a685a9563515e9a0dac38279c2c4001100000000000000; blkCount: 3800 +2023-08-22T22:56:51+02:00 DEBUG Spammer Last sent block, ID: 0xf388b930e7effd3294c86aafdac9aca7a2700b43fbdf3cf89a37b8d049cfa00d1100000000000000; blkCount: 3900 +2023-08-22T22:56:53+02:00 DEBUG Spammer Last sent block, ID: 0xe06627df683ec16a3010f9218c21538f67161312a828c9feb7d18be9cb8579cc1200000000000000; blkCount: 4000 +2023-08-22T22:56:55+02:00 DEBUG Spammer Last sent block, ID: 0x976ff7f1f1595e644c29a2f9501e233af0199dd94531970daf815e844fe929c91200000000000000; blkCount: 4100 +2023-08-22T22:56:57+02:00 DEBUG Spammer Last sent block, ID: 0x2a4a8803a2c6dfe53f7ccd79e978ca882b92ebff4657b10bc2d057c7b1308e201200000000000000; blkCount: 4200 +2023-08-22T22:56:59+02:00 DEBUG Spammer Last sent block, ID: 0xfe1c47b1d2616078fce1b519e710fdee994a850f396707595c7706cd39bba7fd1200000000000000; blkCount: 4300 +2023-08-22T22:57:01+02:00 DEBUG Spammer Last sent block, ID: 0x8b82935b9414ea0f69186d998e99bb2114cf7a4f9601fb09c416065b3e8773c61200000000000000; blkCount: 4400 +2023-08-22T22:57:03+02:00 INFO Spammer Blocks issued so far: 4499, errors encountered: 0 +2023-08-22T22:57:03+02:00 DEBUG Spammer Last sent block, ID: 0x8820d341a4c792348f10d740828a210a25e39f98318c189fa901132550bb04551300000000000000; blkCount: 4500 +2023-08-22T22:57:05+02:00 DEBUG Spammer Last sent block, ID: 0xb7fe431dc0ae00ffa188d8ac2a9daab7cd1111587d1d6cdb92dae32c0ca0d3521300000000000000; blkCount: 4600 +2023-08-22T22:57:07+02:00 DEBUG Spammer Last sent block, ID: 0x5c3e36356f0830ec3b15545d4b7f7517a241913c185c4ac8bbca3c22917f4a3c1300000000000000; blkCount: 4700 +2023-08-22T22:57:09+02:00 DEBUG Spammer Last sent block, ID: 0xf122b547fa15ba118045465c004051257e3deed8343e692a6f4b409511c9b64f1300000000000000; blkCount: 4800 +2023-08-22T22:57:11+02:00 DEBUG Spammer Last sent block, ID: 0x205d6a3fddeba1bcc7a8bfd24d4964a1b1f35fe3ede093db2ef2584e9e692fbd1300000000000000; blkCount: 4900 +2023-08-22T22:57:13+02:00 DEBUG Spammer Last sent block, ID: 0xeda2ce5ce9013e651521632c68c0700ecfc4db3410d05035071886b950c065b01400000000000000; blkCount: 5000 +2023-08-22T22:57:15+02:00 DEBUG Spammer Last sent block, ID: 0xac0d991bcabefd6685c4a558d910e54bcb46002b57863f21a9844293867c9f111400000000000000; blkCount: 5100 +2023-08-22T22:57:17+02:00 DEBUG Spammer Last sent block, ID: 0xaff2f1476b447e2f80a28e94587b716ccdb3916017670c396f417ea70236d03a1400000000000000; blkCount: 5200 +2023-08-22T22:57:19+02:00 DEBUG Spammer Last sent block, ID: 0x31681b506adf32a228195f06fe93b70fb02026aee2375da26f8c7db56d9ad23a1400000000000000; blkCount: 5300 +2023-08-22T22:57:21+02:00 DEBUG Spammer Last sent block, ID: 0xff098db0afbdf705da9efd37fc8cd70555561084d7d3be8f3a615d71cf2934af1400000000000000; blkCount: 5400 +2023-08-22T22:57:23+02:00 DEBUG Spammer Last sent block, ID: 0xd8783a468e367e6996a39dfe6fd9113b576a55c1c63df3221d5da3ddb71e1b9d1500000000000000; blkCount: 5500 +2023-08-22T22:57:25+02:00 DEBUG Spammer Last sent block, ID: 0xe78c9ee3b38aaf26ddde90b0370542bedc7d8b5ff2ed8ae54844fa894c57aaf11500000000000000; blkCount: 5600 +2023-08-22T22:57:27+02:00 DEBUG Spammer Last sent block, ID: 0x482a46771f665468033d30c15634e96a253d4e5919637ea0421eecfd3c4c61c71500000000000000; blkCount: 5700 +2023-08-22T22:57:29+02:00 DEBUG Spammer Last sent block, ID: 0xe00fcc9c6df8b5a0cabd4ce3e56f8615718c248849769ecd5135ef2f6e953f5a1500000000000000; blkCount: 5800 +2023-08-22T22:57:31+02:00 DEBUG Spammer Last sent block, ID: 0x54a2883de572693288823005f5e2263be547c384dbd6b697ed0e32eca5f0e42f1500000000000000; blkCount: 5900 +2023-08-22T22:57:33+02:00 INFO Spammer Blocks issued so far: 5999, errors encountered: 0 +2023-08-22T22:57:33+02:00 DEBUG Spammer Last sent block, ID: 0x1f40ce55a3fb8f03e74ad46a7909527e789cf1a0e1d83fb7c2655bc2910025b01600000000000000; blkCount: 6000 +2023-08-22T22:57:35+02:00 DEBUG Spammer Last sent block, ID: 0x946a2c5dabc16e2e715ff23f1a3703fb9f3e5ad02d649d906b8bfd245fa28bb11600000000000000; blkCount: 6100 diff --git a/tools/evil-spammer/evilwallet/aliasmanager.go b/tools/evil-spammer/evilwallet/aliasmanager.go deleted file mode 100644 index 284e89f6d..000000000 --- a/tools/evil-spammer/evilwallet/aliasmanager.go +++ /dev/null @@ -1,110 +0,0 @@ -package evilwallet - -import ( - "go.uber.org/atomic" - - "github.com/iotaledger/hive.go/ierrors" - "github.com/iotaledger/hive.go/runtime/syncutils" - "github.com/iotaledger/iota-core/tools/evil-spammer/models" -) - -// region AliasManager ///////////////////////////////////////////////////////////////////////////////////////////////// - -// AliasManager is the manager for output aliases. -type AliasManager struct { - outputMap map[string]*models.Output - inputMap map[string]*models.Output - - outputAliasCount *atomic.Uint64 - mu syncutils.RWMutex -} - -// NewAliasManager creates and returns a new AliasManager. -func NewAliasManager() *AliasManager { - return &AliasManager{ - outputMap: make(map[string]*models.Output), - inputMap: make(map[string]*models.Output), - outputAliasCount: atomic.NewUint64(0), - } -} - -// AddOutputAlias maps the given outputAliasName to output, if there's duplicate outputAliasName, it will be overwritten. -func (a *AliasManager) AddOutputAlias(output *models.Output, aliasName string) { - a.mu.Lock() - defer a.mu.Unlock() - - a.outputMap[aliasName] = output -} - -// AddInputAlias adds an input alias. -func (a *AliasManager) AddInputAlias(input *models.Output, aliasName string) { - a.mu.Lock() - defer a.mu.Unlock() - - a.inputMap[aliasName] = input -} - -// GetInput returns the input for the alias specified. -func (a *AliasManager) GetInput(aliasName string) (*models.Output, bool) { - a.mu.RLock() - defer a.mu.RUnlock() - in, ok := a.inputMap[aliasName] - - return in, ok -} - -// GetOutput returns the output for the alias specified. -func (a *AliasManager) GetOutput(aliasName string) *models.Output { - a.mu.RLock() - defer a.mu.RUnlock() - - return a.outputMap[aliasName] -} - -// ClearAllAliases clears all aliases. -func (a *AliasManager) ClearAllAliases() { - a.mu.Lock() - defer a.mu.Unlock() - - a.inputMap = make(map[string]*models.Output) - a.outputMap = make(map[string]*models.Output) -} - -// ClearAliases clears provided aliases. -func (a *AliasManager) ClearAliases(aliases ScenarioAlias) { - a.mu.Lock() - defer a.mu.Unlock() - - for _, in := range aliases.Inputs { - delete(a.inputMap, in) - } - for _, out := range aliases.Outputs { - delete(a.outputMap, out) - } -} - -// AddOutputAliases batch adds the outputs their respective aliases. -func (a *AliasManager) AddOutputAliases(outputs []*models.Output, aliases []string) error { - if len(outputs) != len(aliases) { - return ierrors.New("mismatch outputs and aliases length") - } - for i, out := range outputs { - a.AddOutputAlias(out, aliases[i]) - } - - return nil -} - -// AddInputAliases batch adds the inputs their respective aliases. -func (a *AliasManager) AddInputAliases(inputs []*models.Output, aliases []string) error { - if len(inputs) != len(aliases) { - return ierrors.New("mismatch outputs and aliases length") - } - for i, out := range inputs { - a.AddInputAlias(out, aliases[i]) - } - - return nil -} - -// endregion ///////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/tools/evil-spammer/evilwallet/customscenarios.go b/tools/evil-spammer/evilwallet/customscenarios.go deleted file mode 100644 index e26142746..000000000 --- a/tools/evil-spammer/evilwallet/customscenarios.go +++ /dev/null @@ -1,238 +0,0 @@ -package evilwallet - -import ( - "strconv" -) - -var scenariosMap map[string]EvilBatch - -func init() { - scenariosMap = make(map[string]EvilBatch) - scenariosMap["tx"] = SingleTransactionBatch() - scenariosMap["ds"] = NSpendBatch(2) - scenariosMap["conflict-circle"] = ConflictSetCircle(4) - scenariosMap["guava"] = Scenario1() - scenariosMap["orange"] = Scenario2() - scenariosMap["mango"] = Scenario3() - scenariosMap["pear"] = Scenario4() - scenariosMap["lemon"] = Scenario5() - scenariosMap["banana"] = Scenario6() - scenariosMap["kiwi"] = Scenario7() - scenariosMap["peace"] = NoConflictsScenario1() -} - -// GetScenario returns an evil batch based i=on its name. -func GetScenario(scenarioName string) (batch EvilBatch, ok bool) { - batch, ok = scenariosMap[scenarioName] - return -} - -// SingleTransactionBatch returns an EvilBatch that is a single transaction. -func SingleTransactionBatch() EvilBatch { - return EvilBatch{{ScenarioAlias{Inputs: []string{"1"}, Outputs: []string{"2"}}}} -} - -// ConflictSetCircle creates a circular conflict set for a given size, e.g. for size=3, conflict set: A-B-C-A. -func ConflictSetCircle(size int) EvilBatch { - scenarioAlias := make([]ScenarioAlias, 0) - inputStartNum := size - - for i := 0; i < inputStartNum; i++ { - in := i - in2 := (in + 1) % inputStartNum - scenarioAlias = append(scenarioAlias, - ScenarioAlias{ - Inputs: []string{strconv.Itoa(in), strconv.Itoa(in2)}, - Outputs: []string{strconv.Itoa(inputStartNum + i)}, - }) - } - - return EvilBatch{scenarioAlias} -} - -func NSpendBatch(nSpent int) EvilBatch { - conflictSlice := make(EvilBatch, 0) - scenarioAlias := make([]ScenarioAlias, 0) - inputStartNum := nSpent + 1 - - for i := 1; i <= nSpent; i++ { - scenarioAlias = append(scenarioAlias, - ScenarioAlias{ - Inputs: []string{strconv.Itoa(inputStartNum)}, - Outputs: []string{strconv.Itoa(i)}, - }, - ) - } - conflictSlice = append(conflictSlice, scenarioAlias) - - return conflictSlice -} - -// Scenario1 describes two double spends and aggregates them. -func Scenario1() EvilBatch { - return EvilBatch{ - []ScenarioAlias{ - {Inputs: []string{"1"}, Outputs: []string{"2", "3"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"2"}, Outputs: []string{"4"}}, - {Inputs: []string{"2"}, Outputs: []string{"5"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"3"}, Outputs: []string{"6"}}, - {Inputs: []string{"3"}, Outputs: []string{"7"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"6", "5"}, Outputs: []string{"8"}}, - }, - } -} - -// Scenario2 is a reflection of UTXO unit test scenario example B - packages/ledgerstate/utxo_dag_test_exampleB.png. -func Scenario2() EvilBatch { - return EvilBatch{ - []ScenarioAlias{ - {Inputs: []string{"A"}, Outputs: []string{"C"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"B"}, Outputs: []string{"D"}}, - {Inputs: []string{"B"}, Outputs: []string{"G"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"C", "D"}, Outputs: []string{"E"}}, - {Inputs: []string{"D"}, Outputs: []string{"F"}}, - }, - } -} - -// Scenario3 is a reflection of UTXO unit test scenario example C - packages/ledgerstate/utxo_dag_test_exampleC.png. -func Scenario3() EvilBatch { - return EvilBatch{ - []ScenarioAlias{ - {Inputs: []string{"A"}, Outputs: []string{"C"}}, - {Inputs: []string{"A"}, Outputs: []string{"D"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"B"}, Outputs: []string{"E"}}, - {Inputs: []string{"B"}, Outputs: []string{"F"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"D", "E"}, Outputs: []string{"G"}}, - }, - } -} - -// Scenario4 is a reflection of ledgerstate unit test for conflict confirmation - packages/ledgerstate/ledgerstate_test_SetConflictConfirmed.png. -func Scenario4() EvilBatch { - return EvilBatch{ - []ScenarioAlias{ - {Inputs: []string{"1"}, Outputs: []string{"A"}}, - {Inputs: []string{"1"}, Outputs: []string{"B"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"2"}, Outputs: []string{"C"}}, - {Inputs: []string{"2"}, Outputs: []string{"D"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"3"}, Outputs: []string{"E"}}, - {Inputs: []string{"3"}, Outputs: []string{"F"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"D", "E"}, Outputs: []string{"G"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"G"}, Outputs: []string{"H"}}, - {Inputs: []string{"G"}, Outputs: []string{"I"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"A", "I"}, Outputs: []string{"J"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"J"}, Outputs: []string{"K"}}, - }, - } -} - -// Scenario5 uses ConflictSetCircle with size 4 and aggregate its outputs. -func Scenario5() EvilBatch { - circularConflict := ConflictSetCircle(4) - circularConflict = append(circularConflict, []ScenarioAlias{{ - Inputs: []string{"4", "6"}, - Outputs: []string{"8"}, - }}) - circularConflict = append(circularConflict, []ScenarioAlias{{ - Inputs: []string{"5", "7"}, - Outputs: []string{"9"}, - }}) - - return circularConflict -} - -// Scenario6 returns 5 levels deep scenario. -func Scenario6() EvilBatch { - return EvilBatch{ - []ScenarioAlias{ - {Inputs: []string{"1"}, Outputs: []string{"A", "B"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"A"}, Outputs: []string{"C", "D"}}, - {Inputs: []string{"B"}, Outputs: []string{"E"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"C"}, Outputs: []string{"F", "G"}}, - {Inputs: []string{"C", "D"}, Outputs: []string{"H"}}, - {Inputs: []string{"D"}, Outputs: []string{"I"}}, - {Inputs: []string{"F", "D"}, Outputs: []string{"J"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"G"}, Outputs: []string{"K"}}, - {Inputs: []string{"I"}, Outputs: []string{"L"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"K", "I"}, Outputs: []string{"M"}}, - {Inputs: []string{"L"}, Outputs: []string{"N"}}, - }, - } -} - -// Scenario7 three level deep scenario, with two separate conflict sets aggregated. -func Scenario7() EvilBatch { - return EvilBatch{ - []ScenarioAlias{ - {Inputs: []string{"A"}, Outputs: []string{"E"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"A", "B"}, Outputs: []string{"F"}}, - {Inputs: []string{"B"}, Outputs: []string{"G"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"C"}, Outputs: []string{"H"}}, - {Inputs: []string{"D"}, Outputs: []string{"I"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"E", "G"}, Outputs: []string{"J"}}, - {Inputs: []string{"H"}, Outputs: []string{"K"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"J", "K"}, Outputs: []string{"L"}}, - {Inputs: []string{"I", "K"}, Outputs: []string{"M"}}, - }, - } -} - -// NoConflictsScenario1 returns batch with no conflicts that is 3 levels deep. -func NoConflictsScenario1() EvilBatch { - return EvilBatch{ - []ScenarioAlias{ - {Inputs: []string{"1"}, Outputs: []string{"3", "4"}}, - {Inputs: []string{"2"}, Outputs: []string{"5", "6"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"6", "4"}, Outputs: []string{"7"}}, - {Inputs: []string{"3", "5"}, Outputs: []string{"8"}}, - }, - []ScenarioAlias{ - {Inputs: []string{"8", "7"}, Outputs: []string{"9"}}, - }, - } -} diff --git a/tools/evil-spammer/evilwallet/evilscenario.go b/tools/evil-spammer/evilwallet/evilscenario.go deleted file mode 100644 index 00f15d968..000000000 --- a/tools/evil-spammer/evilwallet/evilscenario.go +++ /dev/null @@ -1,143 +0,0 @@ -package evilwallet - -import ( - "fmt" - "strconv" - - "github.com/mr-tron/base58" - "go.uber.org/atomic" - - "github.com/iotaledger/hive.go/ds/types" - iotago "github.com/iotaledger/iota.go/v4" -) - -// The custom conflict in spammer can be provided like this: -// EvilBatch{ -// { -// ScenarioAlias{inputs: []{"1"}, outputs: []{"2","3"}} -// }, -// { -// ScenarioAlias{inputs: []{"2"}, outputs: []{"4"}}, -// ScenarioAlias{inputs: []{"2"}, outputs: []{"5"}} -// } -// } - -type ScenarioAlias struct { - Inputs []string - Outputs []string -} - -func NewScenarioAlias() ScenarioAlias { - return ScenarioAlias{ - Inputs: make([]string, 0), - Outputs: make([]string, 0), - } -} - -type EvilBatch [][]ScenarioAlias - -type EvilScenario struct { - ID string - // provides a user-friendly way of listing input and output aliases - ConflictBatch EvilBatch - // determines whether outputs of the batch should be reused during the spam to create deep UTXO tree structure. - Reuse bool - // specifies the output type of the spam, if not provided, defaults to BasicOutput - OutputType iotago.OutputType - // if provided, the outputs from the spam will be saved into this wallet, accepted types of wallet: Reuse, RestrictedReuse. - // if type == Reuse, then wallet is available for reuse spamming scenarios that did not provide RestrictedWallet. - OutputWallet *Wallet - // if provided and reuse set to true, outputs from this wallet will be used for deep spamming, allows for controllable building of UTXO deep structures. - // if not provided evil wallet will use Reuse wallet if any is available. Accepts only RestrictedReuse wallet type. - RestrictedInputWallet *Wallet - // used together with scenario ID to create a prefix for distinct batch alias creation - BatchesCreated *atomic.Uint64 - // used to determine how many clients are needed to run this scenario, some double spends need more than one client to pass the filter - NumOfClientsNeeded int -} - -func NewEvilScenario(options ...ScenarioOption) *EvilScenario { - scenario := &EvilScenario{ - ConflictBatch: SingleTransactionBatch(), - Reuse: false, - OutputType: iotago.OutputBasic, - OutputWallet: NewWallet(), - BatchesCreated: atomic.NewUint64(0), - } - - for _, option := range options { - option(scenario) - } - scenario.ID = base58.Encode([]byte(fmt.Sprintf("%v%v%v", scenario.ConflictBatch, scenario.Reuse, scenario.OutputWallet.ID)))[:11] - scenario.NumOfClientsNeeded = calculateNumofClientsNeeded(scenario) - - return scenario -} - -func calculateNumofClientsNeeded(scenario *EvilScenario) (counter int) { - for _, conflictMap := range scenario.ConflictBatch { - if len(conflictMap) > counter { - counter = len(conflictMap) - } - } - - return -} - -// readCustomConflictsPattern determines outputs of the batch, needed for saving batch outputs to the outputWallet. -func (e *EvilScenario) readCustomConflictsPattern(batch EvilBatch) (batchOutputs map[string]types.Empty) { - outputs := make(map[string]types.Empty) - inputs := make(map[string]types.Empty) - - for _, conflictMap := range batch { - for _, conflicts := range conflictMap { - // add output to outputsAliases - for _, input := range conflicts.Inputs { - inputs[input] = types.Void - } - for _, output := range conflicts.Outputs { - outputs[output] = types.Void - } - } - } - // remove outputs that were never used as input in this EvilBatch to determine batch outputs - for output := range outputs { - if _, ok := inputs[output]; ok { - delete(outputs, output) - } - } - batchOutputs = outputs - - return -} - -// NextBatchPrefix creates a new batch prefix by increasing the number of created batches for this scenario. -func (e *EvilScenario) nextBatchPrefix() string { - return e.ID + strconv.Itoa(int(e.BatchesCreated.Add(1))) -} - -// ConflictBatchWithPrefix generates a new conflict batch with scenario prefix created from scenario ID and batch count. -// BatchOutputs are outputs of the batch that can be reused in deep spamming by collecting them in Reuse wallet. -func (e *EvilScenario) ConflictBatchWithPrefix() (prefixedBatch EvilBatch, allAliases ScenarioAlias, batchOutputs map[string]types.Empty) { - allAliases = NewScenarioAlias() - prefix := e.nextBatchPrefix() - for _, conflictMap := range e.ConflictBatch { - scenarioAlias := make([]ScenarioAlias, 0) - for _, aliases := range conflictMap { - sa := NewScenarioAlias() - for _, in := range aliases.Inputs { - sa.Inputs = append(sa.Inputs, prefix+in) - allAliases.Inputs = append(allAliases.Inputs, prefix+in) - } - for _, out := range aliases.Outputs { - sa.Outputs = append(sa.Outputs, prefix+out) - allAliases.Outputs = append(allAliases.Outputs, prefix+out) - } - scenarioAlias = append(scenarioAlias, sa) - } - prefixedBatch = append(prefixedBatch, scenarioAlias) - } - batchOutputs = e.readCustomConflictsPattern(prefixedBatch) - - return -} diff --git a/tools/evil-spammer/evilwallet/evilwallet.go b/tools/evil-spammer/evilwallet/evilwallet.go deleted file mode 100644 index 007059cce..000000000 --- a/tools/evil-spammer/evilwallet/evilwallet.go +++ /dev/null @@ -1,846 +0,0 @@ -package evilwallet - -import ( - "sync" - "time" - - "github.com/ethereum/go-ethereum/log" - - "github.com/iotaledger/hive.go/ds/types" - "github.com/iotaledger/hive.go/ierrors" - "github.com/iotaledger/hive.go/lo" - "github.com/iotaledger/hive.go/logger" - "github.com/iotaledger/hive.go/runtime/options" - "github.com/iotaledger/iota-core/pkg/blockhandler" - "github.com/iotaledger/iota-core/tools/evil-spammer/accountwallet" - evillogger "github.com/iotaledger/iota-core/tools/evil-spammer/logger" - "github.com/iotaledger/iota-core/tools/evil-spammer/models" - iotago "github.com/iotaledger/iota.go/v4" - "github.com/iotaledger/iota.go/v4/builder" - "github.com/iotaledger/iota.go/v4/nodeclient/apimodels" - "github.com/iotaledger/iota.go/v4/tpkg" -) - -const ( - // FaucetRequestSplitNumber defines the number of outputs to split from a faucet request. - FaucetRequestSplitNumber = 100 - faucetTokensPerRequest iotago.BaseToken = 1_000_000 - - waitForConfirmation = 15 * time.Second - waitForSolidification = 10 * time.Second - - awaitConfirmationSleep = 2 * time.Second - awaitSolidificationSleep = time.Millisecond * 500 -) - -var ( - defaultClientsURLs = []string{"http://localhost:8080", "http://localhost:8090"} -) - -// region EvilWallet /////////////////////////////////////////////////////////////////////////////////////////////////////// - -// EvilWallet provides a user-friendly way to do complicated double spend scenarios. -type EvilWallet struct { - // faucet is the wallet of faucet - faucet *Wallet - wallets *Wallets - accWallet *accountwallet.AccountWallet - connector models.Connector - outputManager *OutputManager - aliasManager *AliasManager - - optsClientURLs []string - log *logger.Logger -} - -// NewEvilWallet creates an EvilWallet instance. -func NewEvilWallet(opts ...options.Option[EvilWallet]) *EvilWallet { - return options.Apply(&EvilWallet{ - wallets: NewWallets(), - aliasManager: NewAliasManager(), - optsClientURLs: defaultClientsURLs, - log: evillogger.New("EvilWallet"), - }, opts, func(w *EvilWallet) { - connector := models.NewWebClients(w.optsClientURLs) - w.connector = connector - w.outputManager = NewOutputManager(connector, w.wallets, w.log) - - }) -} - -func (e *EvilWallet) LastFaucetUnspentOutput() iotago.OutputID { - faucetAddr := e.faucet.AddressOnIndex(0) - unspentFaucet := e.faucet.UnspentOutput(faucetAddr.String()) - - return unspentFaucet.OutputID -} - -// NewWallet creates a new wallet of the given wallet type. -func (e *EvilWallet) NewWallet(wType ...WalletType) *Wallet { - walletType := Other - if len(wType) != 0 { - walletType = wType[0] - } - - return e.wallets.NewWallet(walletType) -} - -// GetClients returns the given number of clients. -func (e *EvilWallet) GetClients(num int) []models.Client { - return e.connector.GetClients(num) -} - -// Connector give access to the EvilWallet connector. -func (e *EvilWallet) Connector() models.Connector { - return e.connector -} - -func (e *EvilWallet) UnspentOutputsLeft(walletType WalletType) int { - return e.wallets.UnspentOutputsLeft(walletType) -} - -func (e *EvilWallet) NumOfClient() int { - clts := e.connector.Clients() - return len(clts) -} - -func (e *EvilWallet) AddClient(clientURL string) { - e.connector.AddClient(clientURL) -} - -func (e *EvilWallet) RemoveClient(clientURL string) { - e.connector.RemoveClient(clientURL) -} - -func (e *EvilWallet) GetAccount(alias string) (blockhandler.Account, error) { - account, err := e.accWallet.GetAccount(alias) - if err != nil { - return nil, err - } - - return account.Account, nil -} - -func (e *EvilWallet) PrepareAndPostBlock(clt models.Client, payload iotago.Payload, congestionResp *apimodels.CongestionResponse, issuer blockhandler.Account) (iotago.BlockID, error) { - congestionResp, issuerResp, version, err := e.accWallet.RequestBlockBuiltData(clt.Client(), issuer.ID()) - if err != nil { - return iotago.EmptyBlockID, ierrors.Wrapf(err, "failed to get block built data for issuer %s", issuer.ID().ToHex()) - } - blockID, err := e.accWallet.PostWithBlock(clt, payload, issuer, congestionResp, issuerResp, version) - if err != nil { - return iotago.EmptyBlockID, err - } - - return blockID, nil -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// region EvilWallet Faucet Requests /////////////////////////////////////////////////////////////////////////////////// - -// RequestFundsFromFaucet requests funds from the faucet, then track the confirmed status of unspent output, -// also register the alias name for the unspent output if provided. -func (e *EvilWallet) RequestFundsFromFaucet(options ...FaucetRequestOption) (initWallet *Wallet, err error) { - initWallet = e.NewWallet(Fresh) - buildOptions := NewFaucetRequestOptions(options...) - - output, err := e.requestFaucetFunds(initWallet) - if err != nil { - return - } - - if buildOptions.outputAliasName != "" { - e.aliasManager.AddInputAlias(output, buildOptions.outputAliasName) - } - - log.Debug("Funds requested succesfully") - - return -} - -// RequestFreshBigFaucetWallets creates n new wallets, each wallet is created from one faucet request and contains 1000 outputs. -func (e *EvilWallet) RequestFreshBigFaucetWallets(numberOfWallets int) { - // channel to block the number of concurrent goroutines - semaphore := make(chan bool, 1) - wg := sync.WaitGroup{} - - for reqNum := 0; reqNum < numberOfWallets; reqNum++ { - wg.Add(1) - // block if full - semaphore <- true - go func() { - defer wg.Done() - defer func() { - // release - <-semaphore - }() - - err := e.RequestFreshBigFaucetWallet() - if err != nil { - return - } - }() - } - wg.Wait() -} - -// RequestFreshBigFaucetWallet creates a new wallet and fills the wallet with 1000 outputs created from funds -// requested from the Faucet. -func (e *EvilWallet) RequestFreshBigFaucetWallet() error { - initWallet := NewWallet() - receiveWallet := e.NewWallet(Fresh) - - txIDs := make(iotago.TransactionIDs, 0) - for i := 0; i < 1; i++ { - txID, err := e.requestAndSplitFaucetFunds(initWallet, receiveWallet) - if err != nil { - return ierrors.Wrap(err, "failed to request big funds from faucet") - } - - txIDs = append(txIDs, txID) - } - - e.outputManager.AwaitTransactionsConfirmation(txIDs...) - - e.wallets.SetWalletReady(receiveWallet) - - return nil -} - -// RequestFreshFaucetWallet creates a new wallet and fills the wallet with 100 outputs created from funds -// requested from the Faucet. -func (e *EvilWallet) RequestFreshFaucetWallet() error { - initWallet := NewWallet() - receiveWallet := e.NewWallet(Fresh) - txID, err := e.requestAndSplitFaucetFunds(initWallet, receiveWallet) - if err != nil { - return ierrors.Wrap(err, "failed to request funds from faucet") - } - - e.outputManager.AwaitTransactionsConfirmation(txID) - - e.wallets.SetWalletReady(receiveWallet) - - return err -} - -func (e *EvilWallet) requestAndSplitFaucetFunds(initWallet, receiveWallet *Wallet) (txID iotago.TransactionID, err error) { - splitOutput, err := e.requestFaucetFunds(initWallet) - if err != nil { - return iotago.EmptyTransactionID, err - } - - e.log.Debugf("Faucet funds received, continue spliting output: %s", splitOutput.OutputID.ToHex()) - // first split 1 to FaucetRequestSplitNumber outputs - return e.splitOutputs(splitOutput, initWallet, receiveWallet) -} - -func (e *EvilWallet) requestFaucetFunds(wallet *Wallet) (outputID *models.Output, err error) { - receiveAddr := wallet.AddressOnIndex(0) - clt := e.connector.GetClient() - - output, err := e.accWallet.RequestFaucetFunds(clt, receiveAddr, faucetTokensPerRequest) - if err != nil { - return nil, ierrors.Wrap(err, "failed to request funds from faucet") - } - - // update wallet with newly created output - e.outputManager.createOutputFromAddress(wallet, receiveAddr, faucetTokensPerRequest, output.OutputID, output.OutputStruct) - - return output, nil -} - -// splitOutputs splits faucet input to 100 outputs. -func (e *EvilWallet) splitOutputs(splitOutput *models.Output, inputWallet, outputWallet *Wallet) (iotago.TransactionID, error) { - if inputWallet.IsEmpty() { - return iotago.EmptyTransactionID, ierrors.New("inputWallet is empty") - } - - input, outputs := e.handleInputOutputDuringSplitOutputs(splitOutput, FaucetRequestSplitNumber, outputWallet) - - faucetAccount, err := e.accWallet.GetAccount(accountwallet.FaucetAccountAlias) - if err != nil { - return iotago.EmptyTransactionID, err - } - txData, err := e.CreateTransaction( - WithInputs(input), - WithOutputs(outputs), - WithInputWallet(inputWallet), - WithOutputWallet(outputWallet), - WithIssuanceStrategy(models.AllotmentStrategyAll, faucetAccount.Account.ID()), - ) - - if err != nil { - return iotago.EmptyTransactionID, err - } - - _, err = e.PrepareAndPostBlock(e.connector.GetClient(), txData.Payload, txData.CongestionResponse, faucetAccount.Account) - if err != nil { - return iotago.TransactionID{}, err - } - - if txData.Payload.PayloadType() != iotago.PayloadSignedTransaction { - return iotago.EmptyTransactionID, ierrors.New("payload type is not signed transaction") - } - - txID := lo.PanicOnErr(txData.Payload.(*iotago.SignedTransaction).Transaction.ID()) - - e.log.Debugf("Splitting output %s finished with tx: %s", splitOutput.OutputID.ToHex(), txID.ToHex()) - - return txID, nil -} - -func (e *EvilWallet) handleInputOutputDuringSplitOutputs(splitOutput *models.Output, splitNumber int, receiveWallet *Wallet) (input *models.Output, outputs []*OutputOption) { - input = splitOutput - - balances := SplitBalanceEqually(splitNumber, input.Balance) - for _, bal := range balances { - outputs = append(outputs, &OutputOption{amount: bal, address: receiveWallet.Address(), outputType: iotago.OutputBasic}) - } - - return -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// region EvilWallet functionality /////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// ClearAliases remove only provided aliases from AliasManager. -func (e *EvilWallet) ClearAliases(aliases ScenarioAlias) { - e.aliasManager.ClearAliases(aliases) -} - -// ClearAllAliases remove all registered alias names. -func (e *EvilWallet) ClearAllAliases() { - e.aliasManager.ClearAllAliases() -} - -func (e *EvilWallet) PrepareCustomConflicts(conflictsMaps []ConflictSlice) (conflictBatch [][]*models.PayloadIssuanceData, err error) { - for _, conflictMap := range conflictsMaps { - var txsData []*models.PayloadIssuanceData - for _, conflictOptions := range conflictMap { - txData, err2 := e.CreateTransaction(conflictOptions...) - if err2 != nil { - return nil, err2 - } - txsData = append(txsData, txData) - } - conflictBatch = append(conflictBatch, txsData) - } - - return conflictBatch, nil -} - -// CreateTransaction creates a transaction based on provided options. If no input wallet is provided, the next non-empty faucet wallet is used. -// Inputs of the transaction are determined in three ways: -// 1 - inputs are provided directly without associated alias, 2- alias is provided, and input is already stored in an alias manager, -// 3 - alias is provided, and there are no inputs assigned in Alias manager, so aliases are assigned to next ready inputs from input wallet. -func (e *EvilWallet) CreateTransaction(options ...Option) (*models.PayloadIssuanceData, error) { - buildOptions, err := NewOptions(options...) - if err != nil { - return nil, err - } - // wallet used only for outputs in the middle of the batch, that will never be reused outside custom conflict batch creation. - tempWallet := e.NewWallet() - - err = e.updateInputWallet(buildOptions) - if err != nil { - return nil, err - } - - inputs, err := e.prepareInputs(buildOptions) - if err != nil { - return nil, err - } - - outputs, addrAliasMap, tempAddresses, err := e.prepareOutputs(buildOptions, tempWallet) - if err != nil { - return nil, err - } - - alias, remainder, remainderAddr, hasRemainder := e.prepareRemainderOutput(buildOptions, outputs) - if hasRemainder { - outputs = append(outputs, remainder) - if alias != "" && addrAliasMap != nil { - addrAliasMap[remainderAddr.String()] = alias - } - } - - var congestionResp *apimodels.CongestionResponse - // request congestion endpoint if allotment strategy configured - if buildOptions.allotmentStrategy == models.AllotmentStrategyMinCost { - congestionResp, err = e.connector.GetClient().GetCongestion(buildOptions.issuerAccountID) - if err != nil { - return nil, err - } - } - - signedTx, err := e.makeTransaction(inputs, outputs, buildOptions.inputWallet, congestionResp, buildOptions.allotmentStrategy, buildOptions.issuerAccountID) - if err != nil { - return nil, err - } - txData := &models.PayloadIssuanceData{ - Payload: signedTx, - CongestionResponse: congestionResp, - } - - e.addOutputsToOutputManager(signedTx, buildOptions.outputWallet, tempWallet, tempAddresses) - e.registerOutputAliases(signedTx, addrAliasMap) - - return txData, nil -} - -// addOutputsToOutputManager adds output to the OutputManager if. -func (e *EvilWallet) addOutputsToOutputManager(signedTx *iotago.SignedTransaction, outWallet, tmpWallet *Wallet, tempAddresses map[string]types.Empty) { - for idx, o := range signedTx.Transaction.Outputs { - if o.UnlockConditionSet().Address() == nil { - continue - } - - // register UnlockConditionAddress only (skip account outputs) - addr := o.UnlockConditionSet().Address().Address - out := &models.Output{ - OutputID: iotago.OutputIDFromTransactionIDAndIndex(lo.PanicOnErr(signedTx.Transaction.ID()), uint16(idx)), - Address: addr, - Balance: o.BaseTokenAmount(), - OutputStruct: o, - } - - if _, ok := tempAddresses[addr.String()]; ok { - e.outputManager.AddOutput(tmpWallet, out) - } else { - out.Index = outWallet.AddrIndexMap(addr.String()) - e.outputManager.AddOutput(outWallet, out) - } - } -} - -// updateInputWallet if input wallet is not specified, or aliases were provided without inputs (batch inputs) use Fresh faucet wallet. -func (e *EvilWallet) updateInputWallet(buildOptions *Options) error { - for alias := range buildOptions.aliasInputs { - // inputs provided for aliases (middle inputs in a batch) - _, ok := e.aliasManager.GetInput(alias) - if ok { - // leave nil, wallet will be selected based on OutputIDWalletMap - buildOptions.inputWallet = nil - return nil - } - - break - } - wallet, err := e.useFreshIfInputWalletNotProvided(buildOptions) - if err != nil { - return err - } - buildOptions.inputWallet = wallet - - return nil -} - -func (e *EvilWallet) registerOutputAliases(signedTx *iotago.SignedTransaction, addrAliasMap map[string]string) { - if len(addrAliasMap) == 0 { - return - } - - for idx := range signedTx.Transaction.Outputs { - id := iotago.OutputIDFromTransactionIDAndIndex(lo.PanicOnErr(signedTx.Transaction.ID()), uint16(idx)) - out := e.outputManager.GetOutput(id) - if out == nil { - continue - } - - // register output alias - e.aliasManager.AddOutputAlias(out, addrAliasMap[out.Address.String()]) - - // register output as unspent output(input) - e.aliasManager.AddInputAlias(out, addrAliasMap[out.Address.String()]) - } -} - -func (e *EvilWallet) prepareInputs(buildOptions *Options) (inputs []*models.Output, err error) { - if buildOptions.areInputsProvidedWithoutAliases() { - inputs = append(inputs, buildOptions.inputs...) - - return - } - // append inputs with alias - aliasInputs, err := e.matchInputsWithAliases(buildOptions) - if err != nil { - return nil, err - } - inputs = append(inputs, aliasInputs...) - - return inputs, nil -} - -// prepareOutputs creates outputs for different scenarios, if no aliases were provided, new empty outputs are created from buildOptions.outputs balances. -func (e *EvilWallet) prepareOutputs(buildOptions *Options, tempWallet *Wallet) (outputs []iotago.Output, - addrAliasMap map[string]string, tempAddresses map[string]types.Empty, err error, -) { - if buildOptions.areOutputsProvidedWithoutAliases() { - outputs = append(outputs, buildOptions.outputs...) - } else { - // if outputs were provided with aliases - outputs, addrAliasMap, tempAddresses, err = e.matchOutputsWithAliases(buildOptions, tempWallet) - } - - return -} - -// matchInputsWithAliases gets input from the alias manager. if input was not assigned to an alias before, -// it assigns a new Fresh faucet output. -func (e *EvilWallet) matchInputsWithAliases(buildOptions *Options) (inputs []*models.Output, err error) { - // get inputs by alias - for inputAlias := range buildOptions.aliasInputs { - in, ok := e.aliasManager.GetInput(inputAlias) - if !ok { - wallet, err2 := e.useFreshIfInputWalletNotProvided(buildOptions) - if err2 != nil { - err = err2 - return - } - // No output found for given alias, use internal Fresh output if wallets are non-empty. - in = e.wallets.GetUnspentOutput(wallet) - if in == nil { - return nil, ierrors.New("could not get unspent output") - } - e.aliasManager.AddInputAlias(in, inputAlias) - } - inputs = append(inputs, in) - } - - return inputs, nil -} - -func (e *EvilWallet) useFreshIfInputWalletNotProvided(buildOptions *Options) (*Wallet, error) { - // if input wallet is not specified, use Fresh faucet wallet - if buildOptions.inputWallet == nil { - // deep spam enabled and no input reuse wallet provided, use evil wallet reuse wallet if enough outputs are available - if buildOptions.reuse { - outputsNeeded := len(buildOptions.inputs) - if wallet := e.wallets.reuseWallet(outputsNeeded); wallet != nil { - return wallet, nil - } - } - - wallet, err := e.wallets.freshWallet() - if err != nil { - return nil, ierrors.Wrap(err, "no Fresh wallet is available") - } - - return wallet, nil - } - - return buildOptions.inputWallet, nil -} - -// matchOutputsWithAliases creates outputs based on balances provided via options. -// Outputs are not yet added to the Alias Manager, as they have no ID before the transaction is created. -// Thus, they are tracker in address to alias map. If the scenario is used, the outputBatchAliases map is provided -// that indicates which outputs should be saved to the outputWallet.All other outputs are created with temporary wallet, -// and their addresses are stored in tempAddresses. -func (e *EvilWallet) matchOutputsWithAliases(buildOptions *Options, tempWallet *Wallet) (outputs []iotago.Output, - addrAliasMap map[string]string, tempAddresses map[string]types.Empty, err error, -) { - err = e.updateOutputBalances(buildOptions) - if err != nil { - return nil, nil, nil, err - } - - tempAddresses = make(map[string]types.Empty) - addrAliasMap = make(map[string]string) - for alias, output := range buildOptions.aliasOutputs { - var addr *iotago.Ed25519Address - if _, ok := buildOptions.outputBatchAliases[alias]; ok { - addr = buildOptions.outputWallet.Address() - } else { - addr = tempWallet.Address() - tempAddresses[addr.String()] = types.Void - } - - switch output.Type() { - case iotago.OutputBasic: - outputBuilder := builder.NewBasicOutputBuilder(addr, output.BaseTokenAmount()) - outputs = append(outputs, outputBuilder.MustBuild()) - case iotago.OutputAccount: - outputBuilder := builder.NewAccountOutputBuilder(addr, addr, output.BaseTokenAmount()) - outputs = append(outputs, outputBuilder.MustBuild()) - } - - addrAliasMap[addr.String()] = alias - } - - return -} - -func (e *EvilWallet) prepareRemainderOutput(buildOptions *Options, outputs []iotago.Output) (alias string, remainderOutput iotago.Output, remainderAddress iotago.Address, added bool) { - inputBalance := iotago.BaseToken(0) - - for inputAlias := range buildOptions.aliasInputs { - in, _ := e.aliasManager.GetInput(inputAlias) - inputBalance += in.Balance - - if alias == "" { - remainderAddress = in.Address - alias = inputAlias - } - } - - for _, input := range buildOptions.inputs { - // get balance from output manager - in := e.outputManager.GetOutput(input.OutputID) - inputBalance += in.Balance - - if remainderAddress == nil { - remainderAddress = in.Address - } - } - - outputBalance := iotago.BaseToken(0) - for _, o := range outputs { - outputBalance += o.BaseTokenAmount() - } - - // remainder balances is sent to one of the address in inputs - if outputBalance < inputBalance { - remainderOutput = &iotago.BasicOutput{ - Amount: inputBalance - outputBalance, - Conditions: iotago.BasicOutputUnlockConditions{ - &iotago.AddressUnlockCondition{Address: remainderAddress}, - }, - } - - added = true - } - - return -} - -func (e *EvilWallet) updateOutputBalances(buildOptions *Options) (err error) { - // when aliases are not used for outputs, the balance had to be provided in options, nothing to do - if buildOptions.areOutputsProvidedWithoutAliases() { - return - } - totalBalance := iotago.BaseToken(0) - if !buildOptions.isBalanceProvided() { - if buildOptions.areInputsProvidedWithoutAliases() { - for _, input := range buildOptions.inputs { - // get balance from output manager - inputDetails := e.outputManager.GetOutput(input.OutputID) - totalBalance += inputDetails.Balance - } - } else { - for inputAlias := range buildOptions.aliasInputs { - in, ok := e.aliasManager.GetInput(inputAlias) - if !ok { - err = ierrors.New("could not get input by input alias") - return - } - totalBalance += in.Balance - } - } - balances := SplitBalanceEqually(len(buildOptions.outputs)+len(buildOptions.aliasOutputs), totalBalance) - i := 0 - for out, output := range buildOptions.aliasOutputs { - switch output.Type() { - case iotago.OutputBasic: - buildOptions.aliasOutputs[out] = &iotago.BasicOutput{ - Amount: balances[i], - } - case iotago.OutputAccount: - buildOptions.aliasOutputs[out] = &iotago.AccountOutput{ - Amount: balances[i], - } - } - i++ - } - } - - return -} - -func (e *EvilWallet) makeTransaction(inputs []*models.Output, outputs iotago.Outputs[iotago.Output], w *Wallet, congestionResponse *apimodels.CongestionResponse, allotmentStrategy models.AllotmentStrategy, issuerAccountID iotago.AccountID) (tx *iotago.SignedTransaction, err error) { - clt := e.Connector().GetClient() - currentTime := time.Now() - targetSlot := clt.LatestAPI().TimeProvider().SlotFromTime(currentTime) - targetAPI := clt.APIForSlot(targetSlot) - - txBuilder := builder.NewTransactionBuilder(targetAPI) - - for _, input := range inputs { - txBuilder.AddInput(&builder.TxInput{UnlockTarget: input.Address, InputID: input.OutputID, Input: input.OutputStruct}) - } - - for _, output := range outputs { - txBuilder.AddOutput(output) - } - - randomPayload := tpkg.Rand12ByteArray() - txBuilder.AddTaggedDataPayload(&iotago.TaggedData{Tag: randomPayload[:], Data: randomPayload[:]}) - - walletKeys := make([]iotago.AddressKeys, len(inputs)) - for i, input := range inputs { - addr := input.Address - var wallet *Wallet - if w == nil { // aliases provided with inputs, use wallet saved in outputManager - wallet = e.outputManager.OutputIDWalletMap(input.OutputID.ToHex()) - } else { - wallet = w - } - index := wallet.AddrIndexMap(addr.String()) - inputPrivateKey, _ := wallet.KeyPair(index) - walletKeys[i] = iotago.AddressKeys{Address: addr, Keys: inputPrivateKey} - } - - txBuilder.SetCreationSlot(targetSlot) - // no allotment strategy - if congestionResponse == nil { - return txBuilder.Build(iotago.NewInMemoryAddressSigner(walletKeys...)) - } - switch allotmentStrategy { - case models.AllotmentStrategyAll: - txBuilder.AllotAllMana(targetSlot, issuerAccountID) - case models.AllotmentStrategyMinCost: - txBuilder.AllotRequiredManaAndStoreRemainingManaInOutput(targetSlot, congestionResponse.ReferenceManaCost, issuerAccountID, 0) - } - - return txBuilder.Build(iotago.NewInMemoryAddressSigner(walletKeys...)) -} - -func (e *EvilWallet) PrepareCustomConflictsSpam(scenario *EvilScenario, strategy *models.IssuancePaymentStrategy) (txsData [][]*models.PayloadIssuanceData, allAliases ScenarioAlias, err error) { - conflicts, allAliases := e.prepareConflictSliceForScenario(scenario, strategy) - txsData, err = e.PrepareCustomConflicts(conflicts) - - return txsData, allAliases, err -} - -func (e *EvilWallet) PrepareAccountSpam(scenario *EvilScenario, strategy *models.IssuancePaymentStrategy) (*models.PayloadIssuanceData, ScenarioAlias, error) { - accountSpamOptions, allAliases := e.prepareFlatOptionsForAccountScenario(scenario, strategy) - - txData, err := e.CreateTransaction(accountSpamOptions...) - - return txData, allAliases, err -} - -func (e *EvilWallet) evaluateIssuanceStrategy(strategy *models.IssuancePaymentStrategy) (models.AllotmentStrategy, iotago.AccountID) { - var issuerAccountID iotago.AccountID - if strategy.AllotmentStrategy != models.AllotmentStrategyNone { - // get issuer accountID - accData, err := e.accWallet.GetAccount(strategy.IssuerAlias) - if err != nil { - panic("could not get issuer accountID while preparing conflicts") - } - issuerAccountID = accData.Account.ID() - } - return strategy.AllotmentStrategy, issuerAccountID -} - -func (e *EvilWallet) prepareConflictSliceForScenario(scenario *EvilScenario, strategy *models.IssuancePaymentStrategy) (conflictSlice []ConflictSlice, allAliases ScenarioAlias) { - genOutputOptions := func(aliases []string) []*OutputOption { - outputOptions := make([]*OutputOption, 0) - for _, o := range aliases { - outputOptions = append(outputOptions, &OutputOption{aliasName: o, outputType: iotago.OutputBasic}) - } - - return outputOptions - } - - // make conflictSlice - prefixedBatch, allAliases, batchOutputs := scenario.ConflictBatchWithPrefix() - conflictSlice = make([]ConflictSlice, 0) - for _, conflictMap := range prefixedBatch { - conflicts := make([][]Option, 0) - for _, aliases := range conflictMap { - outs := genOutputOptions(aliases.Outputs) - option := []Option{WithInputs(aliases.Inputs), WithOutputs(outs), WithOutputBatchAliases(batchOutputs)} - if scenario.OutputWallet != nil { - option = append(option, WithOutputWallet(scenario.OutputWallet)) - } - if scenario.RestrictedInputWallet != nil { - option = append(option, WithInputWallet(scenario.RestrictedInputWallet)) - } - if scenario.Reuse { - option = append(option, WithReuseOutputs()) - } - option = append(option, WithIssuanceStrategy(e.evaluateIssuanceStrategy(strategy))) - conflicts = append(conflicts, option) - } - conflictSlice = append(conflictSlice, conflicts) - } - - return -} - -func (e *EvilWallet) prepareFlatOptionsForAccountScenario(scenario *EvilScenario, strategy *models.IssuancePaymentStrategy) ([]Option, ScenarioAlias) { - // we do not care about batchedOutputs, because we do not support saving account spam result in evil wallet for now - prefixedBatch, allAliases, _ := scenario.ConflictBatchWithPrefix() - if len(prefixedBatch) != 1 { - panic("invalid scenario, cannot prepare flat option structure with deep scenario, EvilBatch should have only one element") - } - evilBatch := prefixedBatch[0] - if len(evilBatch) != 1 { - panic("invalid scenario, cannot prepare flat option structure with deep scenario, EvilBatch should have only one element") - } - - genOutputOptions := func(aliases []string) []*OutputOption { - outputOptions := make([]*OutputOption, 0) - for _, o := range aliases { - outputOptions = append(outputOptions, &OutputOption{ - aliasName: o, - outputType: iotago.OutputAccount, - }) - } - - return outputOptions - } - scenarioAlias := evilBatch[0] - outs := genOutputOptions(scenarioAlias.Outputs) - - return []Option{ - WithInputs(scenarioAlias.Inputs), - WithOutputs(outs), - WithIssuanceStrategy(e.evaluateIssuanceStrategy(strategy)), - }, allAliases -} - -// AwaitInputsSolidity waits for all inputs to be solid for client clt. -// func (e *EvilWallet) AwaitInputsSolidity(inputs devnetvm.Inputs, clt Client) (allSolid bool) { -// awaitSolid := make([]string, 0) -// for _, in := range inputs { -// awaitSolid = append(awaitSolid, in.Base58()) -// } -// allSolid = e.outputManager.AwaitOutputsToBeSolid(awaitSolid, clt, maxGoroutines) -// return -// } - -// SetTxOutputsSolid marks all outputs as solid in OutputManager for clientID. -func (e *EvilWallet) SetTxOutputsSolid(outputs iotago.OutputIDs, clientID string) { - for _, out := range outputs { - e.outputManager.SetOutputIDSolidForIssuer(out, clientID) - } -} - -// AddReuseOutputsToThePool adds all addresses corresponding to provided outputs to the reuse pool. -// func (e *EvilWallet) AddReuseOutputsToThePool(outputs devnetvm.Outputs) { -// for _, out := range outputs { -// evilOutput := e.outputManager.GetOutput(out.ID()) -// if evilOutput != nil { -// wallet := e.outputManager.OutputIDWalletMap(out.ID().Base58()) -// wallet.AddReuseAddress(evilOutput.Address.Base58()) -// } -// } -// } - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// - -func WithClients(urls ...string) options.Option[EvilWallet] { - return func(opts *EvilWallet) { - opts.optsClientURLs = urls - } -} - -func WithAccountsWallet(wallet *accountwallet.AccountWallet) options.Option[EvilWallet] { - return func(opts *EvilWallet) { - opts.accWallet = wallet - } -} diff --git a/tools/evil-spammer/evilwallet/options.go b/tools/evil-spammer/evilwallet/options.go deleted file mode 100644 index 7b0b581f8..000000000 --- a/tools/evil-spammer/evilwallet/options.go +++ /dev/null @@ -1,295 +0,0 @@ -package evilwallet - -import ( - "time" - - "github.com/iotaledger/hive.go/ds/types" - "github.com/iotaledger/hive.go/ierrors" - "github.com/iotaledger/iota-core/tools/evil-spammer/models" - iotago "github.com/iotaledger/iota.go/v4" - "github.com/iotaledger/iota.go/v4/builder" -) - -// region Options /////////////////////////////////////////////////////////////////////////// - -// Options is a struct that represents a collection of options that can be set when creating a block. -type Options struct { - aliasInputs map[string]types.Empty - inputs []*models.Output - aliasOutputs map[string]iotago.Output - outputs []iotago.Output - inputWallet *Wallet - outputWallet *Wallet - outputBatchAliases map[string]types.Empty - reuse bool - issuingTime time.Time - allotmentStrategy models.AllotmentStrategy - issuerAccountID iotago.AccountID - // maps input alias to desired output type, used to create account output types - specialOutputTypes map[string]iotago.OutputType -} - -type OutputOption struct { - aliasName string - amount iotago.BaseToken - address *iotago.Ed25519Address - outputType iotago.OutputType -} - -// NewOptions is the constructor for the tx creation. -func NewOptions(options ...Option) (option *Options, err error) { - option = &Options{ - aliasInputs: make(map[string]types.Empty), - inputs: make([]*models.Output, 0), - aliasOutputs: make(map[string]iotago.Output), - outputs: make([]iotago.Output, 0), - specialOutputTypes: make(map[string]iotago.OutputType), - } - - for _, opt := range options { - opt(option) - } - - // check if alias and non-alias are mixed in use. - if err := option.checkInputsAndOutputs(); err != nil { - return nil, err - } - - // input and output wallets must be provided if inputs/outputs are not aliases. - if err := option.isWalletProvidedForInputsOutputs(); err != nil { - return nil, err - } - - if option.outputWallet == nil { - option.outputWallet = NewWallet() - } - - return -} - -// Option is the type that is used for options that can be passed into the CreateBlock method to configure its -// behavior. -type Option func(*Options) - -func (o *Options) isBalanceProvided() bool { - provided := false - - for _, output := range o.aliasOutputs { - if output.BaseTokenAmount() > 0 { - provided = true - } - } - - return provided -} - -func (o *Options) isWalletProvidedForInputsOutputs() error { - if o.areInputsProvidedWithoutAliases() { - if o.inputWallet == nil { - return ierrors.New("no input wallet provided for inputs without aliases") - } - } - if o.areOutputsProvidedWithoutAliases() { - if o.outputWallet == nil { - return ierrors.New("no output wallet provided for outputs without aliases") - } - } - - return nil -} - -func (o *Options) areInputsProvidedWithoutAliases() bool { - return len(o.inputs) > 0 -} - -func (o *Options) areOutputsProvidedWithoutAliases() bool { - return len(o.outputs) > 0 -} - -// checkInputsAndOutputs checks if either all provided inputs/outputs are with aliases or all are without, -// we do not allow for mixing those two possibilities. -func (o *Options) checkInputsAndOutputs() error { - inLength, outLength, aliasInLength, aliasOutLength := len(o.inputs), len(o.outputs), len(o.aliasInputs), len(o.aliasOutputs) - - if (inLength == 0 && aliasInLength == 0) || (outLength == 0 && aliasOutLength == 0) { - return ierrors.New("no inputs or outputs provided") - } - - inputsOk := (inLength > 0 && aliasInLength == 0) || (aliasInLength > 0 && inLength == 0) - outputsOk := (outLength > 0 && aliasOutLength == 0) || (aliasOutLength > 0 && outLength == 0) - if !inputsOk || !outputsOk { - return ierrors.New("mixing providing inputs/outputs with and without aliases is not allowed") - } - - return nil -} - -// WithInputs returns an Option that is used to provide the Inputs of the Transaction. -func WithInputs(inputs interface{}) Option { - return func(options *Options) { - switch in := inputs.(type) { - case string: - options.aliasInputs[in] = types.Void - case []string: - for _, input := range in { - options.aliasInputs[input] = types.Void - } - case *models.Output: - options.inputs = append(options.inputs, in) - case []*models.Output: - options.inputs = append(options.inputs, in...) - } - } -} - -// WithOutputs returns an Option that is used to define a non-colored Outputs for the Transaction in the Block. -func WithOutputs(outputsOptions []*OutputOption) Option { - return func(options *Options) { - for _, outputOptions := range outputsOptions { - var output iotago.Output - switch outputOptions.outputType { - case iotago.OutputBasic: - outputBuilder := builder.NewBasicOutputBuilder(outputOptions.address, outputOptions.amount) - output = outputBuilder.MustBuild() - case iotago.OutputAccount: - outputBuilder := builder.NewAccountOutputBuilder(outputOptions.address, outputOptions.address, outputOptions.amount) - output = outputBuilder.MustBuild() - } - - if outputOptions.aliasName != "" { - options.aliasOutputs[outputOptions.aliasName] = output - } else { - options.outputs = append(options.outputs, output) - } - } - } -} - -func WithIssuanceStrategy(strategy models.AllotmentStrategy, issuerID iotago.AccountID) Option { - return func(options *Options) { - options.allotmentStrategy = strategy - options.issuerAccountID = issuerID - } -} - -// WithInputWallet returns a BlockOption that is used to define the inputWallet of the Block. -func WithInputWallet(issuer *Wallet) Option { - return func(options *Options) { - options.inputWallet = issuer - } -} - -// WithOutputWallet returns a BlockOption that is used to define the inputWallet of the Block. -func WithOutputWallet(wallet *Wallet) Option { - return func(options *Options) { - options.outputWallet = wallet - } -} - -// WithOutputBatchAliases returns a BlockOption that is used to determine which outputs should be added to the outWallet. -func WithOutputBatchAliases(outputAliases map[string]types.Empty) Option { - return func(options *Options) { - options.outputBatchAliases = outputAliases - } -} - -// WithReuseOutputs returns a BlockOption that is used to enable deep spamming with Reuse wallet outputs. -func WithReuseOutputs() Option { - return func(options *Options) { - options.reuse = true - } -} - -// WithIssuingTime returns a BlockOption that is used to set issuing time of the Block. -func WithIssuingTime(issuingTime time.Time) Option { - return func(options *Options) { - options.issuingTime = issuingTime - } -} - -// ConflictSlice represents a set of conflict transactions. -type ConflictSlice [][]Option - -// endregion ////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// region FaucetRequestOptions ///////////////////////////////////////////////////////////////////////////////////////// - -// FaucetRequestOptions is options for faucet request. -type FaucetRequestOptions struct { - outputAliasName string -} - -// NewFaucetRequestOptions creates options for a faucet request. -func NewFaucetRequestOptions(options ...FaucetRequestOption) *FaucetRequestOptions { - reqOptions := &FaucetRequestOptions{ - outputAliasName: "", - } - - for _, option := range options { - option(reqOptions) - } - - return reqOptions -} - -// FaucetRequestOption is an option for faucet request. -type FaucetRequestOption func(*FaucetRequestOptions) - -// WithOutputAlias returns an Option that is used to provide the Output of the Transaction. -func WithOutputAlias(aliasName string) FaucetRequestOption { - return func(options *FaucetRequestOptions) { - options.outputAliasName = aliasName - } -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// region EvilScenario Options ///////////////////////////////////////////////////////////////////////////////////////// - -type ScenarioOption func(scenario *EvilScenario) - -// WithScenarioCustomConflicts specifies the EvilBatch that describes the UTXO structure that should be used for the spam. -func WithScenarioCustomConflicts(batch EvilBatch) ScenarioOption { - return func(options *EvilScenario) { - if batch != nil { - options.ConflictBatch = batch - } - } -} - -// WithScenarioDeepSpamEnabled enables deep spam, the outputs from available Reuse wallets or RestrictedReuse wallet -// if provided with WithReuseInputWalletForDeepSpam option will be used for spam instead fresh faucet outputs. -func WithScenarioDeepSpamEnabled() ScenarioOption { - return func(options *EvilScenario) { - options.Reuse = true - } -} - -// WithScenarioReuseOutputWallet the outputs from the spam will be saved into this wallet, accepted types of wallet: Reuse, RestrictedReuse. -func WithScenarioReuseOutputWallet(wallet *Wallet) ScenarioOption { - return func(options *EvilScenario) { - if wallet != nil { - if wallet.walletType == Reuse || wallet.walletType == RestrictedReuse { - options.OutputWallet = wallet - } - } - } -} - -// WithScenarioInputWalletForDeepSpam reuse set to true, outputs from this wallet will be used for deep spamming, -// allows for controllable building of UTXO deep structures. Accepts only RestrictedReuse wallet type. -func WithScenarioInputWalletForDeepSpam(wallet *Wallet) ScenarioOption { - return func(options *EvilScenario) { - if wallet.walletType == RestrictedReuse { - options.RestrictedInputWallet = wallet - } - } -} - -func WithCreateAccounts() ScenarioOption { - return func(options *EvilScenario) { - options.OutputType = iotago.OutputAccount - } -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/tools/evil-spammer/evilwallet/output_manager.go b/tools/evil-spammer/evilwallet/output_manager.go deleted file mode 100644 index e86ac7448..000000000 --- a/tools/evil-spammer/evilwallet/output_manager.go +++ /dev/null @@ -1,367 +0,0 @@ -package evilwallet - -import ( - "sync" - "sync/atomic" - "time" - - "github.com/iotaledger/hive.go/ds/types" - "github.com/iotaledger/hive.go/ierrors" - "github.com/iotaledger/hive.go/logger" - "github.com/iotaledger/hive.go/runtime/syncutils" - "github.com/iotaledger/iota-core/tools/evil-spammer/models" - iotago "github.com/iotaledger/iota.go/v4" -) - -const ( - awaitOutputToBeConfirmed = 10 * time.Second -) - -// OutputManager keeps track of the output statuses. -type OutputManager struct { - connector models.Connector - - wallets *Wallets - outputIDWalletMap map[string]*Wallet - outputIDAddrMap map[string]string - // stores solid outputs per node - issuerSolidOutIDMap map[string]map[iotago.OutputID]types.Empty - - log *logger.Logger - - syncutils.RWMutex -} - -// NewOutputManager creates an OutputManager instance. -func NewOutputManager(connector models.Connector, wallets *Wallets, log *logger.Logger) *OutputManager { - return &OutputManager{ - connector: connector, - wallets: wallets, - outputIDWalletMap: make(map[string]*Wallet), - outputIDAddrMap: make(map[string]string), - issuerSolidOutIDMap: make(map[string]map[iotago.OutputID]types.Empty), - log: log, - } -} - -// setOutputIDWalletMap sets wallet for the provided outputID. -func (o *OutputManager) setOutputIDWalletMap(outputID string, wallet *Wallet) { - o.Lock() - defer o.Unlock() - - o.outputIDWalletMap[outputID] = wallet -} - -// setOutputIDAddrMap sets address for the provided outputID. -func (o *OutputManager) setOutputIDAddrMap(outputID string, addr string) { - o.Lock() - defer o.Unlock() - - o.outputIDAddrMap[outputID] = addr -} - -// OutputIDWalletMap returns wallet corresponding to the outputID stored in OutputManager. -func (o *OutputManager) OutputIDWalletMap(outputID string) *Wallet { - o.RLock() - defer o.RUnlock() - - return o.outputIDWalletMap[outputID] -} - -// OutputIDAddrMap returns address corresponding to the outputID stored in OutputManager. -func (o *OutputManager) OutputIDAddrMap(outputID string) (addr string) { - o.RLock() - defer o.RUnlock() - - addr = o.outputIDAddrMap[outputID] - - return -} - -// SetOutputIDSolidForIssuer sets solid flag for the provided outputID and issuer. -func (o *OutputManager) SetOutputIDSolidForIssuer(outputID iotago.OutputID, issuer string) { - o.Lock() - defer o.Unlock() - - if _, ok := o.issuerSolidOutIDMap[issuer]; !ok { - o.issuerSolidOutIDMap[issuer] = make(map[iotago.OutputID]types.Empty) - } - o.issuerSolidOutIDMap[issuer][outputID] = types.Void -} - -// IssuerSolidOutIDMap checks whether output was marked as solid for a given node. -func (o *OutputManager) IssuerSolidOutIDMap(issuer string, outputID iotago.OutputID) (isSolid bool) { - o.RLock() - defer o.RUnlock() - - if solidOutputs, ok := o.issuerSolidOutIDMap[issuer]; ok { - if _, isSolid = solidOutputs[outputID]; isSolid { - return - } - } - - return -} - -// Track the confirmed statuses of the given outputIDs, it returns true if all of them are confirmed. -func (o *OutputManager) Track(outputIDs ...iotago.OutputID) (allConfirmed bool) { - var ( - wg sync.WaitGroup - unconfirmedOutputFound atomic.Bool - ) - - for _, ID := range outputIDs { - wg.Add(1) - - go func(id iotago.OutputID) { - defer wg.Done() - - if !o.AwaitOutputToBeAccepted(id, awaitOutputToBeConfirmed) { - unconfirmedOutputFound.Store(true) - } - }(ID) - } - wg.Wait() - - return !unconfirmedOutputFound.Load() -} - -// createOutputFromAddress creates output, retrieves outputID, and adds it to the wallet. -// Provided address should be generated from provided wallet. Considers only first output found on address. -func (o *OutputManager) createOutputFromAddress(w *Wallet, addr *iotago.Ed25519Address, balance iotago.BaseToken, outputID iotago.OutputID, outputStruct iotago.Output) *models.Output { - index := w.AddrIndexMap(addr.String()) - out := &models.Output{ - Address: addr, - Index: index, - OutputID: outputID, - Balance: balance, - OutputStruct: outputStruct, - } - w.AddUnspentOutput(out) - o.setOutputIDWalletMap(outputID.ToHex(), w) - o.setOutputIDAddrMap(outputID.ToHex(), addr.String()) - - return out -} - -// AddOutput adds existing output from wallet w to the OutputManager. -func (o *OutputManager) AddOutput(w *Wallet, output *models.Output) *models.Output { - idx := w.AddrIndexMap(output.Address.String()) - out := &models.Output{ - Address: output.Address, - Index: idx, - OutputID: output.OutputID, - Balance: output.Balance, - OutputStruct: output.OutputStruct, - } - w.AddUnspentOutput(out) - o.setOutputIDWalletMap(out.OutputID.ToHex(), w) - o.setOutputIDAddrMap(out.OutputID.ToHex(), output.Address.String()) - - return out -} - -// GetOutput returns the Output of the given outputID. -// Firstly checks if output can be retrieved by outputManager from wallet, if not does an API call. -func (o *OutputManager) GetOutput(outputID iotago.OutputID) (output *models.Output) { - output = o.getOutputFromWallet(outputID) - - // get output info via web api - if output == nil { - clt := o.connector.GetClient() - out := clt.GetOutput(outputID) - if out == nil { - return nil - } - - basicOutput, isBasic := out.(*iotago.BasicOutput) - if !isBasic { - return nil - } - - output = &models.Output{ - OutputID: outputID, - Address: basicOutput.UnlockConditionSet().Address().Address, - Balance: basicOutput.BaseTokenAmount(), - OutputStruct: basicOutput, - } - } - - return output -} - -func (o *OutputManager) getOutputFromWallet(outputID iotago.OutputID) (output *models.Output) { - o.RLock() - defer o.RUnlock() - w, ok := o.outputIDWalletMap[outputID.ToHex()] - if ok { - addr := o.outputIDAddrMap[outputID.ToHex()] - output = w.UnspentOutput(addr) - } - - return -} - -// RequestOutputsByAddress finds the unspent outputs of a given address and updates the provided output status map. -// func (o *OutputManager) RequestOutputsByAddress(address string) (outputIDs []iotago.OutputID) { -// s := time.Now() -// clt := o.connector.GetClient() -// for ; time.Since(s) < awaitOutputsByAddress; time.Sleep(1 * time.Second) { -// outputIDs, err := clt.GetAddressUnspentOutputs(address) -// if err == nil && len(outputIDs) > 0 { -// return outputIDs -// } -// } - -// return -// } - -// RequestOutputsByTxID adds the outputs of a given transaction to the output status map. -func (o *OutputManager) RequestOutputsByTxID(txID iotago.TransactionID) (outputIDs iotago.OutputIDs) { - clt := o.connector.GetClient() - - tx, err := clt.GetTransaction(txID) - if err != nil { - return - } - - for index := range tx.Transaction.Outputs { - outputIDs = append(outputIDs, iotago.OutputIDFromTransactionIDAndIndex(txID, uint16(index))) - } - - return outputIDs -} - -// AwaitWalletOutputsToBeConfirmed awaits for all outputs in the wallet are confirmed. -func (o *OutputManager) AwaitWalletOutputsToBeConfirmed(wallet *Wallet) { - wg := sync.WaitGroup{} - for _, output := range wallet.UnspentOutputs() { - wg.Add(1) - if output == nil { - continue - } - - var outs iotago.OutputIDs - outs = append(outs, output.OutputID) - - go func(outs iotago.OutputIDs) { - defer wg.Done() - - o.Track(outs...) - }(outs) - } - wg.Wait() -} - -// AwaitOutputToBeAccepted awaits for output from a provided outputID is accepted. Timeout is waitFor. -// Useful when we have only an address and no transactionID, e.g. faucet funds request. -func (o *OutputManager) AwaitOutputToBeAccepted(outputID iotago.OutputID, waitFor time.Duration) (accepted bool) { - s := time.Now() - clt := o.connector.GetClient() - accepted = false - for ; time.Since(s) < waitFor; time.Sleep(awaitConfirmationSleep) { - confirmationState := clt.GetOutputConfirmationState(outputID) - if confirmationState == "confirmed" { - accepted = true - break - } - } - - return accepted -} - -// AwaitTransactionsConfirmation awaits for transaction confirmation and updates wallet with outputIDs. -func (o *OutputManager) AwaitTransactionsConfirmation(txIDs ...iotago.TransactionID) { - wg := sync.WaitGroup{} - semaphore := make(chan bool, 1) - - o.log.Debugf("Awaiting confirmation of %d transactions", len(txIDs)) - - for _, txID := range txIDs { - wg.Add(1) - go func(txID iotago.TransactionID) { - defer wg.Done() - semaphore <- true - defer func() { - <-semaphore - }() - err := o.AwaitTransactionToBeAccepted(txID, waitForConfirmation) - if err != nil { - return - } - }(txID) - } - wg.Wait() -} - -// AwaitTransactionToBeAccepted awaits for acceptance of a single transaction. -func (o *OutputManager) AwaitTransactionToBeAccepted(txID iotago.TransactionID, waitFor time.Duration) error { - s := time.Now() - clt := o.connector.GetClient() - var accepted bool - for ; time.Since(s) < waitFor; time.Sleep(awaitConfirmationSleep) { - confirmationState := clt.GetTransactionConfirmationState(txID) - o.log.Debugf("Tx %s confirmationState: %s", txID.ToHex(), confirmationState) - if confirmationState == "confirmed" || confirmationState == "finalized" { - accepted = true - break - } - } - if !accepted { - return ierrors.Errorf("transaction %s not accepted in time", txID) - } - - o.log.Debugf("Transaction %s accepted", txID) - - return nil -} - -// AwaitOutputToBeSolid awaits for solidification of a single output by provided clt. -func (o *OutputManager) AwaitOutputToBeSolid(outID iotago.OutputID, clt models.Client, waitFor time.Duration) error { - s := time.Now() - var solid bool - - for ; time.Since(s) < waitFor; time.Sleep(awaitSolidificationSleep) { - solid = o.IssuerSolidOutIDMap(clt.URL(), outID) - if solid { - break - } - if output := clt.GetOutput(outID); output != nil { - o.SetOutputIDSolidForIssuer(outID, clt.URL()) - solid = true - - break - } - } - if !solid { - return ierrors.Errorf("output %s not solidified in time", outID) - } - - return nil -} - -// AwaitOutputsToBeSolid awaits for all provided outputs are solid for a provided client. -func (o *OutputManager) AwaitOutputsToBeSolid(outputs iotago.OutputIDs, clt models.Client, maxGoroutines int) (allSolid bool) { - wg := sync.WaitGroup{} - semaphore := make(chan bool, maxGoroutines) - allSolid = true - - for _, outID := range outputs { - wg.Add(1) - go func(outID iotago.OutputID) { - defer wg.Done() - semaphore <- true - defer func() { - <-semaphore - }() - err := o.AwaitOutputToBeSolid(outID, clt, waitForSolidification) - if err != nil { - allSolid = false - return - } - }(outID) - } - wg.Wait() - - return -} diff --git a/tools/evil-spammer/evilwallet/utils.go b/tools/evil-spammer/evilwallet/utils.go deleted file mode 100644 index 733d86987..000000000 --- a/tools/evil-spammer/evilwallet/utils.go +++ /dev/null @@ -1,25 +0,0 @@ -package evilwallet - -import iotago "github.com/iotaledger/iota.go/v4" - -// region utxo/tx realted functions //////////////////////////////////////////////////////////////////////////////////////////// - -// SplitBalanceEqually splits the balance equally between `splitNumber` outputs. -func SplitBalanceEqually(splitNumber int, balance iotago.BaseToken) []iotago.BaseToken { - outputBalances := make([]iotago.BaseToken, 0) - - // make sure the output balances are equal input - var totalBalance iotago.BaseToken - - // input is divided equally among outputs - for i := 0; i < splitNumber-1; i++ { - outputBalances = append(outputBalances, balance/iotago.BaseToken(splitNumber)) - totalBalance += outputBalances[i] - } - lastBalance := balance - totalBalance - outputBalances = append(outputBalances, lastBalance) - - return outputBalances -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/tools/evil-spammer/evilwallet/wallet.go b/tools/evil-spammer/evilwallet/wallet.go deleted file mode 100644 index 5b97d86f8..000000000 --- a/tools/evil-spammer/evilwallet/wallet.go +++ /dev/null @@ -1,259 +0,0 @@ -package evilwallet - -import ( - "crypto/ed25519" - - "go.uber.org/atomic" - - "github.com/iotaledger/hive.go/ds/types" - "github.com/iotaledger/hive.go/runtime/syncutils" - "github.com/iotaledger/iota-core/pkg/testsuite/mock" - "github.com/iotaledger/iota-core/tools/evil-spammer/models" - iotago "github.com/iotaledger/iota.go/v4" - "github.com/iotaledger/iota.go/v4/tpkg" -) - -// region Wallet /////////////////////////////////////////////////////////////////////////////////////////////////////// - -// Wallet is the definition of a wallet. -type Wallet struct { - ID walletID - walletType WalletType - unspentOutputs map[string]*models.Output // maps addr to its unspentOutput - indexAddrMap map[uint64]string - addrIndexMap map[string]uint64 - inputTransactions map[string]types.Empty - reuseAddressPool map[string]types.Empty - seed [32]byte - - lastAddrIdxUsed atomic.Int64 // used during filling in wallet with new outputs - lastAddrSpent atomic.Int64 // used during spamming with outputs one by one - - *syncutils.RWMutex -} - -// NewWallet creates a wallet of a given type. -func NewWallet(wType ...WalletType) *Wallet { - walletType := Other - if len(wType) > 0 { - walletType = wType[0] - } - idxSpent := atomic.NewInt64(-1) - addrUsed := atomic.NewInt64(-1) - - wallet := &Wallet{ - walletType: walletType, - ID: -1, - seed: tpkg.RandEd25519Seed(), - unspentOutputs: make(map[string]*models.Output), - indexAddrMap: make(map[uint64]string), - addrIndexMap: make(map[string]uint64), - inputTransactions: make(map[string]types.Empty), - lastAddrSpent: *idxSpent, - lastAddrIdxUsed: *addrUsed, - RWMutex: &syncutils.RWMutex{}, - } - - if walletType == Reuse { - wallet.reuseAddressPool = make(map[string]types.Empty) - } - - return wallet -} - -// Type returns the wallet type. -func (w *Wallet) Type() WalletType { - return w.walletType -} - -// Address returns a new and unused address of a given wallet. -func (w *Wallet) Address() *iotago.Ed25519Address { - w.Lock() - defer w.Unlock() - - index := uint64(w.lastAddrIdxUsed.Add(1)) - hdWallet := mock.NewHDWallet("", w.seed[:], index) - //nolint:forcetypeassert - addr := hdWallet.Address(iotago.AddressEd25519).(*iotago.Ed25519Address) - w.indexAddrMap[index] = addr.String() - w.addrIndexMap[addr.String()] = index - - return addr -} - -// AddressOnIndex returns a new and unused address of a given wallet. -func (w *Wallet) AddressOnIndex(index uint64) *iotago.Ed25519Address { - w.Lock() - defer w.Unlock() - - hdWallet := mock.NewHDWallet("", w.seed[:], index) - //nolint:forcetypeassert - addr := hdWallet.Address(iotago.AddressEd25519).(*iotago.Ed25519Address) - - return addr -} - -// UnspentOutput returns the unspent output on the address. -func (w *Wallet) UnspentOutput(addr string) *models.Output { - w.RLock() - defer w.RUnlock() - - return w.unspentOutputs[addr] -} - -// UnspentOutputs returns all unspent outputs on the wallet. -func (w *Wallet) UnspentOutputs() (outputs map[string]*models.Output) { - w.RLock() - defer w.RUnlock() - outputs = make(map[string]*models.Output) - for addr, outs := range w.unspentOutputs { - outputs[addr] = outs - } - - return outputs -} - -// IndexAddrMap returns the address for the index specified. -func (w *Wallet) IndexAddrMap(outIndex uint64) string { - w.RLock() - defer w.RUnlock() - - return w.indexAddrMap[outIndex] -} - -// AddrIndexMap returns the index for the address specified. -func (w *Wallet) AddrIndexMap(address string) uint64 { - w.RLock() - defer w.RUnlock() - - return w.addrIndexMap[address] -} - -// AddUnspentOutput adds an unspentOutput of a given wallet. -func (w *Wallet) AddUnspentOutput(output *models.Output) { - w.Lock() - defer w.Unlock() - w.unspentOutputs[output.Address.String()] = output -} - -// UnspentOutputBalance returns the balance on the unspent output sitting on the address specified. -func (w *Wallet) UnspentOutputBalance(addr string) iotago.BaseToken { - w.RLock() - defer w.RUnlock() - - total := iotago.BaseToken(0) - if out, ok := w.unspentOutputs[addr]; ok { - total += out.Balance - } - - return total -} - -// IsEmpty returns true if the wallet is empty. -func (w *Wallet) IsEmpty() (empty bool) { - switch w.walletType { - case Reuse: - empty = len(w.reuseAddressPool) == 0 - default: - empty = w.UnspentOutputsLength() == 0 - } - - return -} - -// UnspentOutputsLeft returns how many unused outputs are available in wallet. -func (w *Wallet) UnspentOutputsLeft() (left int) { - switch w.walletType { - case Reuse: - left = len(w.reuseAddressPool) - default: - left = int(w.lastAddrIdxUsed.Load() - w.lastAddrSpent.Load()) - } - - return -} - -// AddReuseAddress adds address to the reuse ready outputs' addresses pool for a Reuse wallet. -func (w *Wallet) AddReuseAddress(addr string) { - w.Lock() - defer w.Unlock() - - if w.walletType == Reuse { - w.reuseAddressPool[addr] = types.Void - } -} - -// GetReuseAddress get random address from reuse addresses reuseOutputsAddresses pool. Address is removed from the pool after selecting. -func (w *Wallet) GetReuseAddress() string { - w.Lock() - defer w.Unlock() - - if w.walletType == Reuse { - if len(w.reuseAddressPool) > 0 { - for addr := range w.reuseAddressPool { - delete(w.reuseAddressPool, addr) - return addr - } - } - } - - return "" -} - -// GetUnspentOutput returns an unspent output on the oldest address ordered by index. -func (w *Wallet) GetUnspentOutput() *models.Output { - switch w.walletType { - case Reuse: - addr := w.GetReuseAddress() - return w.UnspentOutput(addr) - default: - if w.lastAddrSpent.Load() < w.lastAddrIdxUsed.Load() { - idx := w.lastAddrSpent.Add(1) - addr := w.IndexAddrMap(uint64(idx)) - outs := w.UnspentOutput(addr) - - return outs - } - } - - return nil -} - -// Sign signs the tx essence. -func (w *Wallet) AddressSigner(addr *iotago.Ed25519Address) iotago.AddressSigner { - w.RLock() - defer w.RUnlock() - index := w.AddrIndexMap(addr.String()) - hdWallet := mock.NewHDWallet("", w.seed[:], index) - - return hdWallet.AddressSigner() -} - -func (w *Wallet) KeyPair(index uint64) (ed25519.PrivateKey, ed25519.PublicKey) { - w.RLock() - defer w.RUnlock() - hdWallet := mock.NewHDWallet("", w.seed[:], index) - - return hdWallet.KeyPair() -} - -// UpdateUnspentOutputID updates the unspent output on the address specified. -// func (w *Wallet) UpdateUnspentOutputID(addr string, outputID utxo.OutputID) error { -// w.RLock() -// walletOutput, ok := w.unspentOutputs[addr] -// w.RUnlock() -// if !ok { -// return errors.Errorf("could not find unspent output under provided address in the wallet, outID:%s, addr: %s", outputID.Base58(), addr) -// } -// w.Lock() -// walletOutput.OutputID = outputID -// w.Unlock() -// return nil -// } - -// UnspentOutputsLength returns the number of unspent outputs on the wallet. -func (w *Wallet) UnspentOutputsLength() int { - return len(w.unspentOutputs) -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/tools/evil-spammer/evilwallet/wallets.go b/tools/evil-spammer/evilwallet/wallets.go deleted file mode 100644 index 31e5c0366..000000000 --- a/tools/evil-spammer/evilwallet/wallets.go +++ /dev/null @@ -1,226 +0,0 @@ -package evilwallet - -import ( - "go.uber.org/atomic" - - "github.com/iotaledger/hive.go/ierrors" - "github.com/iotaledger/hive.go/runtime/syncutils" - "github.com/iotaledger/iota-core/tools/evil-spammer/models" -) - -type walletID int - -type ( - // WalletType is the type of the wallet. - //nolint:revive - WalletType int8 - //nolint:revive - WalletStatus int8 -) - -const ( - Other WalletType = iota - // Fresh is used for automatic Faucet Requests, outputs are returned one by one. - Fresh - // Reuse stores resulting outputs of double spends or transactions issued by the evilWallet, - // outputs from this wallet are reused in spamming scenario with flag reuse set to true and no RestrictedReuse wallet provided. - // Reusing spammed outputs allows for creation of deeper UTXO DAG structure. - Reuse - // RestrictedReuse it is a reuse wallet, that will be available only to spamming scenarios that - // will provide RestrictedWallet for the reuse spamming. - RestrictedReuse -) - -// region Wallets /////////////////////////////////////////////////////////////////////////////////////////////////////// - -// Wallets is a container of wallets. -type Wallets struct { - wallets map[walletID]*Wallet - // we store here non-empty wallets ids of wallets with Fresh faucet outputs. - faucetWallets []walletID - // reuse wallets are stored without an order, so they are picked up randomly. - // Boolean flag indicates if wallet is ready - no new addresses will be generated, so empty wallets can be deleted. - reuseWallets map[walletID]bool - mu syncutils.RWMutex - - lastWalletID atomic.Int64 -} - -// NewWallets creates and returns a new Wallets container. -func NewWallets() *Wallets { - return &Wallets{ - wallets: make(map[walletID]*Wallet), - faucetWallets: make([]walletID, 0), - reuseWallets: make(map[walletID]bool), - lastWalletID: *atomic.NewInt64(-1), - } -} - -// NewWallet adds a new wallet to Wallets and returns the created wallet. -func (w *Wallets) NewWallet(walletType WalletType) *Wallet { - wallet := NewWallet(walletType) - wallet.ID = walletID(w.lastWalletID.Add(1)) - - w.addWallet(wallet) - if walletType == Reuse { - w.addReuseWallet(wallet) - } - - return wallet -} - -// GetWallet returns the wallet with the specified ID. -func (w *Wallets) GetWallet(walletID walletID) *Wallet { - return w.wallets[walletID] -} - -// GetNextWallet get next non-empty wallet based on provided type. -func (w *Wallets) GetNextWallet(walletType WalletType, minOutputsLeft int) (*Wallet, error) { - w.mu.Lock() - defer w.mu.Unlock() - - switch walletType { - case Fresh: - if !w.IsFaucetWalletAvailable() { - return nil, ierrors.New("no faucet wallets available, need to request more funds") - } - - wallet := w.wallets[w.faucetWallets[0]] - if wallet.IsEmpty() { - return nil, ierrors.New("wallet is empty, need to request more funds") - } - - return wallet, nil - case Reuse: - for id, ready := range w.reuseWallets { - wal := w.wallets[id] - if wal.UnspentOutputsLeft() > minOutputsLeft { - // if are solid - - return wal, nil - } - // no outputs will be ever added to this wallet, so it can be deleted - if wal.IsEmpty() && ready { - w.removeReuseWallet(id) - } - } - - return nil, ierrors.New("no reuse wallets available") - } - - return nil, ierrors.New("wallet type not supported for ordered usage, use GetWallet by ID instead") -} - -func (w *Wallets) UnspentOutputsLeft(walletType WalletType) int { - w.mu.RLock() - defer w.mu.RUnlock() - outputsLeft := 0 - - switch walletType { - case Fresh: - for _, wID := range w.faucetWallets { - outputsLeft += w.wallets[wID].UnspentOutputsLeft() - } - case Reuse: - for wID := range w.reuseWallets { - outputsLeft += w.wallets[wID].UnspentOutputsLeft() - } - } - - return outputsLeft -} - -// addWallet stores newly created wallet. -func (w *Wallets) addWallet(wallet *Wallet) { - w.mu.Lock() - defer w.mu.Unlock() - - w.wallets[wallet.ID] = wallet -} - -// addReuseWallet stores newly created wallet. -func (w *Wallets) addReuseWallet(wallet *Wallet) { - w.mu.Lock() - defer w.mu.Unlock() - - w.reuseWallets[wallet.ID] = false -} - -// GetUnspentOutput gets first found unspent output for a given walletType. -func (w *Wallets) GetUnspentOutput(wallet *Wallet) *models.Output { - if wallet == nil { - return nil - } - - return wallet.GetUnspentOutput() -} - -// IsFaucetWalletAvailable checks if there is any faucet wallet left. -func (w *Wallets) IsFaucetWalletAvailable() bool { - return len(w.faucetWallets) > 0 -} - -// freshWallet returns the first non-empty wallet from the faucetWallets queue. If current wallet is empty, -// it is removed and the next enqueued one is returned. -func (w *Wallets) freshWallet() (*Wallet, error) { - wallet, err := w.GetNextWallet(Fresh, 1) - if err != nil { - w.removeFreshWallet() - wallet, err = w.GetNextWallet(Fresh, 1) - if err != nil { - return nil, err - } - } - - return wallet, nil -} - -// reuseWallet returns the first non-empty wallet from the reuseWallets queue. If current wallet is empty, -// it is removed and the next enqueued one is returned. -func (w *Wallets) reuseWallet(outputsNeeded int) *Wallet { - wallet, err := w.GetNextWallet(Reuse, outputsNeeded) - if err != nil { - return nil - } - - return wallet -} - -// removeWallet removes wallet, for Fresh wallet it will be the first wallet in a queue. -func (w *Wallets) removeFreshWallet() { - w.mu.Lock() - defer w.mu.Unlock() - - if w.IsFaucetWalletAvailable() { - removedID := w.faucetWallets[0] - w.faucetWallets = w.faucetWallets[1:] - delete(w.wallets, removedID) - } -} - -// removeWallet removes wallet, for Fresh wallet it will be the first wallet in a queue. -func (w *Wallets) removeReuseWallet(walletID walletID) { - if _, ok := w.reuseWallets[walletID]; ok { - delete(w.reuseWallets, walletID) - delete(w.wallets, walletID) - } -} - -// SetWalletReady makes wallet ready to use, Fresh wallet is added to freshWallets queue. -func (w *Wallets) SetWalletReady(wallet *Wallet) { - w.mu.Lock() - defer w.mu.Unlock() - - if wallet.IsEmpty() { - return - } - wType := wallet.walletType - switch wType { - case Fresh: - w.faucetWallets = append(w.faucetWallets, wallet.ID) - case Reuse: - w.reuseWallets[wallet.ID] = true - } -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/tools/evil-spammer/go.mod b/tools/evil-spammer/go.mod deleted file mode 100644 index a117ec9c8..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-20231102135205-0f3d1e75042a - github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231102135205-0f3d1e75042a - github.com/iotaledger/hive.go/ds v0.0.0-20231102135205-0f3d1e75042a - github.com/iotaledger/hive.go/ierrors v0.0.0-20231102135205-0f3d1e75042a - github.com/iotaledger/hive.go/lo v0.0.0-20231102135205-0f3d1e75042a - github.com/iotaledger/hive.go/logger v0.0.0-20231102135205-0f3d1e75042a - github.com/iotaledger/hive.go/runtime v0.0.0-20231102135205-0f3d1e75042a - 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-20231019174124-aa2290512bcd - 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-20231102135205-0f3d1e75042a // indirect - github.com/iotaledger/hive.go/constraints v0.0.0-20231102135205-0f3d1e75042a // indirect - github.com/iotaledger/hive.go/crypto v0.0.0-20231102135205-0f3d1e75042a // indirect - github.com/iotaledger/hive.go/kvstore v0.0.0-20231102135205-0f3d1e75042a // indirect - github.com/iotaledger/hive.go/log v0.0.0-20231102135205-0f3d1e75042a // indirect - github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231102135205-0f3d1e75042a // indirect - github.com/iotaledger/hive.go/stringify v0.0.0-20231102135205-0f3d1e75042a // 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.30.0 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.19 // 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.11.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-20230905200255-921286631fa9 // 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 fc214a300..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-20231102135205-0f3d1e75042a h1:ORysxrc6QCG/uX0eHPmrl00isL+K37Gi7CPXWkWvKpA= -github.com/iotaledger/hive.go/ads v0.0.0-20231102135205-0f3d1e75042a/go.mod h1:IFh0gDfeMgZtfCo+5afK59IDR4xXh+cTR9YtLnZPcbY= -github.com/iotaledger/hive.go/app v0.0.0-20231102135205-0f3d1e75042a h1:sNID0417k8UPZAHZjzx9cfPdDLD1J3EV89nWwRXkKA4= -github.com/iotaledger/hive.go/app v0.0.0-20231102135205-0f3d1e75042a/go.mod h1:8ZbIKR84oQd/3iQ5eeT7xpudO9/ytzXP7veIYnk7Orc= -github.com/iotaledger/hive.go/constraints v0.0.0-20231102135205-0f3d1e75042a h1:kB5JFbOsZj/pBIcqnIMEnwk9XRBXZC6bEo08M6kFPaY= -github.com/iotaledger/hive.go/constraints v0.0.0-20231102135205-0f3d1e75042a/go.mod h1:dOBOM2s4se3HcWefPe8sQLUalGXJ8yVXw58oK8jke3s= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231102135205-0f3d1e75042a h1:shooidIGLQm8wt7hzcx2hWXuC3ce4XLjIqB920R1V4U= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231102135205-0f3d1e75042a/go.mod h1:Mc+ACqBGPxrPMIPUBOm6/HL0J6m0iVMwjtIEKW3uow8= -github.com/iotaledger/hive.go/crypto v0.0.0-20231102135205-0f3d1e75042a h1:B1/zDZJDnBgCh+hwxzHyjGrUM1MTLumHwKZei+5Aehg= -github.com/iotaledger/hive.go/crypto v0.0.0-20231102135205-0f3d1e75042a/go.mod h1:h3o6okvMSEK3KOX6pOp3yq1h9ohTkTfo6X8MzEadeb0= -github.com/iotaledger/hive.go/ds v0.0.0-20231102135205-0f3d1e75042a h1:hAsljqcUaL2ywSnztVBoaNh0cJwqaqNfPfTrUSLT9xo= -github.com/iotaledger/hive.go/ds v0.0.0-20231102135205-0f3d1e75042a/go.mod h1:3XkUSKfHaVxGbT0XAvjNlVYqPzhfLTGhDtdNA5UBPco= -github.com/iotaledger/hive.go/ierrors v0.0.0-20231102135205-0f3d1e75042a h1:c3XbjBDMiqo7jilZzZd+kmWrYvzufKmbHUOcOE3NpdI= -github.com/iotaledger/hive.go/ierrors v0.0.0-20231102135205-0f3d1e75042a/go.mod h1:HcE8B5lP96enc/OALTb2/rIIi+yOLouRoHOKRclKmC8= -github.com/iotaledger/hive.go/kvstore v0.0.0-20231102135205-0f3d1e75042a h1:2RZenapa2UI/Vj4vLNIlUgFUN1jJICV2XsZiIo9jQT4= -github.com/iotaledger/hive.go/kvstore v0.0.0-20231102135205-0f3d1e75042a/go.mod h1:O/U3jtiUDeqqM0MZQFu2UPqS9fUm0C5hNISxlmg/thE= -github.com/iotaledger/hive.go/lo v0.0.0-20231102135205-0f3d1e75042a h1:cHjCgpSdnrOCc69YPyntVVO+dFmVQh9kDj+rVpoJNlE= -github.com/iotaledger/hive.go/lo v0.0.0-20231102135205-0f3d1e75042a/go.mod h1:s4kzx9QY1MVWHJralj+3q5kI0eARtrJhphYD/iBbPfo= -github.com/iotaledger/hive.go/log v0.0.0-20231102135205-0f3d1e75042a h1:PU+z5uVPUEVzmQf0JRLxJjnfMzdtz6mlnToECVzdVFQ= -github.com/iotaledger/hive.go/log v0.0.0-20231102135205-0f3d1e75042a/go.mod h1:JvokzmpmFZPDskMlUqqjgHtD8usVJU4nAY/TNMGge8M= -github.com/iotaledger/hive.go/logger v0.0.0-20231102135205-0f3d1e75042a h1:8RigRlTTTBsT5Ik/Jz6VcLWgIzbx9HynzgnRq9/VhJE= -github.com/iotaledger/hive.go/logger v0.0.0-20231102135205-0f3d1e75042a/go.mod h1:aBfAfIB2GO/IblhYt5ipCbyeL9bXSNeAwtYVA3hZaHg= -github.com/iotaledger/hive.go/runtime v0.0.0-20231102135205-0f3d1e75042a h1:vYyd5E4j02kwbAhx+nivcc7DPpmI46S72GBGGGpalSs= -github.com/iotaledger/hive.go/runtime v0.0.0-20231102135205-0f3d1e75042a/go.mod h1:jRw8yFipiPaqmTPHh7hTcxAP9u6pjRGpByS3REJKkbY= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231102135205-0f3d1e75042a h1:/xwSejpRNjjv20gX9kQZ1Pgy+m6kFHcUMt1u/f+kc1Y= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231102135205-0f3d1e75042a/go.mod h1:SdK26z8/VhWtxaqCuQrufm80SELgowQPmu9T/8eUQ8g= -github.com/iotaledger/hive.go/stringify v0.0.0-20231102135205-0f3d1e75042a h1:DNcRsEdvfnrfwh9Mb19lHfsz5Izf9BiMqJILYYW0SvU= -github.com/iotaledger/hive.go/stringify v0.0.0-20231102135205-0f3d1e75042a/go.mod h1:FTo/UWzNYgnQ082GI9QVM9HFDERqf9rw9RivNpqrnTs= -github.com/iotaledger/iota.go/v4 v4.0.0-20231019174124-aa2290512bcd h1:FKiEzy3gOyWDGXs6mTMS9qFHGXSxH97N5w+08ljbEtU= -github.com/iotaledger/iota.go/v4 v4.0.0-20231019174124-aa2290512bcd/go.mod h1:XFwcDfAF0hv6wB/trCsyDmQyPLjEZ60yTrllJ2bprN8= -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.30.0 h1:9EZwFtJPFBcs/yJTnP90TpN1hgrT/EsFfM+OZuwV87U= -github.com/libp2p/go-libp2p v0.30.0/go.mod h1:nr2g5V7lfftwgiJ78/HrID+pwvayLyqKCEirT2Y3Byg= -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.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/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.11.0 h1:XqGyJ8ufbCE0HmTDwx2kPdsrQ36AGPZNZX6s6xfJH10= -github.com/multiformats/go-multiaddr v0.11.0/go.mod h1:gWUm0QLR4thQ6+ZF6SXUw8YjtwQSPapICM+NmCkxHSM= -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-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= -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 e09b01da5..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.TxState -} - -// GetTransaction gets the transaction. -func (c *WebClient) GetTransaction(txID iotago.TransactionID) (tx *iotago.SignedTransaction, err error) { - resp, err := c.client.TransactionIncludedBlock(context.Background(), txID) - if err != nil { - return - } - - modelBlk, err := model.BlockFromBlock(resp) - if err != nil { - return - } - - tx, _ = modelBlk.SignedTransaction() - - return tx, nil -} - -func (c *WebClient) GetBlockIssuance(slotIndex ...iotago.SlotIndex) (resp *apimodels.IssuanceBlockHeaderResponse, err error) { - return c.client.BlockIssuance(context.Background(), slotIndex...) -} - -func (c *WebClient) GetCongestion(accountID iotago.AccountID) (resp *apimodels.CongestionResponse, err error) { - return c.client.Congestion(context.Background(), accountID) -} diff --git a/tools/evil-spammer/models/output.go b/tools/evil-spammer/models/output.go deleted file mode 100644 index 539da3a19..000000000 --- a/tools/evil-spammer/models/output.go +++ /dev/null @@ -1,88 +0,0 @@ -package models - -import ( - "crypto/ed25519" - - "github.com/iotaledger/iota-core/pkg/blockhandler" - iotago "github.com/iotaledger/iota.go/v4" - "github.com/iotaledger/iota.go/v4/nodeclient/apimodels" -) - -// Input contains details of an input. -type Input struct { - OutputID iotago.OutputID - Address iotago.Address -} - -// Output contains details of an output ID. -type Output struct { - OutputID iotago.OutputID - Address iotago.Address - Index uint64 - Balance iotago.BaseToken - - OutputStruct iotago.Output -} - -// Outputs is a list of Output. -type Outputs []*Output - -type AccountStatus uint8 - -const ( - AccountPending AccountStatus = iota - AccountReady -) - -type AccountData struct { - Alias string - Status AccountStatus - Account blockhandler.Account - OutputID iotago.OutputID - Index uint64 -} - -type AccountState struct { - Alias string `serix:"0,lengthPrefixType=uint8"` - AccountID iotago.AccountID `serix:"2"` - PrivateKey ed25519.PrivateKey `serix:"3,lengthPrefixType=uint8"` - OutputID iotago.OutputID `serix:"4"` - Index uint64 `serix:"5"` -} - -func AccountStateFromAccountData(acc *AccountData) *AccountState { - return &AccountState{ - Alias: acc.Alias, - AccountID: acc.Account.ID(), - PrivateKey: acc.Account.PrivateKey(), - OutputID: acc.OutputID, - Index: acc.Index, - } -} - -func (a *AccountState) ToAccountData() *AccountData { - return &AccountData{ - Alias: a.Alias, - Account: blockhandler.NewEd25519Account(a.AccountID, a.PrivateKey), - OutputID: a.OutputID, - Index: a.Index, - } -} - -type PayloadIssuanceData struct { - Payload iotago.Payload - CongestionResponse *apimodels.CongestionResponse -} - -type AllotmentStrategy uint8 - -const ( - AllotmentStrategyNone AllotmentStrategy = iota - AllotmentStrategyMinCost - AllotmentStrategyAll -) - -type IssuancePaymentStrategy struct { - AllotmentStrategy AllotmentStrategy - IssuerAlias string -} diff --git a/tools/evil-spammer/parse.go b/tools/evil-spammer/parse.go deleted file mode 100644 index b62b85292..000000000 --- a/tools/evil-spammer/parse.go +++ /dev/null @@ -1,494 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "os" - "strconv" - "strings" - "time" - - "github.com/iotaledger/hive.go/ierrors" - "github.com/iotaledger/iota-core/tools/evil-spammer/accountwallet" - "github.com/iotaledger/iota-core/tools/evil-spammer/evilwallet" -) - -func parseFlags() (help bool) { - if len(os.Args) <= 1 { - return true - } - script := os.Args[1] - - Script = script - log.Infof("script %s", Script) - - switch Script { - case "basic": - parseBasicSpamFlags() - case "accounts": - // pass subcommands - subcommands := make([]string, 0) - if len(os.Args) > 2 { - subcommands = os.Args[2:] - } - splitedCmds := readSubcommandsAndFlagSets(subcommands) - accountsSubcommandsFlags = parseAccountTestFlags(splitedCmds) - - case "quick": - parseQuickTestFlags() - // case SpammerTypeCommitments: - // parseCommitmentsSpamFlags() - } - if Script == "help" || Script == "-h" || Script == "--help" { - return true - } - - return -} - -func parseOptionFlagSet(flagSet *flag.FlagSet, args ...[]string) { - commands := os.Args[2:] - if len(args) > 0 { - commands = args[0] - } - err := flagSet.Parse(commands) - if err != nil { - log.Errorf("Cannot parse first `script` parameter") - return - } -} - -func parseBasicSpamFlags() { - urls := optionFlagSet.String("urls", "", "API urls for clients used in test separated with commas") - spamTypes := optionFlagSet.String("spammer", "", "Spammers used during test. Format: strings separated with comma, available options: 'blk' - block,"+ - " 'tx' - transaction, 'ds' - double spends spammers, 'nds' - n-spends spammer, 'custom' - spams with provided scenario") - rate := optionFlagSet.String("rate", "", "Spamming rate for provided 'spammer'. Format: numbers separated with comma, e.g. 10,100,1 if three spammers were provided for 'spammer' parameter.") - duration := optionFlagSet.String("duration", "", "Spam duration. Cannot be combined with flag 'blkNum'. Format: separated by commas list of decimal numbers, each with optional fraction and a unit suffix, such as '300ms', '-1.5h' or '2h45m'.\n Valid time units are 'ns', 'us', 'ms', 's', 'm', 'h'.") - blkNum := optionFlagSet.String("blkNum", "", "Spam duration in seconds. Cannot be combined with flag 'duration'. Format: numbers separated with comma, e.g. 10,100,1 if three spammers were provided for 'spammer' parameter.") - timeunit := optionFlagSet.Duration("tu", customSpamParams.TimeUnit, "Time unit for the spamming rate. Format: decimal numbers, each with optional fraction and a unit suffix, such as '300ms', '-1.5h' or '2h45m'.\n Valid time units are 'ns', 'us', 'ms', 's', 'm', 'h'.") - delayBetweenConflicts := optionFlagSet.Duration("dbc", customSpamParams.DelayBetweenConflicts, "delayBetweenConflicts - Time delay between conflicts in double spend spamming") - scenario := optionFlagSet.String("scenario", "", "Name of the EvilBatch that should be used for the spam. By default uses Scenario1. Possible scenarios can be found in evilwallet/customscenarion.go.") - deepSpam := optionFlagSet.Bool("deep", customSpamParams.DeepSpam, "Enable the deep spam, by reusing outputs created during the spam.") - nSpend := optionFlagSet.Int("nSpend", customSpamParams.NSpend, "Number of outputs to be spent in n-spends spammer for the spammer type needs to be set to 'ds'. Default value is 2 for double-spend.") - account := optionFlagSet.String("account", "", "Account alias to be used for the spam. Account should be created first with accounts tool.") - - parseOptionFlagSet(optionFlagSet) - - if *urls != "" { - parsedUrls := parseCommaSepString(*urls) - quickTestParams.ClientURLs = parsedUrls - customSpamParams.ClientURLs = parsedUrls - } - if *spamTypes != "" { - parsedSpamTypes := parseCommaSepString(*spamTypes) - customSpamParams.SpamTypes = parsedSpamTypes - } - if *rate != "" { - parsedRates := parseCommaSepInt(*rate) - customSpamParams.Rates = parsedRates - } - if *duration != "" { - parsedDurations := parseDurations(*duration) - customSpamParams.Durations = parsedDurations - } - if *blkNum != "" { - parsedBlkNums := parseCommaSepInt(*blkNum) - customSpamParams.BlkToBeSent = parsedBlkNums - } - if *scenario != "" { - conflictBatch, ok := evilwallet.GetScenario(*scenario) - if ok { - customSpamParams.Scenario = conflictBatch - } - } - - customSpamParams.NSpend = *nSpend - customSpamParams.DeepSpam = *deepSpam - customSpamParams.TimeUnit = *timeunit - customSpamParams.DelayBetweenConflicts = *delayBetweenConflicts - if *account != "" { - customSpamParams.AccountAlias = *account - } - - // fill in unused parameter: blkNum or duration with zeros - if *duration == "" && *blkNum != "" { - customSpamParams.Durations = make([]time.Duration, len(customSpamParams.BlkToBeSent)) - } - if *blkNum == "" && *duration != "" { - customSpamParams.BlkToBeSent = make([]int, len(customSpamParams.Durations)) - } -} - -func parseQuickTestFlags() { - urls := optionFlagSet.String("urls", "", "API urls for clients used in test separated with commas") - rate := optionFlagSet.Int("rate", quickTestParams.Rate, "The spamming rate") - duration := optionFlagSet.Duration("duration", quickTestParams.Duration, "Duration of the spam. Format: decimal numbers, each with optional fraction and a unit suffix, such as '300ms', '-1.5h' or '2h45m'.\n Valid time units are 'ns', 'us', 'ms', 's', 'm', 'h'.") - timeunit := optionFlagSet.Duration("tu", quickTestParams.TimeUnit, "Time unit for the spamming rate. Format: decimal numbers, each with optional fraction and a unit suffix, such as '300ms', '-1.5h' or '2h45m'.\n Valid time units are 'ns', 'us', 'ms', 's', 'm', 'h'.") - delayBetweenConflicts := optionFlagSet.Duration("dbc", quickTestParams.DelayBetweenConflicts, "delayBetweenConflicts - Time delay between conflicts in double spend spamming") - verifyLedger := optionFlagSet.Bool("verify", quickTestParams.VerifyLedger, "Set to true if verify ledger script should be run at the end of the test") - - parseOptionFlagSet(optionFlagSet) - - if *urls != "" { - parsedUrls := parseCommaSepString(*urls) - quickTestParams.ClientURLs = parsedUrls - } - quickTestParams.Rate = *rate - quickTestParams.Duration = *duration - quickTestParams.TimeUnit = *timeunit - quickTestParams.DelayBetweenConflicts = *delayBetweenConflicts - quickTestParams.VerifyLedger = *verifyLedger -} - -// readSubcommandsAndFlagSets splits the subcommands on multiple flag sets. -func readSubcommandsAndFlagSets(subcommands []string) [][]string { - prevSplitIndex := 0 - subcommandsSplit := make([][]string, 0) - if len(subcommands) == 0 { - return nil - } - - // mainCmd := make([]string, 0) - for index := 0; index < len(subcommands); index++ { - validCommand := accountwallet.AvailableCommands(subcommands[index]) - - if !strings.HasPrefix(subcommands[index], "--") && validCommand { - if index != 0 { - subcommandsSplit = append(subcommandsSplit, subcommands[prevSplitIndex:index]) - } - prevSplitIndex = index - } - } - subcommandsSplit = append(subcommandsSplit, subcommands[prevSplitIndex:]) - - return subcommandsSplit -} - -func parseAccountTestFlags(splitedCmds [][]string) []accountwallet.AccountSubcommands { - parsedCmds := make([]accountwallet.AccountSubcommands, 0) - - for _, cmds := range splitedCmds { - switch cmds[0] { - case "create": - createAccountParams, err := parseCreateAccountFlags(cmds[1:]) - if err != nil { - continue - } - - parsedCmds = append(parsedCmds, createAccountParams) - case "convert": - convertAccountParams, err := parseConvertAccountFlags(cmds[1:]) - if err != nil { - continue - } - - parsedCmds = append(parsedCmds, convertAccountParams) - case "destroy": - destroyAccountParams, err := parseDestroyAccountFlags(cmds[1:]) - if err != nil { - continue - } - - parsedCmds = append(parsedCmds, destroyAccountParams) - case "allot": - allotAccountParams, err := parseAllotAccountFlags(cmds[1:]) - if err != nil { - continue - } - - parsedCmds = append(parsedCmds, allotAccountParams) - case "delegate": - delegatingAccountParams, err := parseDelegateAccountFlags(cmds[1:]) - if err != nil { - continue - } - - parsedCmds = append(parsedCmds, delegatingAccountParams) - case "stake": - stakingAccountParams, err := parseStakeAccountFlags(cmds[1:]) - if err != nil { - continue - } - - parsedCmds = append(parsedCmds, stakingAccountParams) - case "update": - updateAccountParams, err := parseUpdateAccountFlags(cmds[1:]) - if err != nil { - continue - } - - parsedCmds = append(parsedCmds, updateAccountParams) - case "list": - parsedCmds = append(parsedCmds, &accountwallet.NoAccountParams{ - Operation: accountwallet.OperationListAccounts, - }) - default: - accountUsage() - return nil - } - } - - return parsedCmds -} - -func accountUsage() { - fmt.Println("Usage for accounts [COMMAND] [FLAGS], multiple commands can be chained together.") - fmt.Printf("COMMAND: %s\n", accountwallet.CmdNameCreateAccount) - parseCreateAccountFlags(nil) - - fmt.Printf("COMMAND: %s\n", accountwallet.CmdNameConvertAccount) - parseConvertAccountFlags(nil) - - fmt.Printf("COMMAND: %s\n", accountwallet.CmdNameDestroyAccount) - parseDestroyAccountFlags(nil) - - fmt.Printf("COMMAND: %s\n", accountwallet.CmdNameAllotAccount) - parseAllotAccountFlags(nil) - - fmt.Printf("COMMAND: %s\n", accountwallet.CmdNameDelegateAccount) - parseDelegateAccountFlags(nil) - - fmt.Printf("COMMAND: %s\n", accountwallet.CmdNameStakeAccount) - parseStakeAccountFlags(nil) -} - -func parseCreateAccountFlags(subcommands []string) (*accountwallet.CreateAccountParams, error) { - flagSet := flag.NewFlagSet("create", flag.ExitOnError) - alias := flagSet.String("alias", "", "The alias name of new created account") - amount := flagSet.Int64("amount", 1000, "The amount to be transfered to the new account") - noBIF := flagSet.Bool("noBIF", false, "Create account without Block Issuer Feature") - implicit := flagSet.Bool("implicit", false, "Create an implicit account") - - if subcommands == nil { - flagSet.Usage() - - return nil, ierrors.Errorf("no subcommands") - } - - log.Infof("Parsing create account flags, subcommands: %v", subcommands) - err := flagSet.Parse(subcommands) - if err != nil { - log.Errorf("Cannot parse first `script` parameter") - - return nil, ierrors.Wrap(err, "cannot parse first `script` parameter") - } - - return &accountwallet.CreateAccountParams{ - Alias: *alias, - Amount: uint64(*amount), - NoBIF: *noBIF, - Implicit: *implicit, - }, nil -} - -func parseConvertAccountFlags(subcommands []string) (*accountwallet.ConvertAccountParams, error) { - flagSet := flag.NewFlagSet("convert", flag.ExitOnError) - alias := flagSet.String("alias", "", "The implicit account to be converted to full account") - - if subcommands == nil { - flagSet.Usage() - - return nil, ierrors.Errorf("no subcommands") - } - - log.Infof("Parsing convert account flags, subcommands: %v", subcommands) - err := flagSet.Parse(subcommands) - if err != nil { - log.Errorf("Cannot parse first `script` parameter") - - return nil, ierrors.Wrap(err, "cannot parse first `script` parameter") - } - - return &accountwallet.ConvertAccountParams{ - AccountAlias: *alias, - }, nil -} - -func parseDestroyAccountFlags(subcommands []string) (*accountwallet.DestroyAccountParams, error) { - flagSet := flag.NewFlagSet("destroy", flag.ExitOnError) - alias := flagSet.String("alias", "", "The alias name of the account to be destroyed") - expirySlot := flagSet.Int64("expirySlot", 0, "The expiry slot of the account to be destroyed") - - if subcommands == nil { - flagSet.Usage() - - return nil, ierrors.Errorf("no subcommands") - } - - log.Infof("Parsing destroy account flags, subcommands: %v", subcommands) - err := flagSet.Parse(subcommands) - if err != nil { - log.Errorf("Cannot parse first `script` parameter") - - return nil, ierrors.Wrap(err, "cannot parse first `script` parameter") - } - - return &accountwallet.DestroyAccountParams{ - AccountAlias: *alias, - ExpirySlot: uint64(*expirySlot), - }, nil -} - -func parseAllotAccountFlags(subcommands []string) (*accountwallet.AllotAccountParams, error) { - flagSet := flag.NewFlagSet("allot", flag.ExitOnError) - from := flagSet.String("from", "", "The alias name of the account to allot mana from") - to := flagSet.String("to", "", "The alias of the account to allot mana to") - amount := flagSet.Int64("amount", 1000, "The amount of mana to allot") - - if subcommands == nil { - flagSet.Usage() - - return nil, ierrors.Errorf("no subcommands") - } - - log.Infof("Parsing allot account flags, subcommands: %v", subcommands) - err := flagSet.Parse(subcommands) - if err != nil { - log.Errorf("Cannot parse first `script` parameter") - - return nil, ierrors.Wrap(err, "cannot parse first `script` parameter") - } - - return &accountwallet.AllotAccountParams{ - From: *from, - To: *to, - Amount: uint64(*amount), - }, nil -} - -func parseStakeAccountFlags(subcommands []string) (*accountwallet.StakeAccountParams, error) { - flagSet := flag.NewFlagSet("stake", flag.ExitOnError) - alias := flagSet.String("alias", "", "The alias name of the account to stake") - amount := flagSet.Int64("amount", 100, "The amount of tokens to stake") - fixedCost := flagSet.Int64("fixedCost", 0, "The fixed cost of the account to stake") - startEpoch := flagSet.Int64("startEpoch", 0, "The start epoch of the account to stake") - endEpoch := flagSet.Int64("endEpoch", 0, "The end epoch of the account to stake") - - if subcommands == nil { - flagSet.Usage() - - return nil, ierrors.Errorf("no subcommands") - } - - log.Infof("Parsing staking account flags, subcommands: %v", subcommands) - err := flagSet.Parse(subcommands) - if err != nil { - log.Errorf("Cannot parse first `script` parameter") - - return nil, ierrors.Wrap(err, "cannot parse first `script` parameter") - } - - return &accountwallet.StakeAccountParams{ - Alias: *alias, - Amount: uint64(*amount), - FixedCost: uint64(*fixedCost), - StartEpoch: uint64(*startEpoch), - EndEpoch: uint64(*endEpoch), - }, nil -} - -func parseDelegateAccountFlags(subcommands []string) (*accountwallet.DelegateAccountParams, error) { - flagSet := flag.NewFlagSet("delegate", flag.ExitOnError) - from := flagSet.String("from", "", "The alias name of the account to delegate mana from") - to := flagSet.String("to", "", "The alias of the account to delegate mana to") - amount := flagSet.Int64("amount", 100, "The amount of mana to delegate") - - if subcommands == nil { - flagSet.Usage() - - return nil, ierrors.Errorf("no subcommands") - } - - log.Infof("Parsing delegate account flags, subcommands: %v", subcommands) - err := flagSet.Parse(subcommands) - if err != nil { - log.Errorf("Cannot parse first `script` parameter") - - return nil, ierrors.Wrap(err, "cannot parse first `script` parameter") - } - - return &accountwallet.DelegateAccountParams{ - From: *from, - To: *to, - Amount: uint64(*amount), - }, nil -} - -func parseUpdateAccountFlags(subcommands []string) (*accountwallet.UpdateAccountParams, error) { - flagSet := flag.NewFlagSet("update", flag.ExitOnError) - alias := flagSet.String("alias", "", "The alias name of the account to update") - bik := flagSet.String("bik", "", "The block issuer key (in hex) to add") - amount := flagSet.Int64("addamount", 100, "The amount of token to add") - mana := flagSet.Int64("addmana", 100, "The amount of mana to add") - expirySlot := flagSet.Int64("expirySlot", 0, "Update the expiry slot of the account") - - if subcommands == nil { - flagSet.Usage() - - return nil, ierrors.Errorf("no subcommands") - } - - log.Infof("Parsing update account flags, subcommands: %v", subcommands) - err := flagSet.Parse(subcommands) - if err != nil { - log.Errorf("Cannot parse first `script` parameter") - - return nil, ierrors.Wrap(err, "cannot parse first `script` parameter") - } - - return &accountwallet.UpdateAccountParams{ - Alias: *alias, - BlockIssuerKey: *bik, - Amount: uint64(*amount), - Mana: uint64(*mana), - ExpirySlot: uint64(*expirySlot), - }, nil -} - -// func parseCommitmentsSpamFlags() { -// commitmentType := optionFlagSet.String("type", commitmentsSpamParams.CommitmentType, "Type of commitment spam. Possible values: 'latest' - valid commitment spam, 'random' - completely new, invalid cahin, 'fork' - forked chain, combine with 'forkAfter' parameter.") -// rate := optionFlagSet.Int("rate", commitmentsSpamParams.Rate, "Commitment spam rate") -// duration := optionFlagSet.Duration("duration", commitmentsSpamParams.Duration, "Duration of the spam. Format: decimal numbers, each with optional fraction and a unit suffix, such as '300ms', '-1.5h' or '2h45m'.\n Valid time units are 'ns', 'us', 'ms', 's', 'm', 'h'.") -// timeUnit := optionFlagSet.Duration("tu", commitmentsSpamParams.TimeUnit, "Time unit for the spamming rate. Format: decimal numbers, each with optional fraction and a unit suffix, such as '300ms', '-1.5h' or '2h45m'.\n Valid time units are 'ns', 'us', 'ms', 's', 'm', 'h'.") -// networkAlias := optionFlagSet.String("network", commitmentsSpamParams.NetworkAlias, "Network alias for the test. Check your keys-config.json file for possible values.") -// identityAlias := optionFlagSet.String("spammerAlias", commitmentsSpamParams.SpammerAlias, "Identity alias for the node identity and its private keys that will be used to spam. Check your keys-config.json file for possible values.") -// validAlias := optionFlagSet.String("validAlias", commitmentsSpamParams.ValidAlias, "Identity alias for the honest node and its private keys, will be used to request valid commitment and block data. Check your keys-config.json file for possible values.") -// forkAfter := optionFlagSet.Int("forkAfter", commitmentsSpamParams.Rate, "Indicates how many slots after spammer startup should fork be placed in the created commitment chain. Works only for 'fork' commitment spam type.") - -// parseOptionFlagSet(optionFlagSet) - -// commitmentsSpamParams.CommitmentType = *commitmentType -// commitmentsSpamParams.Rate = *rate -// commitmentsSpamParams.Duration = *duration -// commitmentsSpamParams.TimeUnit = *timeUnit -// commitmentsSpamParams.NetworkAlias = *networkAlias -// commitmentsSpamParams.SpammerAlias = *identityAlias -// commitmentsSpamParams.ValidAlias = *validAlias -// commitmentsSpamParams.ForkAfter = *forkAfter -// } - -func parseCommaSepString(urls string) []string { - split := strings.Split(urls, ",") - - return split -} - -func parseCommaSepInt(nums string) []int { - split := strings.Split(nums, ",") - parsed := make([]int, len(split)) - for i, num := range split { - parsed[i], _ = strconv.Atoi(num) - } - - return parsed -} - -func parseDurations(durations string) []time.Duration { - split := strings.Split(durations, ",") - parsed := make([]time.Duration, len(split)) - for i, dur := range split { - parsed[i], _ = time.ParseDuration(dur) - } - - return parsed -} diff --git a/tools/evil-spammer/programs/commitments.go b/tools/evil-spammer/programs/commitments.go deleted file mode 100644 index 5083b8552..000000000 --- a/tools/evil-spammer/programs/commitments.go +++ /dev/null @@ -1,41 +0,0 @@ -package programs - -// import ( -// "time" - -// "github.com/iotaledger/goshimmer/client/evilspammer" -// "github.com/iotaledger/goshimmer/tools/evil-spammer/identity" -// ) - -// type CommitmentsSpamParams struct { -// CommitmentType string -// Rate int -// Duration time.Duration -// TimeUnit time.Duration -// NetworkAlias string -// SpammerAlias string -// ValidAlias string -// ForkAfter int // optional, will be used only with CommitmentType = "fork" -// } - -// func CommitmentsSpam(params *CommitmentsSpamParams) { -// identity.LoadConfig() -// SpamCommitments(*params) -// } - -// func SpamCommitments(params CommitmentsSpamParams) { -// privateKey, urlAPI := identity.LoadIdentity(params.NetworkAlias, params.SpammerAlias) -// _, validAPI := identity.LoadIdentity(params.NetworkAlias, params.ValidAlias) -// options := []evilspammer.Options{ -// evilspammer.WithClientURL(urlAPI), -// evilspammer.WithValidClientURL(validAPI), -// evilspammer.WithSpamRate(params.Rate, params.TimeUnit), -// evilspammer.WithSpamDuration(params.Duration), -// evilspammer.WithSpammingFunc(evilspammer.CommitmentsSpammingFunction), -// evilspammer.WithIdentity(params.SpammerAlias, privateKey), -// evilspammer.WithCommitmentType(params.CommitmentType), -// evilspammer.WithForkAfter(params.ForkAfter), -// } -// spammer := evilspammer.NewSpammer(options...) -// spammer.Spam() -// } diff --git a/tools/evil-spammer/programs/params.go b/tools/evil-spammer/programs/params.go deleted file mode 100644 index f0e1d2070..000000000 --- a/tools/evil-spammer/programs/params.go +++ /dev/null @@ -1,22 +0,0 @@ -package programs - -import ( - "time" - - "github.com/iotaledger/iota-core/tools/evil-spammer/evilwallet" -) - -type CustomSpamParams struct { - ClientURLs []string - SpamTypes []string - Rates []int - Durations []time.Duration - BlkToBeSent []int - TimeUnit time.Duration - DelayBetweenConflicts time.Duration - NSpend int - Scenario evilwallet.EvilBatch - DeepSpam bool - EnableRateSetter bool - AccountAlias string -} diff --git a/tools/evil-spammer/programs/quick-test.go b/tools/evil-spammer/programs/quick-test.go deleted file mode 100644 index e17725594..000000000 --- a/tools/evil-spammer/programs/quick-test.go +++ /dev/null @@ -1,68 +0,0 @@ -package programs - -import ( - "time" - - "github.com/iotaledger/iota-core/tools/evil-spammer/evilwallet" - "github.com/iotaledger/iota-core/tools/evil-spammer/spammer" -) - -type QuickTestParams struct { - ClientURLs []string - Rate int - Duration time.Duration - TimeUnit time.Duration - DelayBetweenConflicts time.Duration - VerifyLedger bool - EnableRateSetter bool -} - -// QuickTest runs short spamming periods with stable mps. -func QuickTest(params *QuickTestParams) { - evilWallet := evilwallet.NewEvilWallet(evilwallet.WithClients(params.ClientURLs...)) - counter := spammer.NewErrorCount() - log.Info("Starting quick test") - - nWallets := 2 * spammer.BigWalletsNeeded(params.Rate, params.TimeUnit, params.Duration) - - log.Info("Start preparing funds") - evilWallet.RequestFreshBigFaucetWallets(nWallets) - - // define spammers - baseOptions := []spammer.Options{ - spammer.WithSpamRate(params.Rate, params.TimeUnit), - spammer.WithSpamDuration(params.Duration), - spammer.WithErrorCounter(counter), - spammer.WithEvilWallet(evilWallet), - } - - //nolint:gocritic // we want a copy here - blkOptions := append(baseOptions, - spammer.WithSpammingFunc(spammer.DataSpammingFunction), - ) - - dsScenario := evilwallet.NewEvilScenario( - evilwallet.WithScenarioCustomConflicts(evilwallet.NSpendBatch(2)), - ) - - //nolint:gocritic // we want a copy here - dsOptions := append(baseOptions, - spammer.WithEvilScenario(dsScenario), - ) - - blkSpammer := spammer.NewSpammer(blkOptions...) - txSpammer := spammer.NewSpammer(baseOptions...) - dsSpammer := spammer.NewSpammer(dsOptions...) - - // start test - txSpammer.Spam() - time.Sleep(5 * time.Second) - - blkSpammer.Spam() - time.Sleep(5 * time.Second) - - dsSpammer.Spam() - - log.Info(counter.GetErrorsSummary()) - log.Info("Quick Test finished") -} diff --git a/tools/evil-spammer/programs/spammers.go b/tools/evil-spammer/programs/spammers.go deleted file mode 100644 index b8ca4845f..000000000 --- a/tools/evil-spammer/programs/spammers.go +++ /dev/null @@ -1,230 +0,0 @@ -package programs - -import ( - "sync" - "time" - - "github.com/iotaledger/iota-core/tools/evil-spammer/accountwallet" - "github.com/iotaledger/iota-core/tools/evil-spammer/evilwallet" - "github.com/iotaledger/iota-core/tools/evil-spammer/logger" - "github.com/iotaledger/iota-core/tools/evil-spammer/spammer" -) - -var log = logger.New("customSpam") - -func CustomSpam(params *CustomSpamParams, accWallet *accountwallet.AccountWallet) { - w := evilwallet.NewEvilWallet(evilwallet.WithClients(params.ClientURLs...), evilwallet.WithAccountsWallet(accWallet)) - wg := sync.WaitGroup{} - - // funds are requested fro all spam types except SpammerTypeBlock - fundsNeeded := false - for _, st := range params.SpamTypes { - if st != spammer.TypeBlock { - fundsNeeded = true - } - } - if fundsNeeded { - err := w.RequestFreshBigFaucetWallet() - if err != nil { - panic(err) - } - } - - for i, sType := range params.SpamTypes { - log.Infof("Start spamming with rate: %d, time unit: %s, and spamming type: %s.", params.Rates[i], params.TimeUnit.String(), sType) - - switch sType { - case spammer.TypeBlock: - wg.Add(1) - go func(i int) { - defer wg.Done() - s := SpamBlocks(w, params.Rates[i], params.TimeUnit, params.Durations[i], params.BlkToBeSent[i], params.EnableRateSetter, params.AccountAlias) - if s == nil { - return - } - s.Spam() - }(i) - case spammer.TypeTx: - wg.Add(1) - go func(i int) { - defer wg.Done() - SpamTransaction(w, params.Rates[i], params.TimeUnit, params.Durations[i], params.DeepSpam, params.EnableRateSetter, params.AccountAlias) - }(i) - case spammer.TypeDs: - wg.Add(1) - go func(i int) { - defer wg.Done() - SpamDoubleSpends(w, params.Rates[i], params.NSpend, params.TimeUnit, params.Durations[i], params.DelayBetweenConflicts, params.DeepSpam, params.EnableRateSetter, params.AccountAlias) - }(i) - case spammer.TypeCustom: - wg.Add(1) - go func(i int) { - defer wg.Done() - s := SpamNestedConflicts(w, params.Rates[i], params.TimeUnit, params.Durations[i], params.Scenario, params.DeepSpam, false, params.EnableRateSetter, params.AccountAlias) - if s == nil { - return - } - s.Spam() - }(i) - case spammer.TypeCommitments: - wg.Add(1) - go func() { - defer wg.Done() - }() - case spammer.TypeAccounts: - wg.Add(1) - go func() { - defer wg.Done() - - s := SpamAccounts(w, params.Rates[i], params.TimeUnit, params.Durations[i], params.EnableRateSetter, params.AccountAlias) - if s == nil { - return - } - s.Spam() - }() - - default: - log.Warn("Spamming type not recognized. Try one of following: tx, ds, blk, custom, commitments") - } - } - - wg.Wait() - log.Info("Basic spamming finished!") -} - -func SpamTransaction(w *evilwallet.EvilWallet, rate int, timeUnit, duration time.Duration, deepSpam, enableRateSetter bool, accountAlias string) { - if w.NumOfClient() < 1 { - log.Infof("Warning: At least one client is needed to spam.") - } - - scenarioOptions := []evilwallet.ScenarioOption{ - evilwallet.WithScenarioCustomConflicts(evilwallet.SingleTransactionBatch()), - } - if deepSpam { - outWallet := evilwallet.NewWallet(evilwallet.Reuse) - scenarioOptions = append(scenarioOptions, - evilwallet.WithScenarioDeepSpamEnabled(), - evilwallet.WithScenarioReuseOutputWallet(outWallet), - evilwallet.WithScenarioInputWalletForDeepSpam(outWallet), - ) - } - scenarioTx := evilwallet.NewEvilScenario(scenarioOptions...) - - options := []spammer.Options{ - spammer.WithSpamRate(rate, timeUnit), - spammer.WithSpamDuration(duration), - spammer.WithRateSetter(enableRateSetter), - spammer.WithEvilWallet(w), - spammer.WithEvilScenario(scenarioTx), - spammer.WithAccountAlias(accountAlias), - } - - s := spammer.NewSpammer(options...) - s.Spam() -} - -func SpamDoubleSpends(w *evilwallet.EvilWallet, rate, nSpent int, timeUnit, duration, delayBetweenConflicts time.Duration, deepSpam, enableRateSetter bool, accountAlias string) { - log.Debugf("Setting up double spend spammer with rate: %d, time unit: %s, and duration: %s.", rate, timeUnit.String(), duration.String()) - if w.NumOfClient() < 2 { - log.Infof("Warning: At least two client are needed to spam, and %d was provided", w.NumOfClient()) - } - - scenarioOptions := []evilwallet.ScenarioOption{ - evilwallet.WithScenarioCustomConflicts(evilwallet.NSpendBatch(nSpent)), - } - if deepSpam { - outWallet := evilwallet.NewWallet(evilwallet.Reuse) - scenarioOptions = append(scenarioOptions, - evilwallet.WithScenarioDeepSpamEnabled(), - evilwallet.WithScenarioReuseOutputWallet(outWallet), - evilwallet.WithScenarioInputWalletForDeepSpam(outWallet), - ) - } - scenarioDs := evilwallet.NewEvilScenario(scenarioOptions...) - options := []spammer.Options{ - spammer.WithSpamRate(rate, timeUnit), - spammer.WithSpamDuration(duration), - spammer.WithEvilWallet(w), - spammer.WithRateSetter(enableRateSetter), - spammer.WithTimeDelayForDoubleSpend(delayBetweenConflicts), - spammer.WithEvilScenario(scenarioDs), - spammer.WithAccountAlias(accountAlias), - } - - s := spammer.NewSpammer(options...) - s.Spam() -} - -func SpamNestedConflicts(w *evilwallet.EvilWallet, rate int, timeUnit, duration time.Duration, conflictBatch evilwallet.EvilBatch, deepSpam, reuseOutputs, enableRateSetter bool, accountAlias string) *spammer.Spammer { - scenarioOptions := []evilwallet.ScenarioOption{ - evilwallet.WithScenarioCustomConflicts(conflictBatch), - } - if deepSpam { - outWallet := evilwallet.NewWallet(evilwallet.Reuse) - scenarioOptions = append(scenarioOptions, - evilwallet.WithScenarioDeepSpamEnabled(), - evilwallet.WithScenarioReuseOutputWallet(outWallet), - evilwallet.WithScenarioInputWalletForDeepSpam(outWallet), - ) - } else if reuseOutputs { - outWallet := evilwallet.NewWallet(evilwallet.Reuse) - scenarioOptions = append(scenarioOptions, evilwallet.WithScenarioReuseOutputWallet(outWallet)) - } - scenario := evilwallet.NewEvilScenario(scenarioOptions...) - if scenario.NumOfClientsNeeded > w.NumOfClient() { - log.Infof("Warning: At least %d client are needed to spam, and %d was provided", scenario.NumOfClientsNeeded, w.NumOfClient()) - } - - options := []spammer.Options{ - spammer.WithSpamRate(rate, timeUnit), - spammer.WithSpamDuration(duration), - spammer.WithEvilWallet(w), - spammer.WithRateSetter(enableRateSetter), - spammer.WithEvilScenario(scenario), - spammer.WithAccountAlias(accountAlias), - } - - return spammer.NewSpammer(options...) -} - -func SpamBlocks(w *evilwallet.EvilWallet, rate int, timeUnit, duration time.Duration, numBlkToSend int, enableRateSetter bool, accountAlias string) *spammer.Spammer { - if w.NumOfClient() < 1 { - log.Infof("Warning: At least one client is needed to spam.") - } - - options := []spammer.Options{ - spammer.WithSpamRate(rate, timeUnit), - spammer.WithSpamDuration(duration), - spammer.WithBatchesSent(numBlkToSend), - spammer.WithRateSetter(enableRateSetter), - spammer.WithEvilWallet(w), - spammer.WithSpammingFunc(spammer.DataSpammingFunction), - spammer.WithAccountAlias(accountAlias), - } - - return spammer.NewSpammer(options...) -} - -func SpamAccounts(w *evilwallet.EvilWallet, rate int, timeUnit, duration time.Duration, enableRateSetter bool, accountAlias string) *spammer.Spammer { - if w.NumOfClient() < 1 { - log.Infof("Warning: At least one client is needed to spam.") - } - scenarioOptions := []evilwallet.ScenarioOption{ - evilwallet.WithScenarioCustomConflicts(evilwallet.SingleTransactionBatch()), - evilwallet.WithCreateAccounts(), - } - - scenarioAccount := evilwallet.NewEvilScenario(scenarioOptions...) - - options := []spammer.Options{ - spammer.WithSpamRate(rate, timeUnit), - spammer.WithSpamDuration(duration), - spammer.WithRateSetter(enableRateSetter), - spammer.WithEvilWallet(w), - spammer.WithSpammingFunc(spammer.AccountSpammingFunction), - spammer.WithEvilScenario(scenarioAccount), - spammer.WithAccountAlias(accountAlias), - } - - return spammer.NewSpammer(options...) -} diff --git a/tools/evil-spammer/spammer/clock.go b/tools/evil-spammer/spammer/clock.go deleted file mode 100644 index 87a3cc069..000000000 --- a/tools/evil-spammer/spammer/clock.go +++ /dev/null @@ -1,76 +0,0 @@ -package spammer - -// import ( -// "time" - -// iotago "github.com/iotaledger/iota.go/v4" -// "github.com/iotaledger/iota-core/tools/evil-spammer/wallet" -// ) - -// // region ClockSync //////////////evilspammerpkg///////////////////////////////////////////////////////////////////////////////// - -// // ClockSync is used to synchronize with connected nodes. -// type ClockSync struct { -// LatestCommittedSlotClock *SlotClock - -// syncTicker *time.Ticker -// clt wallet.Client -// } - -// func NewClockSync(slotDuration time.Duration, syncInterval time.Duration, clientList wallet.Client) *ClockSync { -// updateTicker := time.NewTicker(syncInterval) -// return &ClockSync{ -// LatestCommittedSlotClock: &SlotClock{slotDuration: slotDuration}, - -// syncTicker: updateTicker, -// clt: clientList, -// } -// } - -// // Start starts the clock synchronization in the background after the first sync is done.. -// func (c *ClockSync) Start() { -// c.Synchronize() -// go func() { -// for range c.syncTicker.C { -// c.Synchronize() -// } -// }() -// } - -// func (c *ClockSync) Shutdown() { -// c.syncTicker.Stop() -// } - -// func (c *ClockSync) Synchronize() { -// si, err := c.clt.GetLatestCommittedSlot() -// if err != nil { -// return -// } -// c.LatestCommittedSlotClock.Update(si) -// } - -// // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////// - -// // region SlotClock /////////////////////////////////////////////////////////////////////////////////////////////// - -// type SlotClock struct { -// lastUpdated time.Time -// updatedSlot slot.Index - -// slotDuration time.Duration -// } - -// func (c *SlotClock) Update(value slot.Index) { -// c.lastUpdated = time.Now() -// c.updatedSlot = value -// } - -// func (c *SlotClock) Get() slot.Index { -// return c.updatedSlot -// } - -// func (c *SlotClock) GetRelative() slot.Index { -// return c.updatedSlot + slot.Index(time.Since(c.lastUpdated)/c.slotDuration) -// } - -// // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/tools/evil-spammer/spammer/commitmentmanager.go b/tools/evil-spammer/spammer/commitmentmanager.go deleted file mode 100644 index dc9181f04..000000000 --- a/tools/evil-spammer/spammer/commitmentmanager.go +++ /dev/null @@ -1,245 +0,0 @@ -package spammer - -// import ( -// "crypto/sha256" -// "math/rand" -// "time" - -// "github.com/iotaledger/hive.go/core/slot" -// "github.com/iotaledger/iota-core/tools/evil-spammer/wallet" -// iotago "github.com/iotaledger/iota.go/v4" - -// "github.com/pkg/errors" -// ) - -// type CommitmentManagerParams struct { -// CommitmentType string -// ValidClientURL string -// ParentRefsCount int -// ClockResyncTime time.Duration -// GenesisTime time.Time -// SlotDuration time.Duration - -// OptionalForkAfter int -// } -// type CommitmentManager struct { -// Params *CommitmentManagerParams -// // we store here only the valid commitments to not request them again through API -// validChain map[slot.Index]*iotago.Commitment -// // commitments used to spam -// commitmentChain map[slot.Index]*iotago.Commitment - -// initiationSlot slot.Index -// forkIndex slot.Index -// latestCommitted slot.Index - -// clockSync *ClockSync -// validClient wallet.Client - -// log Logger -// } - -// func NewCommitmentManager() *CommitmentManager { -// return &CommitmentManager{ -// Params: &CommitmentManagerParams{ -// ParentRefsCount: 2, -// ClockResyncTime: 30 * time.Second, -// GenesisTime: time.Now(), -// SlotDuration: 5 * time.Second, -// }, -// validChain: make(map[slot.Index]*iotago.Commitment), -// commitmentChain: make(map[slot.Index]*iotago.Commitment), -// } -// } - -// func (c *CommitmentManager) Setup(l Logger) { -// c.log = l - -// c.log.Infof("Commitment Manager will be based on the valid client: %s", c.Params.ValidClientURL) -// c.validClient = wallet.NewWebClient(c.Params.ValidClientURL) -// c.setupTimeParams(c.validClient) - -// c.clockSync = NewClockSync(c.Params.SlotDuration, c.Params.ClockResyncTime, c.validClient) -// c.clockSync.Start() - -// c.setupForkingPoint() -// c.setupInitCommitment() -// } - -// // SetupInitCommitment sets the initiation commitment which is the current valid commitment requested from validClient. -// func (c *CommitmentManager) setupInitCommitment() { -// c.initiationSlot = c.clockSync.LatestCommittedSlotClock.Get() -// comm, err := c.getValidCommitment(c.initiationSlot) -// if err != nil { -// panic(errors.Wrapf(err, "failed to get initiation commitment")) -// } -// c.commitmentChain[comm.Slot()] = comm -// c.latestCommitted = comm.Slot() -// } - -// // SetupTimeParams requests through API and sets the genesis time and slot duration for the commitment manager. -// func (c *CommitmentManager) setupTimeParams(clt wallet.Client) { -// genesisTime, slotDuration, err := clt.GetTimeProvider() -// if err != nil { -// panic(errors.Wrapf(err, "failed to get time provider for the commitment manager setup")) -// } -// c.Params.GenesisTime = genesisTime -// c.Params.SlotDuration = slotDuration -// } - -// func (c *CommitmentManager) SetCommitmentType(commitmentType string) { -// c.Params.CommitmentType = commitmentType -// } - -// func (c *CommitmentManager) SetForkAfter(forkAfter int) { -// c.Params.OptionalForkAfter = forkAfter -// } - -// // SetupForkingPoint sets the forking point for the commitment manager. It uses ForkAfter parameter so need to be called after params are read. -// func (c *CommitmentManager) setupForkingPoint() { -// c.forkIndex = c.clockSync.LatestCommittedSlotClock.Get() + slot.Index(c.Params.OptionalForkAfter) -// } - -// func (c *CommitmentManager) Shutdown() { -// c.clockSync.Shutdown() -// } - -// func (c *CommitmentManager) commit(comm *iotago.Commitment) { -// c.commitmentChain[comm.Slot()] = comm -// if comm.Slot() > c.latestCommitted { -// if comm.Slot()-c.latestCommitted != 1 { -// panic("next committed slot is not sequential, lastCommitted: " + c.latestCommitted.String() + " nextCommitted: " + comm.Slot().String()) -// } -// c.latestCommitted = comm.Slot() -// } -// } - -// func (c *CommitmentManager) getLatestCommitment() *iotago.Commitment { -// return c.commitmentChain[c.latestCommitted] -// } - -// // GenerateCommitment generates a commitment based on the commitment type provided in spam details. -// func (c *CommitmentManager) GenerateCommitment(clt wallet.Client) (*iotago.Commitment, slot.Index, error) { -// switch c.Params.CommitmentType { -// // todo refactor this to work with chainsA -// case "latest": -// comm, err := clt.GetLatestCommitment() -// if err != nil { -// return nil, 0, errors.Wrap(err, "failed to get latest commitment") -// } -// index, err := clt.GetLatestConfirmedIndex() -// if err != nil { -// return nil, 0, errors.Wrap(err, "failed to get latest confirmed index") -// } -// return comm, index, err -// case "random": -// slot := c.clockSync.LatestCommittedSlotClock.Get() -// newCommitment := randomCommitmentChain(slot) - -// return newCommitment, slot - 10, nil - -// case "fork": -// // it should request time periodically, and be relative -// slot := c.clockSync.LatestCommittedSlotClock.Get() -// // make sure chain is upto date to the forking point -// uptoSlot := c.forkIndex -// // get minimum -// if slot < c.forkIndex { -// uptoSlot = slot -// } -// err := c.updateChainWithValidCommitment(uptoSlot) -// if err != nil { -// return nil, 0, errors.Wrap(err, "failed to update chain with valid commitment") -// } -// if c.isAfterForkPoint(slot) { -// c.updateForkedChain(slot) -// } -// comm := c.getLatestCommitment() -// index, err := clt.GetLatestConfirmedIndex() -// if err != nil { -// return nil, 0, errors.Wrap(err, "failed to get latest confirmed index") -// } -// return comm, index - 1, nil -// } -// return nil, 0, nil -// } - -// func (c *CommitmentManager) isAfterForkPoint(slot slot.Index) bool { -// return c.forkIndex != 0 && slot > c.forkIndex -// } - -// // updateChainWithValidCommitment commits the chain up to the given slot with the valid commitments. -// func (c *CommitmentManager) updateChainWithValidCommitment(s slot.Index) error { -// for i := c.latestCommitted + 1; i <= s; i++ { -// comm, err := c.getValidCommitment(i) -// if err != nil { -// return errors.Wrapf(err, "failed to get valid commitment for slot %d", i) -// } -// c.commit(comm) -// } -// return nil -// } - -// func (c *CommitmentManager) updateForkedChain(slot slot.Index) { -// for i := c.latestCommitted + 1; i <= slot; i++ { -// comm, err := c.getForkedCommitment(i) -// if err != nil { -// panic(errors.Wrapf(err, "failed to get forked commitment for slot %d", i)) -// } -// c.commit(comm) -// } -// } - -// // getValidCommitment returns the valid commitment for the given slot if not exists it requests it from the node and update the validChain. -// func (c *CommitmentManager) getValidCommitment(slot slot.Index) (*commitment.Commitment, error) { -// if comm, ok := c.validChain[slot]; ok { -// return comm, nil -// } -// // if not requested before then get it from the node -// comm, err := c.validClient.GetCommitment(int(slot)) -// if err != nil { -// return nil, errors.Wrapf(err, "failed to get commitment for slot %d", slot) -// } -// c.validChain[slot] = comm - -// return comm, nil -// } - -// func (c *CommitmentManager) getForkedCommitment(slot slot.Index) (*commitment.Commitment, error) { -// validComm, err := c.getValidCommitment(slot) -// if err != nil { -// return nil, errors.Wrapf(err, "failed to get valid commitment for slot %d", slot) -// } -// prevComm := c.commitmentChain[slot-1] -// forkedComm := commitment.New( -// validComm.Slot(), -// prevComm.ID(), -// randomRoot(), -// validComm.CumulativeWeight(), -// ) -// return forkedComm, nil -// } - -// func randomCommitmentChain(currSlot slot.Index) *commitment.Commitment { -// chain := make([]*commitment.Commitment, currSlot+1) -// chain[0] = commitment.NewEmptyCommitment() -// for i := slot.Index(0); i < currSlot-1; i++ { -// prevComm := chain[i] -// newCommitment := commitment.New( -// i, -// prevComm.ID(), -// randomRoot(), -// 100, -// ) -// chain[i+1] = newCommitment -// } -// return chain[currSlot-1] -// } - -// func randomRoot() [32]byte { -// data := make([]byte, 10) -// for i := range data { -// data[i] = byte(rand.Intn(256)) -// } -// return sha256.Sum256(data) -// } diff --git a/tools/evil-spammer/spammer/errors.go b/tools/evil-spammer/spammer/errors.go deleted file mode 100644 index 4c056ffaa..000000000 --- a/tools/evil-spammer/spammer/errors.go +++ /dev/null @@ -1,69 +0,0 @@ -package spammer - -import ( - "fmt" - - "go.uber.org/atomic" - - "github.com/iotaledger/hive.go/ierrors" - "github.com/iotaledger/hive.go/runtime/syncutils" -) - -var ( - ErrFailPostBlock = ierrors.New("failed to post block") - ErrFailSendDataBlock = ierrors.New("failed to send a data block") - ErrFailGetReferences = ierrors.New("failed to get references") - ErrTransactionIsNil = ierrors.New("provided transaction is nil") - ErrTransactionInvalid = ierrors.New("provided transaction is invalid") - ErrPayloadIsNil = ierrors.New("provided payload is nil") - ErrFailToPrepareBatch = ierrors.New("custom conflict batch could not be prepared") - ErrInsufficientClients = ierrors.New("insufficient clients to send conflicts") - ErrInputsNotSolid = ierrors.New("not all inputs are solid") - ErrFailPrepareBlock = ierrors.New("failed to prepare block") - ErrFailGetAccount = ierrors.New("failed to get account from the account wallet") -) - -// ErrorCounter counts errors that appeared during the spam, -// as during the spam they are ignored and allows to print the summary (might be useful for debugging). -type ErrorCounter struct { - errorsMap map[error]*atomic.Int64 - errInTotalCount *atomic.Int64 - mutex syncutils.RWMutex -} - -func NewErrorCount() *ErrorCounter { - e := &ErrorCounter{ - errorsMap: make(map[error]*atomic.Int64), - errInTotalCount: atomic.NewInt64(0), - } - - return e -} - -func (e *ErrorCounter) CountError(err error) { - e.mutex.Lock() - defer e.mutex.Unlock() - - // check if error is already in the map - if _, ok := e.errorsMap[err]; !ok { - e.errorsMap[err] = atomic.NewInt64(0) - } - e.errInTotalCount.Add(1) - e.errorsMap[err].Add(1) -} - -func (e *ErrorCounter) GetTotalErrorCount() int64 { - return e.errInTotalCount.Load() -} - -func (e *ErrorCounter) GetErrorsSummary() string { - if len(e.errorsMap) == 0 { - return "No errors encountered" - } - blk := "Errors encountered during spam:\n" - for key, value := range e.errorsMap { - blk += fmt.Sprintf("%s: %d\n", key.Error(), value.Load()) - } - - return blk -} diff --git a/tools/evil-spammer/spammer/options.go b/tools/evil-spammer/spammer/options.go deleted file mode 100644 index e42eca8a1..000000000 --- a/tools/evil-spammer/spammer/options.go +++ /dev/null @@ -1,165 +0,0 @@ -package spammer - -import ( - "time" - - "github.com/iotaledger/iota-core/tools/evil-spammer/evilwallet" - "github.com/iotaledger/iota-core/tools/evil-spammer/models" -) - -type Options func(*Spammer) - -// region Spammer general options /////////////////////////////////////////////////////////////////////////////////////////////////// - -// WithSpamRate provides spammer with options regarding rate, time unit, and finishing spam criteria. Provide 0 to one of max parameters to skip it. -func WithSpamRate(rate int, timeUnit time.Duration) Options { - return func(s *Spammer) { - if s.SpamDetails == nil { - s.SpamDetails = &SpamDetails{ - Rate: rate, - TimeUnit: timeUnit, - } - } else { - s.SpamDetails.Rate = rate - s.SpamDetails.TimeUnit = timeUnit - } - } -} - -// WithSpamDuration provides spammer with options regarding rate, time unit, and finishing spam criteria. Provide 0 to one of max parameters to skip it. -func WithSpamDuration(maxDuration time.Duration) Options { - return func(s *Spammer) { - if s.SpamDetails == nil { - s.SpamDetails = &SpamDetails{ - MaxDuration: maxDuration, - } - } else { - s.SpamDetails.MaxDuration = maxDuration - } - } -} - -// WithErrorCounter allows for setting an error counter object, if not provided a new instance will be created. -func WithErrorCounter(errCounter *ErrorCounter) Options { - return func(s *Spammer) { - s.ErrCounter = errCounter - } -} - -// WithLogTickerInterval allows for changing interval between progress spamming logs, default is 30s. -func WithLogTickerInterval(interval time.Duration) Options { - return func(s *Spammer) { - s.State.logTickTime = interval - } -} - -// WithSpammingFunc sets core function of the spammer with spamming logic, needs to use done spammer's channel to communicate. -// end of spamming and errors. Default one is the CustomConflictSpammingFunc. -func WithSpammingFunc(spammerFunc func(s *Spammer)) Options { - return func(s *Spammer) { - s.spamFunc = spammerFunc - } -} - -// WithAccountAlias sets the alias of the account that will be used to pay with mana for sent blocks. -func WithAccountAlias(alias string) Options { - return func(s *Spammer) { - s.IssuerAlias = alias - } -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// region Spammer EvilWallet options /////////////////////////////////////////////////////////////////////////////////////////////////// - -// WithRateSetter enables setting rate of spammer. -func WithRateSetter(enable bool) Options { - return func(s *Spammer) { - s.UseRateSetter = enable - } -} - -// WithBatchesSent provides spammer with options regarding rate, time unit, and finishing spam criteria. Provide 0 to one of max parameters to skip it. -func WithBatchesSent(maxBatchesSent int) Options { - return func(s *Spammer) { - if s.SpamDetails == nil { - s.SpamDetails = &SpamDetails{ - MaxBatchesSent: maxBatchesSent, - } - } else { - s.SpamDetails.MaxBatchesSent = maxBatchesSent - } - } -} - -// WithEvilWallet provides evil wallet instance, that will handle all spam logic according to provided EvilScenario. -func WithEvilWallet(initWallets *evilwallet.EvilWallet) Options { - return func(s *Spammer) { - s.EvilWallet = initWallets - } -} - -// WithEvilScenario provides initWallet of spammer, if omitted spammer will prepare funds based on maxBlkSent parameter. -func WithEvilScenario(scenario *evilwallet.EvilScenario) Options { - return func(s *Spammer) { - s.EvilScenario = scenario - } -} - -func WithTimeDelayForDoubleSpend(timeDelay time.Duration) Options { - return func(s *Spammer) { - s.TimeDelayBetweenConflicts = timeDelay - } -} - -// WithNumberOfSpends sets how many transactions should be created with the same input, e.g 3 for triple spend, -// 2 for double spend. For this to work user needs to make sure that there is enough number of clients. -func WithNumberOfSpends(n int) Options { - return func(s *Spammer) { - s.NumberOfSpends = n - } -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// region Spammer Commitment options /////////////////////////////////////////////////////////////////////////////////////////////////// - -func WithClientURL(clientURL string) Options { - return func(s *Spammer) { - s.Clients = models.NewWebClients([]string{clientURL}) - } -} - -// func WithValidClientURL(validClient string) Options { -// return func(s *Spammer) { -// s.CommitmentManager.Params.ValidClientURL = validClient -// } -// } - -// WithCommitmentType provides commitment type for the spammer, allowed types: fork, valid, random. Enables commitment spam and disables the wallet functionality. -// func WithCommitmentType(commitmentType string) Options { -// return func(s *Spammer) { -// s.SpamType = SpamCommitments -// s.CommitmentManager.SetCommitmentType(commitmentType) -// } -// } - -// WithForkAfter provides after how many slots from the spammer setup should fork bee created, this option can be used with CommitmentType: fork. -// func WithForkAfter(forkingAfter int) Options { -// return func(s *Spammer) { -// s.CommitmentManager.SetForkAfter(forkingAfter) -// } -// } - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -type SpamDetails struct { - Rate int - TimeUnit time.Duration - MaxDuration time.Duration - MaxBatchesSent int -} - -type CommitmentSpamDetails struct { - CommitmentType string -} diff --git a/tools/evil-spammer/spammer/spammer.go b/tools/evil-spammer/spammer/spammer.go deleted file mode 100644 index 83b5d94e8..000000000 --- a/tools/evil-spammer/spammer/spammer.go +++ /dev/null @@ -1,293 +0,0 @@ -package spammer - -import ( - "time" - - "go.uber.org/atomic" - - "github.com/iotaledger/hive.go/app/configuration" - appLogger "github.com/iotaledger/hive.go/app/logger" - "github.com/iotaledger/hive.go/ds/types" - "github.com/iotaledger/hive.go/ierrors" - "github.com/iotaledger/hive.go/logger" - "github.com/iotaledger/iota-core/pkg/protocol/snapshotcreator" - "github.com/iotaledger/iota-core/tools/evil-spammer/evilwallet" - "github.com/iotaledger/iota-core/tools/evil-spammer/models" - "github.com/iotaledger/iota-core/tools/genesis-snapshot/presets" - iotago "github.com/iotaledger/iota.go/v4" -) - -const ( - TypeBlock = "blk" - TypeTx = "tx" - TypeDs = "ds" - TypeCustom = "custom" - TypeCommitments = "commitments" - TypeAccounts = "accounts" -) - -// region Spammer ////////////////////////////////////////////////////////////////////////////////////////////////////// - -//nolint:revive -type SpammerFunc func(*Spammer) - -type State struct { - spamTicker *time.Ticker - logTicker *time.Ticker - spamStartTime time.Time - txSent *atomic.Int64 - batchPrepared *atomic.Int64 - - logTickTime time.Duration - spamDuration time.Duration -} - -type SpamType int - -const ( - SpamEvilWallet SpamType = iota - SpamCommitments -) - -// Spammer is a utility object for new spammer creations, can be modified by passing options. -// Mandatory options: WithClients, WithSpammingFunc -// Not mandatory options, if not provided spammer will use default settings: -// WithSpamDetails, WithEvilWallet, WithErrorCounter, WithLogTickerInterval. -type Spammer struct { - SpamDetails *SpamDetails - State *State - UseRateSetter bool - SpamType SpamType - Clients models.Connector - EvilWallet *evilwallet.EvilWallet - EvilScenario *evilwallet.EvilScenario - // CommitmentManager *CommitmentManager - ErrCounter *ErrorCounter - IssuerAlias string - - log Logger - api iotago.API - - // accessed from spamming functions - done chan bool - shutdown chan types.Empty - spamFunc SpammerFunc - - TimeDelayBetweenConflicts time.Duration - NumberOfSpends int -} - -// NewSpammer is a constructor of Spammer. -func NewSpammer(options ...Options) *Spammer { - protocolParams := snapshotcreator.NewOptions(presets.Docker...).ProtocolParameters - api := iotago.V3API(protocolParams) - - state := &State{ - txSent: atomic.NewInt64(0), - batchPrepared: atomic.NewInt64(0), - logTickTime: time.Second * 30, - } - s := &Spammer{ - SpamDetails: &SpamDetails{}, - spamFunc: CustomConflictSpammingFunc, - State: state, - SpamType: SpamEvilWallet, - EvilScenario: evilwallet.NewEvilScenario(), - // CommitmentManager: NewCommitmentManager(), - UseRateSetter: true, - done: make(chan bool), - shutdown: make(chan types.Empty), - NumberOfSpends: 2, - api: api, - } - - for _, opt := range options { - opt(s) - } - - s.setup() - - return s -} - -func (s *Spammer) BlocksSent() uint64 { - return uint64(s.State.txSent.Load()) -} - -func (s *Spammer) BatchesPrepared() uint64 { - return uint64(s.State.batchPrepared.Load()) -} - -func (s *Spammer) setup() { - if s.log == nil { - s.initLogger() - } - - switch s.SpamType { - case SpamEvilWallet: - if s.EvilWallet == nil { - s.EvilWallet = evilwallet.NewEvilWallet() - } - s.Clients = s.EvilWallet.Connector() - // case SpamCommitments: - // s.CommitmentManager.Setup(s.log) - } - s.setupSpamDetails() - - s.State.spamTicker = s.initSpamTicker() - s.State.logTicker = s.initLogTicker() - - if s.ErrCounter == nil { - s.ErrCounter = NewErrorCount() - } -} - -func (s *Spammer) setupSpamDetails() { - if s.SpamDetails.Rate <= 0 { - s.SpamDetails.Rate = 1 - } - if s.SpamDetails.TimeUnit == 0 { - s.SpamDetails.TimeUnit = time.Second - } - // provided only maxBlkSent, calculating the default max for maxDuration - if s.SpamDetails.MaxDuration == 0 && s.SpamDetails.MaxBatchesSent > 0 { - s.SpamDetails.MaxDuration = time.Hour * 100 - } - // provided only maxDuration, calculating the default max for maxBlkSent - if s.SpamDetails.MaxBatchesSent == 0 && s.SpamDetails.MaxDuration > 0 { - s.SpamDetails.MaxBatchesSent = int(s.SpamDetails.MaxDuration.Seconds()/s.SpamDetails.TimeUnit.Seconds()*float64(s.SpamDetails.Rate)) + 1 - } -} - -func (s *Spammer) initLogger() { - config := configuration.New() - _ = appLogger.InitGlobalLogger(config) - logger.SetLevel(logger.LevelDebug) - s.log = logger.NewLogger("Spammer") -} - -func (s *Spammer) initSpamTicker() *time.Ticker { - tickerTime := float64(s.SpamDetails.TimeUnit) / float64(s.SpamDetails.Rate) - return time.NewTicker(time.Duration(tickerTime)) -} - -func (s *Spammer) initLogTicker() *time.Ticker { - return time.NewTicker(s.State.logTickTime) -} - -// Spam runs the spammer. Function will stop after maxDuration time will pass or when maxBlkSent will be exceeded. -func (s *Spammer) Spam() { - s.log.Infof("Start spamming transactions with %d rate", s.SpamDetails.Rate) - - s.State.spamStartTime = time.Now() - timeExceeded := time.After(s.SpamDetails.MaxDuration) - - go func() { - goroutineCount := atomic.NewInt32(0) - for { - select { - case <-s.State.logTicker.C: - s.log.Infof("Blocks issued so far: %d, errors encountered: %d", s.State.txSent.Load(), s.ErrCounter.GetTotalErrorCount()) - case <-timeExceeded: - s.log.Infof("Maximum spam duration exceeded, stopping spammer....") - s.StopSpamming() - - return - case <-s.done: - s.StopSpamming() - return - case <-s.State.spamTicker.C: - if goroutineCount.Load() > 100 { - break - } - go func() { - goroutineCount.Inc() - defer goroutineCount.Dec() - s.spamFunc(s) - }() - } - } - }() - <-s.shutdown - s.log.Info(s.ErrCounter.GetErrorsSummary()) - s.log.Infof("Finishing spamming, total txns sent: %v, TotalTime: %v, Rate: %f", s.State.txSent.Load(), s.State.spamDuration.Seconds(), float64(s.State.txSent.Load())/s.State.spamDuration.Seconds()) -} - -func (s *Spammer) CheckIfAllSent() { - if s.State.batchPrepared.Load() >= int64(s.SpamDetails.MaxBatchesSent) { - s.log.Infof("Maximum number of blocks sent, stopping spammer...") - s.done <- true - } -} - -// StopSpamming finishes tasks before shutting down the spammer. -func (s *Spammer) StopSpamming() { - s.State.spamDuration = time.Since(s.State.spamStartTime) - s.State.spamTicker.Stop() - s.State.logTicker.Stop() - // s.CommitmentManager.Shutdown() - s.shutdown <- types.Void -} - -func (s *Spammer) PrepareAndPostBlock(txData *models.PayloadIssuanceData, issuerAlias string, clt models.Client) { - if txData.Payload == nil { - s.log.Debug(ErrPayloadIsNil) - s.ErrCounter.CountError(ErrPayloadIsNil) - - return - } - issuerAccount, err := s.EvilWallet.GetAccount(issuerAlias) - if err != nil { - s.log.Debug(ierrors.Wrapf(ErrFailGetAccount, err.Error())) - s.ErrCounter.CountError(ierrors.Wrapf(ErrFailGetAccount, err.Error())) - - return - } - blockID, err := s.EvilWallet.PrepareAndPostBlock(clt, txData.Payload, txData.CongestionResponse, issuerAccount) - if err != nil { - s.log.Debug(ierrors.Wrapf(ErrFailPostBlock, err.Error())) - s.ErrCounter.CountError(ierrors.Wrapf(ErrFailPostBlock, err.Error())) - - return - } - - if txData.Payload.PayloadType() != iotago.PayloadSignedTransaction { - return - } - - signedTx := txData.Payload.(*iotago.SignedTransaction) - - txID, err := signedTx.Transaction.ID() - if err != nil { - s.log.Debug(ierrors.Wrapf(ErrTransactionInvalid, err.Error())) - s.ErrCounter.CountError(ierrors.Wrapf(ErrTransactionInvalid, err.Error())) - - return - } - - // reuse outputs - if txData.Payload.PayloadType() == iotago.PayloadSignedTransaction { - if s.EvilScenario.OutputWallet.Type() == evilwallet.Reuse { - var outputIDs iotago.OutputIDs - for index := range signedTx.Transaction.Outputs { - outputIDs = append(outputIDs, iotago.OutputIDFromTransactionIDAndIndex(txID, uint16(index))) - } - s.EvilWallet.SetTxOutputsSolid(outputIDs, clt.URL()) - } - } - count := s.State.txSent.Add(1) - s.log.Debugf("Last block sent, ID: %s, txCount: %d", blockID.ToHex(), count) -} - -// endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// - -type Logger interface { - Infof(template string, args ...interface{}) - Info(args ...interface{}) - Debugf(template string, args ...interface{}) - Debug(args ...interface{}) - Warn(args ...interface{}) - Warnf(template string, args ...interface{}) - Error(args ...interface{}) - Errorf(template string, args ...interface{}) -} diff --git a/tools/evil-spammer/spammer/spamming_functions.go b/tools/evil-spammer/spammer/spamming_functions.go deleted file mode 100644 index 2462c07ca..000000000 --- a/tools/evil-spammer/spammer/spamming_functions.go +++ /dev/null @@ -1,138 +0,0 @@ -package spammer - -import ( - "math/rand" - "sync" - "time" - - "github.com/iotaledger/hive.go/ierrors" - "github.com/iotaledger/iota-core/tools/evil-spammer/models" - iotago "github.com/iotaledger/iota.go/v4" -) - -func DataSpammingFunction(s *Spammer) { - clt := s.Clients.GetClient() - // sleep randomly to avoid issuing blocks in different goroutines at once - //nolint:gosec - time.Sleep(time.Duration(rand.Float64()*20) * time.Millisecond) - - s.PrepareAndPostBlock(&models.PayloadIssuanceData{ - Payload: &iotago.TaggedData{ - Tag: []byte("SPAM"), - }, - }, s.IssuerAlias, clt) - - s.State.batchPrepared.Add(1) - s.CheckIfAllSent() -} - -func CustomConflictSpammingFunc(s *Spammer) { - conflictBatch, aliases, err := s.EvilWallet.PrepareCustomConflictsSpam(s.EvilScenario, &models.IssuancePaymentStrategy{ - AllotmentStrategy: models.AllotmentStrategyAll, - IssuerAlias: s.IssuerAlias, - }) - - if err != nil { - s.log.Debugf(ierrors.Wrap(ErrFailToPrepareBatch, err.Error()).Error()) - s.ErrCounter.CountError(ierrors.Wrap(ErrFailToPrepareBatch, err.Error())) - } - - for _, txsData := range conflictBatch { - clients := s.Clients.GetClients(len(txsData)) - if len(txsData) > len(clients) { - s.log.Debug(ErrFailToPrepareBatch) - s.ErrCounter.CountError(ErrInsufficientClients) - } - - // send transactions in parallel - wg := sync.WaitGroup{} - for i, txData := range txsData { - wg.Add(1) - go func(clt models.Client, tx *models.PayloadIssuanceData) { - defer wg.Done() - - // sleep randomly to avoid issuing blocks in different goroutines at once - //nolint:gosec - time.Sleep(time.Duration(rand.Float64()*100) * time.Millisecond) - - s.PrepareAndPostBlock(tx, s.IssuerAlias, clt) - }(clients[i], txData) - } - wg.Wait() - } - s.State.batchPrepared.Add(1) - s.EvilWallet.ClearAliases(aliases) - s.CheckIfAllSent() -} - -func AccountSpammingFunction(s *Spammer) { - clt := s.Clients.GetClient() - // update scenario - txData, aliases, err := s.EvilWallet.PrepareAccountSpam(s.EvilScenario, &models.IssuancePaymentStrategy{ - AllotmentStrategy: models.AllotmentStrategyAll, - IssuerAlias: s.IssuerAlias, - }) - if err != nil { - s.log.Debugf(ierrors.Wrap(ErrFailToPrepareBatch, err.Error()).Error()) - s.ErrCounter.CountError(ierrors.Wrap(ErrFailToPrepareBatch, err.Error())) - } - s.PrepareAndPostBlock(txData, s.IssuerAlias, clt) - - s.State.batchPrepared.Add(1) - s.EvilWallet.ClearAliases(aliases) - s.CheckIfAllSent() -} - -// func CommitmentsSpammingFunction(s *Spammer) { -// clt := s.Clients.GetClient() -// p := payload.NewGenericDataPayload([]byte("SPAM")) -// payloadBytes, err := p.Bytes() -// if err != nil { -// s.ErrCounter.CountError(ErrFailToPrepareBatch) -// } -// parents, err := clt.GetReferences(payloadBytes, s.CommitmentManager.Params.ParentRefsCount) -// if err != nil { -// s.ErrCounter.CountError(ErrFailGetReferences) -// } -// localID := s.IdentityManager.GetIdentity() -// commitment, latestConfIndex, err := s.CommitmentManager.GenerateCommitment(clt) -// if err != nil { -// s.log.Debugf(errors.WithMessage(ErrFailToPrepareBatch, err.Error()).Error()) -// s.ErrCounter.CountError(errors.WithMessage(ErrFailToPrepareBatch, err.Error())) -// } -// block := models.NewBlock( -// models.WithParents(parents), -// models.WithIssuer(localID.PublicKey()), -// models.WithIssuingTime(time.Now()), -// models.WithPayload(p), -// models.WithLatestConfirmedSlot(latestConfIndex), -// models.WithCommitment(commitment), -// models.WithSignature(ed25519.EmptySignature), -// ) -// signature, err := wallet.SignBlock(block, localID) -// if err != nil { -// return -// } -// block.SetSignature(signature) -// timeProvider := slot.NewTimeProvider(s.CommitmentManager.Params.GenesisTime.Unix(), int64(s.CommitmentManager.Params.SlotDuration.Seconds())) -// if err = block.DetermineID(timeProvider); err != nil { -// s.ErrCounter.CountError(ErrFailPrepareBlock) -// } -// blockBytes, err := block.Bytes() -// if err != nil { -// s.ErrCounter.CountError(ErrFailPrepareBlock) -// } - -// blkID, err := clt.PostBlock(blockBytes) -// if err != nil { -// fmt.Println(err) -// s.ErrCounter.CountError(ErrFailSendDataBlock) -// } - -// count := s.State.txSent.Add(1) -// if count%int64(s.SpamDetails.Rate*4) == 0 { -// s.log.Debugf("Last sent block, ID: %s; %s blkCount: %d", blkID, commitment.ID().String(), count) -// } -// s.State.batchPrepared.Add(1) -// s.CheckIfAllSent() -// } diff --git a/tools/evil-spammer/spammer/utils.go b/tools/evil-spammer/spammer/utils.go deleted file mode 100644 index 997945723..000000000 --- a/tools/evil-spammer/spammer/utils.go +++ /dev/null @@ -1,16 +0,0 @@ -package spammer - -import ( - "time" - - "github.com/iotaledger/iota-core/tools/evil-spammer/evilwallet" -) - -// BigWalletsNeeded calculates how many big wallets needs to be prepared for a spam based on provided spam details. -func BigWalletsNeeded(rate int, timeUnit, duration time.Duration) int { - bigWalletSize := evilwallet.FaucetRequestSplitNumber * evilwallet.FaucetRequestSplitNumber - outputsNeeded := rate * int(duration/timeUnit) - walletsNeeded := outputsNeeded/bigWalletSize + 1 - - return walletsNeeded -} diff --git a/tools/gendoc/go.mod b/tools/gendoc/go.mod index 782a1451c..e2e9744da 100644 --- a/tools/gendoc/go.mod +++ b/tools/gendoc/go.mod @@ -31,8 +31,8 @@ require ( github.com/flynn/noise v1.0.0 // indirect github.com/fogleman/gg v1.3.0 // indirect github.com/francoispqt/gojay v1.2.13 // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/go-logr/logr v1.2.4 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/go-logr/logr v1.3.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/goccy/go-graphviz v0.1.1 // indirect @@ -40,19 +40,18 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect - github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-github v17.0.0+incompatible // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/gopacket v1.1.19 // indirect - github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f // indirect - github.com/google/uuid v1.3.1 // indirect + github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b // indirect + github.com/google/uuid v1.4.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-version v1.6.0 // indirect - github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/hashicorp/golang-lru v1.0.2 // indirect github.com/holiman/uint256 v1.2.3 // indirect github.com/huin/goupnp v1.3.0 // indirect github.com/iancoleman/orderedmap v0.3.0 // indirect @@ -70,19 +69,19 @@ require ( github.com/iotaledger/hive.go/runtime v0.0.0-20231102135205-0f3d1e75042a // indirect github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231102135205-0f3d1e75042a // indirect github.com/iotaledger/hive.go/stringify v0.0.0-20231102135205-0f3d1e75042a // indirect - github.com/iotaledger/inx-app v1.0.0-rc.3.0.20231020152103-b6ea7ff7a4af // indirect - github.com/iotaledger/inx/go v1.0.0-rc.2.0.20231020151337-569450d5bf7d // indirect - github.com/iotaledger/iota.go/v4 v4.0.0-20231019174124-aa2290512bcd // indirect - github.com/ipfs/boxo v0.10.0 // indirect + github.com/iotaledger/inx-app v1.0.0-rc.3.0.20231031135002-4c79ea5193f5 // indirect + github.com/iotaledger/inx/go v1.0.0-rc.2.0.20231031134131-b6ad918dc1ac // indirect + github.com/iotaledger/iota.go/v4 v4.0.0-20231102113728-20b8d01e826e // 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 github.com/ipfs/go-log v1.0.5 // indirect github.com/ipfs/go-log/v2 v2.5.1 // indirect - github.com/ipld/go-ipld-prime v0.20.0 // indirect + github.com/ipld/go-ipld-prime v0.21.0 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect github.com/jbenet/goprocess v0.1.4 // indirect - github.com/klauspost/compress v1.17.0 // indirect + github.com/klauspost/compress v1.17.2 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/knadh/koanf v1.5.0 // indirect github.com/koron/go-ssdp v0.0.4 // indirect @@ -92,12 +91,12 @@ require ( github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/libp2p/go-cidranger v1.1.0 // indirect github.com/libp2p/go-flow-metrics v0.1.0 // indirect - github.com/libp2p/go-libp2p v0.30.0 // indirect + github.com/libp2p/go-libp2p v0.32.0 // indirect github.com/libp2p/go-libp2p-asn-util v0.3.0 // indirect github.com/libp2p/go-libp2p-kad-dht v0.25.1 // indirect github.com/libp2p/go-libp2p-kbucket v0.6.3 // indirect github.com/libp2p/go-libp2p-record v0.2.0 // indirect - github.com/libp2p/go-libp2p-routing-helpers v0.7.2 // indirect + github.com/libp2p/go-libp2p-routing-helpers v0.7.3 // indirect github.com/libp2p/go-msgio v0.3.0 // indirect github.com/libp2p/go-nat v0.2.0 // indirect github.com/libp2p/go-netroute v0.2.1 // indirect @@ -105,9 +104,9 @@ require ( github.com/libp2p/go-yamux/v4 v4.0.1 // indirect github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/miekg/dns v1.1.55 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect + github.com/miekg/dns v1.1.56 // indirect github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect github.com/minio/sha256-simd v1.0.1 // indirect @@ -117,15 +116,15 @@ require ( 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 v0.11.0 // indirect + github.com/multiformats/go-multiaddr v0.12.0 // indirect github.com/multiformats/go-multiaddr-dns v0.3.1 // indirect github.com/multiformats/go-multiaddr-fmt v0.1.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-multistream v0.4.1 // indirect + github.com/multiformats/go-multistream v0.5.0 // indirect github.com/multiformats/go-varint v0.0.7 // indirect - github.com/onsi/ginkgo/v2 v2.12.0 // indirect + github.com/onsi/ginkgo/v2 v2.13.0 // indirect github.com/opencontainers/runtime-spec v1.1.0 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/otiai10/copy v1.14.0 // indirect @@ -137,13 +136,13 @@ require ( github.com/pokt-network/smt v0.6.1 // indirect github.com/polydawn/refmt v0.89.0 // indirect github.com/prometheus/client_golang v1.17.0 // indirect - github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect - github.com/prometheus/common v0.44.0 // indirect - github.com/prometheus/procfs v0.11.1 // indirect + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/common v0.45.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect - github.com/quic-go/qtls-go1-20 v0.3.3 // indirect - github.com/quic-go/quic-go v0.38.1 // indirect - github.com/quic-go/webtransport-go v0.5.3 // indirect + github.com/quic-go/qtls-go1-20 v0.4.1 // indirect + github.com/quic-go/quic-go v0.40.0 // indirect + github.com/quic-go/webtransport-go v0.6.0 // indirect github.com/raulk/go-watchdog v1.3.0 // indirect github.com/sasha-s/go-deadlock v0.3.1 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect @@ -153,28 +152,30 @@ 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.16.0 // indirect - go.opentelemetry.io/otel/metric v1.16.0 // indirect - go.opentelemetry.io/otel/trace v1.16.0 // indirect + go.opentelemetry.io/otel v1.19.0 // indirect + go.opentelemetry.io/otel/metric v1.19.0 // indirect + go.opentelemetry.io/otel/trace v1.19.0 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/dig v1.17.1 // indirect - go.uber.org/fx v1.20.0 // indirect + go.uber.org/fx v1.20.1 // indirect + go.uber.org/mock v0.3.0 // 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-20230905200255-921286631fa9 // indirect - golang.org/x/image v0.11.0 // indirect - golang.org/x/mod v0.12.0 // indirect + golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect + golang.org/x/image v0.13.0 // indirect + golang.org/x/mod v0.13.0 // 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/text v0.13.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.13.0 // indirect - gonum.org/v1/gonum v0.13.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b // indirect + golang.org/x/tools v0.14.0 // indirect + gonum.org/v1/gonum v0.14.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // indirect google.golang.org/grpc v1.59.0 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/tools/gendoc/go.sum b/tools/gendoc/go.sum index 62a195919..5aac6db21 100644 --- a/tools/gendoc/go.sum +++ b/tools/gendoc/go.sum @@ -115,12 +115,12 @@ github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= -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/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 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/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= @@ -132,8 +132,8 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 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-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -162,8 +162,6 @@ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4er github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= 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= @@ -206,12 +204,12 @@ github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= -github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f h1:pDhu5sgp8yJlEF/g6osliIIpF9K4F5jvkULXa4daRDQ= -github.com/google/pprof v0.0.0-20230821062121-407c9e7a662f/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= +github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b h1:RMpPgZTSApbPf7xaVel+QkoGPRLFLrwFO89uDUHEGf0= +github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 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/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -256,8 +254,8 @@ github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mO github.com/hashicorp/go-version v1.6.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/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= +github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= 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= @@ -309,14 +307,14 @@ github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231102135205-0f3d1e7 github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231102135205-0f3d1e75042a/go.mod h1:SdK26z8/VhWtxaqCuQrufm80SELgowQPmu9T/8eUQ8g= github.com/iotaledger/hive.go/stringify v0.0.0-20231102135205-0f3d1e75042a h1:DNcRsEdvfnrfwh9Mb19lHfsz5Izf9BiMqJILYYW0SvU= github.com/iotaledger/hive.go/stringify v0.0.0-20231102135205-0f3d1e75042a/go.mod h1:FTo/UWzNYgnQ082GI9QVM9HFDERqf9rw9RivNpqrnTs= -github.com/iotaledger/inx-app v1.0.0-rc.3.0.20231020152103-b6ea7ff7a4af h1:3+heabXNGjv3Sx1sg0qG+QdqeEWIpf7jM3T5cPiVmSA= -github.com/iotaledger/inx-app v1.0.0-rc.3.0.20231020152103-b6ea7ff7a4af/go.mod h1:3BZ5r/MhswUZwXHDzMZacOhm6DWpUdGjefNRNshfwiM= -github.com/iotaledger/inx/go v1.0.0-rc.2.0.20231020151337-569450d5bf7d h1:PeJNFBkR28hdQCxUyC/cbOlDjFm3D1U7O5bxi+qsOvQ= -github.com/iotaledger/inx/go v1.0.0-rc.2.0.20231020151337-569450d5bf7d/go.mod h1:/LK3WJ2UhPPAbKTdvRasz0xaQ9qaMI2isYAX9d75WNU= -github.com/iotaledger/iota.go/v4 v4.0.0-20231019174124-aa2290512bcd h1:FKiEzy3gOyWDGXs6mTMS9qFHGXSxH97N5w+08ljbEtU= -github.com/iotaledger/iota.go/v4 v4.0.0-20231019174124-aa2290512bcd/go.mod h1:XFwcDfAF0hv6wB/trCsyDmQyPLjEZ60yTrllJ2bprN8= -github.com/ipfs/boxo v0.10.0 h1:tdDAxq8jrsbRkYoF+5Rcqyeb91hgWe2hp7iLu7ORZLY= -github.com/ipfs/boxo v0.10.0/go.mod h1:Fg+BnfxZ0RPzR0nOodzdIq3A7KgoWAOWsEIImrIQdBM= +github.com/iotaledger/inx-app v1.0.0-rc.3.0.20231031135002-4c79ea5193f5 h1:17JDzMKTMXKF3xys6gPURRddkZhg1LY+xwfhbr/sVqg= +github.com/iotaledger/inx-app v1.0.0-rc.3.0.20231031135002-4c79ea5193f5/go.mod h1:LsJvoBUVVnY7tkwwByCVtAwmp5bFXdyJNGU/+KVQJVM= +github.com/iotaledger/inx/go v1.0.0-rc.2.0.20231031134131-b6ad918dc1ac h1:c7R33+TQGMYP6pvLUQQaqpdDFl+GZbhAcfGMI0285fo= +github.com/iotaledger/inx/go v1.0.0-rc.2.0.20231031134131-b6ad918dc1ac/go.mod h1:qPuMUvCTaghsnYRDnRoRuztTyEKFlmi2S7gb44rH7WM= +github.com/iotaledger/iota.go/v4 v4.0.0-20231102113728-20b8d01e826e h1:ZYRC1MHn/ghsqtjIpYGTxLQrh5n5eUmC0/YWnJiTRhk= +github.com/iotaledger/iota.go/v4 v4.0.0-20231102113728-20b8d01e826e/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= github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= github.com/ipfs/go-datastore v0.6.0 h1:JKyz+Gvz1QEZw0LsX1IBn+JFCJQH4SJVFtM4uWU0Myk= @@ -330,8 +328,8 @@ github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JP github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g= github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY= github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI= -github.com/ipld/go-ipld-prime v0.20.0 h1:Ud3VwE9ClxpO2LkCYP7vWPc0Fo+dYdYzgxUJZ3uRG4g= -github.com/ipld/go-ipld-prime v0.20.0/go.mod h1:PzqZ/ZR981eKbgdr3y2DJYeD/8bgMawdGVlJDE8kK+M= +github.com/ipld/go-ipld-prime v0.21.0 h1:n4JmcpOlPDIxBcY037SVfpd1G+Sj1nKZah0m6QH9C2E= +github.com/ipld/go-ipld-prime v0.21.0/go.mod h1:3RLqy//ERg/y5oShXXdx5YIp50cFGOanyMctpPjsvxQ= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= @@ -356,8 +354,8 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8 github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= 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/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM= -github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4= +github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= 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= @@ -387,8 +385,8 @@ github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38y github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic= github.com/libp2p/go-flow-metrics v0.1.0 h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFGkx3Q3WM= github.com/libp2p/go-flow-metrics v0.1.0/go.mod h1:4Xi8MX8wj5aWNDAZttg6UPmc0ZrnFNsMtpsYUClFtro= -github.com/libp2p/go-libp2p v0.30.0 h1:9EZwFtJPFBcs/yJTnP90TpN1hgrT/EsFfM+OZuwV87U= -github.com/libp2p/go-libp2p v0.30.0/go.mod h1:nr2g5V7lfftwgiJ78/HrID+pwvayLyqKCEirT2Y3Byg= +github.com/libp2p/go-libp2p v0.32.0 h1:86I4B7nBUPIyTgw3+5Ibq6K7DdKRCuZw8URCfPc1hQM= +github.com/libp2p/go-libp2p v0.32.0/go.mod h1:hXXC3kXPlBZ1eu8Q2hptGrMB4mZ3048JUoS4EKaHW5c= github.com/libp2p/go-libp2p-asn-util v0.3.0 h1:gMDcMyYiZKkocGXDQ5nsUQyquC9+H+iLEQHwOCZ7s8s= github.com/libp2p/go-libp2p-asn-util v0.3.0/go.mod h1:B1mcOrKUE35Xq/ASTmQ4tN3LNzVVaMNmq2NACuqyB9w= github.com/libp2p/go-libp2p-kad-dht v0.25.1 h1:ofFNrf6MMEy4vi3R1VbJ7LOcTn3Csh0cDcaWHTxtWNA= @@ -397,8 +395,8 @@ github.com/libp2p/go-libp2p-kbucket v0.6.3 h1:p507271wWzpy2f1XxPzCQG9NiN6R6lHL9G github.com/libp2p/go-libp2p-kbucket v0.6.3/go.mod h1:RCseT7AH6eJWxxk2ol03xtP9pEHetYSPXOaJnOiD8i0= github.com/libp2p/go-libp2p-record v0.2.0 h1:oiNUOCWno2BFuxt3my4i1frNrt7PerzB3queqa1NkQ0= github.com/libp2p/go-libp2p-record v0.2.0/go.mod h1:I+3zMkvvg5m2OcSdoL0KPljyJyvNDFGKX7QdlpYUcwk= -github.com/libp2p/go-libp2p-routing-helpers v0.7.2 h1:xJMFyhQ3Iuqnk9Q2dYE1eUTzsah7NLw3Qs2zjUV78T0= -github.com/libp2p/go-libp2p-routing-helpers v0.7.2/go.mod h1:cN4mJAD/7zfPKXBcs9ze31JGYAZgzdABEm+q/hkswb8= +github.com/libp2p/go-libp2p-routing-helpers v0.7.3 h1:u1LGzAMVRK9Nqq5aYDVOiq/HaB93U9WWczBzGyAC5ZY= +github.com/libp2p/go-libp2p-routing-helpers v0.7.3/go.mod h1:cN4mJAD/7zfPKXBcs9ze31JGYAZgzdABEm+q/hkswb8= github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg= github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0= @@ -428,16 +426,16 @@ github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOA github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +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/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= 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/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo= -github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= +github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= +github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms= github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc= @@ -477,8 +475,8 @@ github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9 github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4= -github.com/multiformats/go-multiaddr v0.11.0 h1:XqGyJ8ufbCE0HmTDwx2kPdsrQ36AGPZNZX6s6xfJH10= -github.com/multiformats/go-multiaddr v0.11.0/go.mod h1:gWUm0QLR4thQ6+ZF6SXUw8YjtwQSPapICM+NmCkxHSM= +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-multiaddr-dns v0.3.1 h1:QgQgR+LQVt3NPTjbrLLpsaT2ufAA2y0Mkk+QRVJbW3A= github.com/multiformats/go-multiaddr-dns v0.3.1/go.mod h1:G/245BRQ6FJGmryJCrOuTdB37AMA5AMOVuO6NY3JwTk= github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= @@ -490,8 +488,8 @@ github.com/multiformats/go-multicodec v0.9.0/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI1 github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= 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-multistream v0.4.1 h1:rFy0Iiyn3YT0asivDUIR05leAdwZq3de4741sbiSdfo= -github.com/multiformats/go-multistream v0.4.1/go.mod h1:Mz5eykRVAjJWckE2U78c6xqdtyNUEhKSM0Lwar2p77Q= +github.com/multiformats/go-multistream v0.5.0 h1:5htLSLl7lvJk3xx3qT/8Zm9J4K8vEOf/QGkvOGQAyiE= +github.com/multiformats/go-multistream v0.5.0/go.mod h1:n6tMZiwiP2wUsR8DgfDWw1dydlEqV3l6N3/GBsX6ILA= github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= 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= @@ -503,8 +501,8 @@ github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5 h1:BvoENQQU+fZ9uukda/R github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= 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/onsi/ginkgo/v2 v2.12.0 h1:UIVDowFPwpg6yMUpPjGkYvf06K3RAiJXUhCxEwQVHRI= -github.com/onsi/ginkgo/v2 v2.12.0/go.mod h1:ZNEzXISYlqpb8S36iN71ifqLi3vVD1rVJGvWRCJOUpQ= +github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= +github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= @@ -556,29 +554,29 @@ github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1: 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/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM= -github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 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/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= -github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= +github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= +github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 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/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= -github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= -github.com/quic-go/qtls-go1-20 v0.3.3 h1:17/glZSLI9P9fDAeyCHBFSWSqJcwx1byhLwP5eUIDCM= -github.com/quic-go/qtls-go1-20 v0.3.3/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= -github.com/quic-go/quic-go v0.38.1 h1:M36YWA5dEhEeT+slOu/SwMEucbYd0YFidxG3KlGPZaE= -github.com/quic-go/quic-go v0.38.1/go.mod h1:ijnZM7JsFIkp4cRyjxJNIzdSfCLmUMg9wdyhGmg+SN4= -github.com/quic-go/webtransport-go v0.5.3 h1:5XMlzemqB4qmOlgIus5zB45AcZ2kCgCy2EptUrfOPWU= -github.com/quic-go/webtransport-go v0.5.3/go.mod h1:OhmmgJIzTTqXK5xvtuX0oBpLV2GkLWNDA+UeTGJXErU= +github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs= +github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= +github.com/quic-go/quic-go v0.40.0 h1:GYd1iznlKm7dpHD7pOVpUvItgMPo/jrMgDWZhMCecqw= +github.com/quic-go/quic-go v0.40.0/go.mod h1:PeN7kuVJ4xZbxSv/4OX6S1USOX8MJvydwpTx31vx60c= +github.com/quic-go/webtransport-go v0.6.0 h1:CvNsKqc4W2HljHJnoT+rMmbRJybShZ0YPFDD3NxaZLY= +github.com/quic-go/webtransport-go v0.6.0/go.mod h1:9KjU4AEBqEQidGHNDkZrb8CAa1abRaosM2yGOyiikEc= github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk= github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU= github.com/rhnvrm/simples3 v0.6.1/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA= @@ -668,10 +666,11 @@ 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= -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= @@ -680,23 +679,25 @@ go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/ go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= -go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= -go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= -go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= -go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= -go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= +go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= +go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= +go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= +go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= +go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= +go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 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/dig v1.17.1 h1:Tga8Lz8PcYNsWsyHMZ1Vm0OQOUaJNDyvPImgbAu9YSc= go.uber.org/dig v1.17.1/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE= -go.uber.org/fx v1.20.0 h1:ZMC/pnRvhsthOZh9MZjMq5U8Or3mA9zBSPaLnzs3ihQ= -go.uber.org/fx v1.20.0/go.mod h1:qCUj0btiR3/JnanEr1TYEePfSw6o/4qYJscgvzQ5Ub0= +go.uber.org/fx v1.20.1 h1:zVwVQGS8zYvhh9Xxcu4w1M6ESyeMzebzj2NbSayZ4Mk= +go.uber.org/fx v1.20.1/go.mod h1:iSYNbHf2y55acNCwCXKx7LbWb5WG1Bnue5RDXz1OREg= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= 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/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo= +go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= @@ -720,14 +721,13 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -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-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= -golang.org/x/image v0.11.0 h1:ds2RoQvBvYTiJkwpSFDwCcDFNX7DqjL2WsUgTNk0Ooo= -golang.org/x/image v0.11.0/go.mod h1:bglhjqbqVuEb9e9+eNR45Jfu7D+T4Qan+NhQk8Ck2P8= +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/image v0.13.0 h1:3cge/F/QTkNLauhf2QoE9zp+7sr+ZcL4HnoZmdwg9sg= +golang.org/x/image v0.13.0/go.mod h1:6mmbMOeV28HuMTgA6OSRkdXKYw/t5W9Uwn2Yv1r3Yxk= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 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= @@ -740,10 +740,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB 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/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= +golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -767,8 +765,6 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v 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.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 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= @@ -786,8 +782,6 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ 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.1.0/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-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -825,22 +819,16 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w 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-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/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.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -848,9 +836,6 @@ 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.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 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-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -875,19 +860,16 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn 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.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= +golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= 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= -gonum.org/v1/gonum v0.13.0 h1:a0T3bh+7fhRyqeNbiC3qVHYmkiQgit3wnNan/2c0HMM= -gonum.org/v1/gonum v0.13.0/go.mod h1:/WPYRckkfWrhWefxyYTfrTtQR0KH4iyHNuzxqXAKyAU= +gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0= +gonum.org/v1/gonum v0.14.0/go.mod h1:AoWeoz0becf9QMWtE8iWXNXc27fK4fNeHNf/oMejGfU= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= @@ -905,8 +887,8 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98 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/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b h1:ZlWIi1wSK56/8hn4QcBp/j9M7Gt3U/3hZw3mC7vDICo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 h1:AB/lmRny7e2pLhFEYIbl5qkDAUt2h0ZRO4wGPhZf+ik= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405/go.mod h1:67X1fPuzjcrkymZzZV1vvkFeTn2Rvc6lYF9MYFGCcwE= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= diff --git a/tools/genesis-snapshot/go.mod b/tools/genesis-snapshot/go.mod index f16624ac1..02b4a5532 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-20231102135205-0f3d1e75042a github.com/iotaledger/hive.go/runtime v0.0.0-20231102135205-0f3d1e75042a github.com/iotaledger/iota-core v0.0.0-00010101000000-000000000000 - github.com/iotaledger/iota.go/v4 v4.0.0-20231019174124-aa2290512bcd + github.com/iotaledger/iota.go/v4 v4.0.0-20231102113728-20b8d01e826e github.com/mr-tron/base58 v1.2.0 github.com/spf13/pflag v1.0.5 golang.org/x/crypto v0.14.0 @@ -22,7 +22,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/ethereum/go-ethereum v1.13.4 // indirect - github.com/google/uuid v1.3.1 // indirect + github.com/google/uuid v1.4.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 @@ -39,11 +39,11 @@ require ( github.com/klauspost/cpuid/v2 v2.2.5 // 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.30.0 // indirect + github.com/libp2p/go-libp2p v0.32.0 // indirect github.com/minio/sha256-simd v1.0.1 // 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.11.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 @@ -62,7 +62,7 @@ require ( go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect - golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect + golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect golang.org/x/sync v0.4.0 // indirect golang.org/x/sys v0.13.0 // indirect google.golang.org/protobuf v1.31.0 // indirect diff --git a/tools/genesis-snapshot/go.sum b/tools/genesis-snapshot/go.sum index a0f58ae5f..9426e4008 100644 --- a/tools/genesis-snapshot/go.sum +++ b/tools/genesis-snapshot/go.sum @@ -20,8 +20,8 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 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/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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= @@ -54,8 +54,8 @@ github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231102135205-0f3d1e7 github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231102135205-0f3d1e75042a/go.mod h1:SdK26z8/VhWtxaqCuQrufm80SELgowQPmu9T/8eUQ8g= github.com/iotaledger/hive.go/stringify v0.0.0-20231102135205-0f3d1e75042a h1:DNcRsEdvfnrfwh9Mb19lHfsz5Izf9BiMqJILYYW0SvU= github.com/iotaledger/hive.go/stringify v0.0.0-20231102135205-0f3d1e75042a/go.mod h1:FTo/UWzNYgnQ082GI9QVM9HFDERqf9rw9RivNpqrnTs= -github.com/iotaledger/iota.go/v4 v4.0.0-20231019174124-aa2290512bcd h1:FKiEzy3gOyWDGXs6mTMS9qFHGXSxH97N5w+08ljbEtU= -github.com/iotaledger/iota.go/v4 v4.0.0-20231019174124-aa2290512bcd/go.mod h1:XFwcDfAF0hv6wB/trCsyDmQyPLjEZ60yTrllJ2bprN8= +github.com/iotaledger/iota.go/v4 v4.0.0-20231102113728-20b8d01e826e h1:ZYRC1MHn/ghsqtjIpYGTxLQrh5n5eUmC0/YWnJiTRhk= +github.com/iotaledger/iota.go/v4 v4.0.0-20231102113728-20b8d01e826e/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= @@ -66,8 +66,8 @@ 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.30.0 h1:9EZwFtJPFBcs/yJTnP90TpN1hgrT/EsFfM+OZuwV87U= -github.com/libp2p/go-libp2p v0.30.0/go.mod h1:nr2g5V7lfftwgiJ78/HrID+pwvayLyqKCEirT2Y3Byg= +github.com/libp2p/go-libp2p v0.32.0 h1:86I4B7nBUPIyTgw3+5Ibq6K7DdKRCuZw8URCfPc1hQM= +github.com/libp2p/go-libp2p v0.32.0/go.mod h1:hXXC3kXPlBZ1eu8Q2hptGrMB4mZ3048JUoS4EKaHW5c= 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/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= @@ -76,8 +76,8 @@ github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aG 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.11.0 h1:XqGyJ8ufbCE0HmTDwx2kPdsrQ36AGPZNZX6s6xfJH10= -github.com/multiformats/go-multiaddr v0.11.0/go.mod h1:gWUm0QLR4thQ6+ZF6SXUw8YjtwQSPapICM+NmCkxHSM= +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= @@ -135,8 +135,8 @@ 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.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +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/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.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/tools/genesis-snapshot/main.go b/tools/genesis-snapshot/main.go index a8d292a87..63d303a83 100644 --- a/tools/genesis-snapshot/main.go +++ b/tools/genesis-snapshot/main.go @@ -8,7 +8,8 @@ import ( "github.com/iotaledger/hive.go/ierrors" "github.com/iotaledger/hive.go/runtime/options" - "github.com/iotaledger/iota-core/pkg/protocol/snapshotcreator" + "github.com/iotaledger/iota-core/pkg/testsuite/mock" + "github.com/iotaledger/iota-core/pkg/testsuite/snapshotcreator" "github.com/iotaledger/iota-core/tools/genesis-snapshot/presets" ) @@ -49,7 +50,8 @@ func parseFlags() (opt []options.Option[snapshotcreator.Options], conf string) { if err != nil { log.Fatal(ierrors.Errorf("failed to decode base58 seed, using the default one: %w", err)) } - opt = append(opt, snapshotcreator.WithGenesisSeed(genesisSeed)) + keyManager := mock.NewKeyManager(genesisSeed[:], 0) + opt = append(opt, snapshotcreator.WithGenesisKeyManager(keyManager)) } return opt, *config diff --git a/tools/genesis-snapshot/presets/presets.go b/tools/genesis-snapshot/presets/presets.go index b97e6069a..29babde9f 100644 --- a/tools/genesis-snapshot/presets/presets.go +++ b/tools/genesis-snapshot/presets/presets.go @@ -9,8 +9,8 @@ import ( "github.com/iotaledger/hive.go/lo" "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/testsuite" + "github.com/iotaledger/iota-core/pkg/testsuite/mock" + "github.com/iotaledger/iota-core/pkg/testsuite/snapshotcreator" iotago "github.com/iotaledger/iota.go/v4" "github.com/iotaledger/iota.go/v4/hexutil" ) @@ -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,82 +36,105 @@ 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-01-validator + + Ed25519 Public Key: 293dc170d9a59474e6d81cfba7f7d924c09b25d7166bcfba606e53114d0a758b + Ed25519 Address: rms1qzg8cqhfxqhq7pt37y8cs4v5u4kcc48lquy2k73ehsdhf5ukhya3ytgk0ny + Account Address: rms1pzg8cqhfxqhq7pt37y8cs4v5u4kcc48lquy2k73ehsdhf5ukhya3y5rx2w6 + Restricted Address: rms1xqqfqlqzayczurc9w8cslzz4jnjkmrz5lurs32m68x7pkaxnj6unkyspqg8mulpm, Capabilities: mana + */ AccountID: blake2b.Sum256(lo.PanicOnErr(hexutil.DecodeHex("0x293dc170d9a59474e6d81cfba7f7d924c09b25d7166bcfba606e53114d0a758b"))), Address: iotago.Ed25519AddressFromPubKey(lo.PanicOnErr(hexutil.DecodeHex("0x293dc170d9a59474e6d81cfba7f7d924c09b25d7166bcfba606e53114d0a758b"))), - Amount: testsuite.MinValidatorAccountAmount, + Amount: mock.MinValidatorAccountAmount, IssuerKey: iotago.Ed25519PublicKeyBlockIssuerKeyFromPublicKey(ed25519.PublicKey(lo.PanicOnErr(hexutil.DecodeHex("0x293dc170d9a59474e6d81cfba7f7d924c09b25d7166bcfba606e53114d0a758b")))), ExpirySlot: iotago.MaxSlotIndex, BlockIssuanceCredits: iotago.MaxBlockIssuanceCredits / 4, StakingEpochEnd: iotago.MaxEpochIndex, FixedCost: 1, - StakedAmount: testsuite.MinValidatorAccountAmount, - Mana: iotago.Mana(testsuite.MinValidatorAccountAmount), + StakedAmount: mock.MinValidatorAccountAmount, + Mana: iotago.Mana(mock.MinValidatorAccountAmount), }, - snapshotcreator.AccountDetails{ // validator-2 + snapshotcreator.AccountDetails{ + /* + node-02-validator + + Ed25519 Public Key: 05c1de274451db8de8182d64c6ee0dca3ae0c9077e0b4330c976976171d79064 + Ed25519 Address: rms1qqm4xk8e9ny5w5rxjkvtp249tfhlwvcshyr3pc0665jvp7g3hc875flpz2p + Account Address: rms1pqm4xk8e9ny5w5rxjkvtp249tfhlwvcshyr3pc0665jvp7g3hc875k538hl + Restricted Address: rms1xqqrw56clykvj36sv62e3v9254dxlaenzzuswy8plt2jfs8ezxlql6spqgkulf7u, Capabilities: mana + */ AccountID: blake2b.Sum256(lo.PanicOnErr(hexutil.DecodeHex("0x05c1de274451db8de8182d64c6ee0dca3ae0c9077e0b4330c976976171d79064"))), Address: iotago.Ed25519AddressFromPubKey(lo.PanicOnErr(hexutil.DecodeHex("0x05c1de274451db8de8182d64c6ee0dca3ae0c9077e0b4330c976976171d79064"))), - Amount: testsuite.MinValidatorAccountAmount, + Amount: mock.MinValidatorAccountAmount, IssuerKey: iotago.Ed25519PublicKeyBlockIssuerKeyFromPublicKey(ed25519.PublicKey(lo.PanicOnErr(hexutil.DecodeHex("0x05c1de274451db8de8182d64c6ee0dca3ae0c9077e0b4330c976976171d79064")))), ExpirySlot: iotago.MaxSlotIndex, BlockIssuanceCredits: iotago.MaxBlockIssuanceCredits / 4, StakingEpochEnd: iotago.MaxEpochIndex, FixedCost: 1, - StakedAmount: testsuite.MinValidatorAccountAmount, - Mana: iotago.Mana(testsuite.MinValidatorAccountAmount), + StakedAmount: mock.MinValidatorAccountAmount, + Mana: iotago.Mana(mock.MinValidatorAccountAmount), }, - snapshotcreator.AccountDetails{ // validator-3 + snapshotcreator.AccountDetails{ + /* + node-03-validator + + Ed25519 Public Key: 1e4b21eb51dcddf65c20db1065e1f1514658b23a3ddbf48d30c0efc926a9a648 + Ed25519 Address: rms1qp4wuuz0y42caz48vv876qfpmffswsvg40zz8v79sy8cp0jfxm4kuvz0a44 + Account Address: rms1pp4wuuz0y42caz48vv876qfpmffswsvg40zz8v79sy8cp0jfxm4kunflcgt + Restricted Address: rms1xqqx4mnsfuj4tr525a3slmgpy8d9xp6p3z4ugganckqslq97fymwkmspqgnzrkjq, Capabilities: mana + */ AccountID: blake2b.Sum256(lo.PanicOnErr(hexutil.DecodeHex("0x1e4b21eb51dcddf65c20db1065e1f1514658b23a3ddbf48d30c0efc926a9a648"))), Address: iotago.Ed25519AddressFromPubKey(lo.PanicOnErr(hexutil.DecodeHex("0x1e4b21eb51dcddf65c20db1065e1f1514658b23a3ddbf48d30c0efc926a9a648"))), - Amount: testsuite.MinValidatorAccountAmount, + Amount: mock.MinValidatorAccountAmount, IssuerKey: iotago.Ed25519PublicKeyBlockIssuerKeyFromPublicKey(ed25519.PublicKey(lo.PanicOnErr(hexutil.DecodeHex("0x1e4b21eb51dcddf65c20db1065e1f1514658b23a3ddbf48d30c0efc926a9a648")))), ExpirySlot: iotago.MaxSlotIndex, BlockIssuanceCredits: iotago.MaxBlockIssuanceCredits / 4, StakingEpochEnd: iotago.MaxEpochIndex, FixedCost: 1, - StakedAmount: testsuite.MinValidatorAccountAmount, - Mana: iotago.Mana(testsuite.MinValidatorAccountAmount), + StakedAmount: mock.MinValidatorAccountAmount, + Mana: iotago.Mana(mock.MinValidatorAccountAmount), }, snapshotcreator.AccountDetails{ /* inx-blockissuer - ed25519 private key: 432c624ca3260f910df35008d5c740593b222f1e196e6cdb8cd1ad080f0d4e33997be92a22b1933f36e26fba5f721756f95811d6b4ae21564197c2bfa4f28270 - ed25519 public key: 997be92a22b1933f36e26fba5f721756f95811d6b4ae21564197c2bfa4f28270 - ed25519 address: edc1c3a42a60a04b69c2fbb6e886414c71c464afbc7d19fb63b36a8065334ada - bech32 address: rms1prkursay9fs2qjmfctamd6yxg9x8r3ry47786x0mvwek4qr9xd9d5c6gkun + Ed25519 Private Key: 432c624ca3260f910df35008d5c740593b222f1e196e6cdb8cd1ad080f0d4e33997be92a22b1933f36e26fba5f721756f95811d6b4ae21564197c2bfa4f28270 + Ed25519 Public Key: 997be92a22b1933f36e26fba5f721756f95811d6b4ae21564197c2bfa4f28270 + Ed25519 Address: rms1qrkursay9fs2qjmfctamd6yxg9x8r3ry47786x0mvwek4qr9xd9d583cnpd + Account Address: rms1prkursay9fs2qjmfctamd6yxg9x8r3ry47786x0mvwek4qr9xd9d5c6gkun + Restricted Address: rms1xqqwmswr5s4xpgztd8p0hdhgseq5cuwyvjhmclgeld3mx65qv5e54kspqgda0nrn, Capabilities: mana */ AccountID: blake2b.Sum256(lo.PanicOnErr(hexutil.DecodeHex("0x997be92a22b1933f36e26fba5f721756f95811d6b4ae21564197c2bfa4f28270"))), Address: iotago.Ed25519AddressFromPubKey(lo.PanicOnErr(hexutil.DecodeHex("0x997be92a22b1933f36e26fba5f721756f95811d6b4ae21564197c2bfa4f28270"))), - Amount: testsuite.MinIssuerAccountAmount, + Amount: mock.MinIssuerAccountAmount, IssuerKey: iotago.Ed25519PublicKeyBlockIssuerKeyFromPublicKey(ed25519.PublicKey(lo.PanicOnErr(hexutil.DecodeHex("0x997be92a22b1933f36e26fba5f721756f95811d6b4ae21564197c2bfa4f28270")))), ExpirySlot: iotago.MaxSlotIndex, BlockIssuanceCredits: iotago.MaxBlockIssuanceCredits / 4, - Mana: iotago.Mana(testsuite.MinIssuerAccountAmount), + Mana: iotago.Mana(mock.MinIssuerAccountAmount), + }, + ), + snapshotcreator.WithBasicOutputs( + /* + inx-faucet + + Ed25519 Private Key: de52b9964dda96564e9fab362ab16c2669c715c6a2a853bece8a25fc58c599755b938327ea463e0c323c0fd44f6fc1843ed94daecc6909c6043d06b7152e4737 + Ed25519 Public Key: 5b938327ea463e0c323c0fd44f6fc1843ed94daecc6909c6043d06b7152e4737 + Ed25519 Address: rms1qqhkf7w30xv375z59vq7qd86qsa3j4qrsadcval04uvkkswg3qpaqf4hga2 + Restricted Address: rms1xqqz7e8e69uej86s2s4srcp5lgzrkx25qwr4hpnha7h3j66pezyq85qpqg55v3ur, Capabilities: mana + */ + 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 @@ -126,46 +149,101 @@ var Feature = []options.Option[snapshotcreator.Options]{ snapshotcreator.WithFilePath("docker-network.snapshot"), snapshotcreator.WithAccounts( snapshotcreator.AccountDetails{ + /* + node-01-validator + + Ed25519 Public Key: 01fb6b9db5d96240aef00bc950d1c67a6494513f6d7cf784e57b4972b96ab2fe + Ed25519 Address: rms1qqlhggrg2ml9p0q5c4593r2yd3jwgxn20d65ulyw6z9r7xmm78apq4y2mxh + Account Address: rms1pqlhggrg2ml9p0q5c4593r2yd3jwgxn20d65ulyw6z9r7xmm78apq2067mf + Restricted Address: rms1xqqr7apqdpt0u59uznzkskydg3kxfeq6dfah2nnu3mgg50cm00cl5yqpqgrpy62q, Capabilities: mana + */ AccountID: blake2b.Sum256(lo.PanicOnErr(hexutil.DecodeHex("0x01fb6b9db5d96240aef00bc950d1c67a6494513f6d7cf784e57b4972b96ab2fe"))), Address: iotago.Ed25519AddressFromPubKey(lo.PanicOnErr(hexutil.DecodeHex("0x01fb6b9db5d96240aef00bc950d1c67a6494513f6d7cf784e57b4972b96ab2fe"))), - Amount: testsuite.MinValidatorAccountAmount, + Amount: mock.MinValidatorAccountAmount, IssuerKey: iotago.Ed25519PublicKeyBlockIssuerKeyFromPublicKey(ed25519.PublicKey(lo.PanicOnErr(hexutil.DecodeHex("0x01fb6b9db5d96240aef00bc950d1c67a6494513f6d7cf784e57b4972b96ab2fe")))), ExpirySlot: iotago.MaxSlotIndex, BlockIssuanceCredits: iotago.MaxBlockIssuanceCredits / 4, StakingEpochEnd: iotago.MaxEpochIndex, FixedCost: 1, - StakedAmount: testsuite.MinValidatorAccountAmount, - Mana: iotago.Mana(testsuite.MinValidatorAccountAmount), + StakedAmount: mock.MinValidatorAccountAmount, + Mana: iotago.Mana(mock.MinValidatorAccountAmount), }, snapshotcreator.AccountDetails{ + /* + node-02-validator + + Ed25519 Public Key: 83e7f71a440afd48981a8b4684ddae24434b7182ce5c47cfb56ac528525fd4b6 + Ed25519 Address: rms1qzjwamvhjuqtw3dkfwmmj2fgcetcdyt4uxrnjxel4caxfstzz903ypu8xvn + Account Address: rms1pzjwamvhjuqtw3dkfwmmj2fgcetcdyt4uxrnjxel4caxfstzz903y7hhr3d + Restricted Address: rms1xqq2fmhdj7tspd69ke9m0wff9rr90p53whscwwgm87hr5expvgg47yspqgm6whkx, Capabilities: mana + */ AccountID: blake2b.Sum256(lo.PanicOnErr(hexutil.DecodeHex("0x83e7f71a440afd48981a8b4684ddae24434b7182ce5c47cfb56ac528525fd4b6"))), Address: iotago.Ed25519AddressFromPubKey(lo.PanicOnErr(hexutil.DecodeHex("0x83e7f71a440afd48981a8b4684ddae24434b7182ce5c47cfb56ac528525fd4b6"))), - Amount: testsuite.MinValidatorAccountAmount, + Amount: mock.MinValidatorAccountAmount, IssuerKey: iotago.Ed25519PublicKeyBlockIssuerKeyFromPublicKey(ed25519.PublicKey(lo.PanicOnErr(hexutil.DecodeHex("0x83e7f71a440afd48981a8b4684ddae24434b7182ce5c47cfb56ac528525fd4b6")))), ExpirySlot: iotago.MaxSlotIndex, BlockIssuanceCredits: iotago.MaxBlockIssuanceCredits / 4, StakingEpochEnd: iotago.MaxEpochIndex, FixedCost: 1, - StakedAmount: testsuite.MinValidatorAccountAmount, - Mana: iotago.Mana(testsuite.MinValidatorAccountAmount), + StakedAmount: mock.MinValidatorAccountAmount, + Mana: iotago.Mana(mock.MinValidatorAccountAmount), }, snapshotcreator.AccountDetails{ + /* + node-03-validator + + Ed25519 Public Key: ac628986b2ef52a1679f2289fcd7b4198476976dea4c30ae34ff04ae52e14805 + Ed25519 Address: rms1qz6kedkxyw9md2cmp0wcdhvsxrn2e7gzuyly76ffymy4dhvtkm58qlkjupg + Account Address: rms1pz6kedkxyw9md2cmp0wcdhvsxrn2e7gzuyly76ffymy4dhvtkm58qqazeuk + Restricted Address: rms1xqqt2m9kcc3chd4trv9ampkajqcwdt8eqtsnunmf9ynvj4ka3wmwsuqpqgvp9zxl, Capabilities: mana + */ AccountID: blake2b.Sum256(lo.PanicOnErr(hexutil.DecodeHex("0xac628986b2ef52a1679f2289fcd7b4198476976dea4c30ae34ff04ae52e14805"))), Address: iotago.Ed25519AddressFromPubKey(lo.PanicOnErr(hexutil.DecodeHex("0xac628986b2ef52a1679f2289fcd7b4198476976dea4c30ae34ff04ae52e14805"))), - Amount: testsuite.MinValidatorAccountAmount, + Amount: mock.MinValidatorAccountAmount, IssuerKey: iotago.Ed25519PublicKeyBlockIssuerKeyFromPublicKey(ed25519.PublicKey(lo.PanicOnErr(hexutil.DecodeHex("0xac628986b2ef52a1679f2289fcd7b4198476976dea4c30ae34ff04ae52e14805")))), ExpirySlot: iotago.MaxSlotIndex, BlockIssuanceCredits: iotago.MaxBlockIssuanceCredits / 4, StakingEpochEnd: iotago.MaxEpochIndex, FixedCost: 1, - StakedAmount: testsuite.MinValidatorAccountAmount, - Mana: iotago.Mana(testsuite.MinValidatorAccountAmount), + StakedAmount: mock.MinValidatorAccountAmount, + Mana: iotago.Mana(mock.MinValidatorAccountAmount), + }, + snapshotcreator.AccountDetails{ + /* + inx-blockissuer + + Ed25519 Public Key: 670a1a20ddb02a6cec53ec3196bc7d5bd26df2f5a6ca90b5fffd71364f104b25 + Ed25519 Address: rms1qqas0clgfsf8du9e6dw0yx9nwclqe0dd4f728pvgmcshpscm8r5mkjxnx5x + Account Address: rms1pqas0clgfsf8du9e6dw0yx9nwclqe0dd4f728pvgmcshpscm8r5mkddrrfc + Restricted Address: rms1xqqrkplrapxpyahsh8f4eusckdmrur9a4k48egu93r0zzuxrrvuwnwcpqgj0nu8t, Capabilities: mana + */ + AccountID: blake2b.Sum256(lo.PanicOnErr(hexutil.DecodeHex("0x670a1a20ddb02a6cec53ec3196bc7d5bd26df2f5a6ca90b5fffd71364f104b25"))), + Address: iotago.Ed25519AddressFromPubKey(lo.PanicOnErr(hexutil.DecodeHex("0x670a1a20ddb02a6cec53ec3196bc7d5bd26df2f5a6ca90b5fffd71364f104b25"))), + Amount: mock.MinIssuerAccountAmount, + IssuerKey: iotago.Ed25519PublicKeyBlockIssuerKeyFromPublicKey(ed25519.PublicKey(lo.PanicOnErr(hexutil.DecodeHex("0x670a1a20ddb02a6cec53ec3196bc7d5bd26df2f5a6ca90b5fffd71364f104b25")))), + ExpirySlot: iotago.MaxSlotIndex, + BlockIssuanceCredits: iotago.MaxBlockIssuanceCredits / 4, + Mana: iotago.Mana(mock.MinIssuerAccountAmount), + }, + ), + snapshotcreator.WithBasicOutputs( + /* + inx-faucet + + Ed25519 Public Key: dcd760a51cfafe901f4ca0745d399af7146028af643e8a339c7bb82fbb1be7f9 + Ed25519 Address: rms1qpy2e4my7cn9ydjx6hx0ytuq06tdxzm6kqry7dctvmagzxv9np0vg9c55n4 + Restricted Address: rms1xqqy3txhvnmzv53kgm2ueu30splfd5ct02cqvnehpdn04qgeskv9a3qpqgrhlhv3, Capabilities: mana + */ + 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