diff --git a/components/restapi/core/accounts.go b/components/restapi/core/accounts.go index 48f51361e..5e7a4babf 100644 --- a/components/restapi/core/accounts.go +++ b/components/restapi/core/accounts.go @@ -11,7 +11,7 @@ import ( ) func congestionByAccountAddress(c echo.Context) (*api.CongestionResponse, error) { - commitmentID, err := httpserver.ParseCommitmentIDQueryParam(c, api.ParameterCommitmentID) + queryCommitmentID, err := httpserver.ParseCommitmentIDQueryParam(c, api.ParameterCommitmentID) if err != nil { return nil, err } @@ -26,8 +26,19 @@ func congestionByAccountAddress(c echo.Context) (*api.CongestionResponse, error) if workScore != 0 { workScores = append(workScores, workScore) } + maxCommittableAge := deps.RequestHandler.CommittedAPI().ProtocolParameters().MaxCommittableAge() + latestCommittedSlot := deps.RequestHandler.GetLatestCommitment().Slot() + if queryCommitmentID != iotago.EmptyCommitmentID { + if latestCommittedSlot >= maxCommittableAge && queryCommitmentID.Slot()+maxCommittableAge < latestCommittedSlot { + return nil, ierrors.Wrapf(echo.ErrBadRequest, "invalid commitmentID, target slot index older than allowed (%d<%d)", queryCommitmentID.Slot(), latestCommittedSlot-maxCommittableAge) + } + + if queryCommitmentID.Slot() > latestCommittedSlot { + return nil, ierrors.Wrapf(echo.ErrBadRequest, "invalid commitmentID, slot %d is not committed yet, latest committed slot: %d", queryCommitmentID.Slot(), latestCommittedSlot) + } + } - commitment, err := deps.RequestHandler.GetCommitmentByID(commitmentID) + queryCommittment, err := deps.RequestHandler.GetCommitmentByID(queryCommitmentID) if err != nil { return nil, err } @@ -43,7 +54,7 @@ func congestionByAccountAddress(c echo.Context) (*api.CongestionResponse, error) return nil, ierrors.Wrapf(httpserver.ErrInvalidParameter, "address %s is not an account address", c.Param(api.ParameterBech32Address)) } - return deps.RequestHandler.CongestionByAccountAddress(accountAddress, commitment, workScores...) + return deps.RequestHandler.CongestionByAccountAddress(accountAddress, queryCommittment, workScores...) } func validators(c echo.Context) (*api.ValidatorsResponse, error) { @@ -61,10 +72,6 @@ func validators(c echo.Context) (*api.ValidatorsResponse, error) { } } - // do not respond to really old requests - if requestedSlot+iotago.SlotIndex(restapi.ParamsRestAPI.MaxRequestedSlotAge) < latestCommittedSlot { - return nil, ierrors.Wrapf(echo.ErrBadRequest, "request is too old, request started at %d, latest committed slot index is %d", requestedSlot, latestCommittedSlot) - } slotRange := uint32(requestedSlot) / restapi.ParamsRestAPI.RequestsMemoryCacheGranularity return deps.RequestHandler.Validators(slotRange, cursorIndex, pageSize) @@ -86,6 +93,7 @@ func validatorByAccountAddress(c echo.Context) (*api.ValidatorResponse, error) { } func rewardsByOutputID(c echo.Context) (*api.ManaRewardsResponse, error) { + var err error outputID, err := httpserver.ParseOutputIDParam(c, api.ParameterOutputID) if err != nil { return nil, ierrors.Wrapf(err, "failed to parse output ID %s", c.Param(api.ParameterOutputID)) @@ -93,7 +101,6 @@ func rewardsByOutputID(c echo.Context) (*api.ManaRewardsResponse, error) { var slot iotago.SlotIndex if len(c.QueryParam(api.ParameterSlot)) > 0 { - var err error slot, err = httpserver.ParseSlotQueryParam(c, api.ParameterSlot) if err != nil { return nil, ierrors.Wrapf(err, "failed to parse slot index %s", c.Param(api.ParameterSlot)) @@ -113,6 +120,7 @@ func rewardsByOutputID(c echo.Context) (*api.ManaRewardsResponse, error) { func selectedCommittee(c echo.Context) (*api.CommitteeResponse, error) { var epoch iotago.EpochIndex + if len(c.QueryParam(api.ParameterEpoch)) == 0 { // by default we return current epoch epoch = deps.RequestHandler.CommittedAPI().TimeProvider().CurrentEpoch() @@ -122,6 +130,10 @@ func selectedCommittee(c echo.Context) (*api.CommitteeResponse, error) { if err != nil { return nil, err } + currentEpoch := deps.RequestHandler.CommittedAPI().TimeProvider().CurrentEpoch() + if epoch > currentEpoch { + return nil, ierrors.Wrapf(echo.ErrBadRequest, "provided epoch %d is from the future, current epoch: %d", epoch, currentEpoch) + } } return deps.RequestHandler.SelectedCommittee(epoch) diff --git a/components/restapi/core/component.go b/components/restapi/core/component.go index cb887c005..46fd270b8 100644 --- a/components/restapi/core/component.go +++ b/components/restapi/core/component.go @@ -222,7 +222,7 @@ func configure() error { } return responseByHeader(c, resp) - }) + }, checkNodeSynced()) routeGroup.GET(api.EndpointWithEchoParameters(api.CoreEndpointOutputWithMetadata), func(c echo.Context) error { resp, err := outputWithMetadataFromOutputID(c) @@ -231,7 +231,7 @@ func configure() error { } return responseByHeader(c, resp) - }) + }, checkNodeSynced()) routeGroup.GET(api.EndpointWithEchoParameters(api.CoreEndpointTransactionsIncludedBlock), func(c echo.Context) error { block, err := blockFromTransactionID(c) diff --git a/go.mod b/go.mod index fef212768..e6015c7bd 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/iotaledger/hive.go/stringify v0.0.0-20240307102857-7e23a3c59bd2 github.com/iotaledger/inx-app v1.0.0-rc.3.0.20240307101848-db58eb9353ec github.com/iotaledger/inx/go v1.0.0-rc.2.0.20240307100839-48553e1d2022 - github.com/iotaledger/iota.go/v4 v4.0.0-20240307091827-db3c503615a6 + github.com/iotaledger/iota.go/v4 v4.0.0-20240307175623-0904c71fcb38 github.com/labstack/echo/v4 v4.11.4 github.com/labstack/gommon v0.4.2 github.com/libp2p/go-libp2p v0.33.0 diff --git a/go.sum b/go.sum index d2c2830d2..1c366dc1c 100644 --- a/go.sum +++ b/go.sum @@ -318,8 +318,8 @@ github.com/iotaledger/inx/go v1.0.0-rc.2.0.20240307100839-48553e1d2022 h1:I178Sa github.com/iotaledger/inx/go v1.0.0-rc.2.0.20240307100839-48553e1d2022/go.mod h1:jTFxIWiMUdAwO263jlJCSWcNLqEkgYEVOFXfjp5aNJM= github.com/iotaledger/iota-crypto-demo v0.0.0-20240216103559-27ca8dffd1e7 h1:t6k4MqiUov0FrBb2o2JhKlOVSdlPbIQWM8ivYHL0G0g= github.com/iotaledger/iota-crypto-demo v0.0.0-20240216103559-27ca8dffd1e7/go.mod h1:do+N3LpeDEi9qselEC4XcjqGoRc7cWGiqBtIeBOKEMs= -github.com/iotaledger/iota.go/v4 v4.0.0-20240307091827-db3c503615a6 h1:dzOaHlQDJecS3SDqwaUDQfTYjZDvwqRdn1/6bCjFpgM= -github.com/iotaledger/iota.go/v4 v4.0.0-20240307091827-db3c503615a6/go.mod h1:8UQOTI7CC5R/3TurawUFuBZbkb37RzW8m4q8Hp7ct30= +github.com/iotaledger/iota.go/v4 v4.0.0-20240307175623-0904c71fcb38 h1:NizJ3CALLCcJowtAtkNuDlpE4gd4qjaWZkp/kTZfeYk= +github.com/iotaledger/iota.go/v4 v4.0.0-20240307175623-0904c71fcb38/go.mod h1:8UQOTI7CC5R/3TurawUFuBZbkb37RzW8m4q8Hp7ct30= github.com/ipfs/boxo v0.18.0 h1:MOL9/AgoV3e7jlVMInicaSdbgralfqSsbkc31dZ9tmw= github.com/ipfs/boxo v0.18.0/go.mod h1:pIZgTWdm3k3pLF9Uq6MB8JEcW07UDwNJjlXW1HELW80= github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= diff --git a/pkg/requesthandler/accounts.go b/pkg/requesthandler/accounts.go index 1d463cf15..4317ed163 100644 --- a/pkg/requesthandler/accounts.go +++ b/pkg/requesthandler/accounts.go @@ -7,6 +7,7 @@ import ( "github.com/labstack/echo/v4" "github.com/iotaledger/hive.go/ierrors" + "github.com/iotaledger/hive.go/kvstore" "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/iota-core/pkg/core/account" "github.com/iotaledger/iota-core/pkg/model" @@ -100,6 +101,10 @@ func (r *RequestHandler) ValidatorByAccountAddress(accountAddress *iotago.Accoun func (r *RequestHandler) RewardsByOutputID(outputID iotago.OutputID, slot iotago.SlotIndex) (*api.ManaRewardsResponse, error) { utxoOutput, err := r.protocol.Engines.Main.Get().Ledger.Output(outputID) if err != nil { + if ierrors.Is(err, kvstore.ErrKeyNotFound) { + return nil, ierrors.Wrapf(echo.ErrNotFound, "output %s not found", outputID.ToHex()) + } + return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to get output %s from ledger: %s", outputID.ToHex(), err) } @@ -156,16 +161,19 @@ func (r *RequestHandler) RewardsByOutputID(outputID iotago.OutputID, slot iotago delegationEnd, claimingEpoch, ) + default: + return nil, ierrors.Wrapf(echo.ErrBadRequest, "output %s is neither a delegation output nor account", outputID.ToHex()) } + if err != nil { return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to calculate reward for output %s: %s", outputID.ToHex(), err) } latestCommittedEpochPoolRewards, poolRewardExists, err := r.protocol.Engines.Main.Get().SybilProtection.PoolRewardsForAccount(stakingPoolValidatorAccountID) - if err != nil { return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to retrieve pool rewards for account %s: %s", stakingPoolValidatorAccountID.ToHex(), err) } + if !poolRewardExists { latestCommittedEpochPoolRewards = 0 } diff --git a/pkg/requesthandler/blocks.go b/pkg/requesthandler/blocks.go index 1838848d9..7d42b94e5 100644 --- a/pkg/requesthandler/blocks.go +++ b/pkg/requesthandler/blocks.go @@ -6,6 +6,7 @@ import ( "github.com/labstack/echo/v4" "github.com/iotaledger/hive.go/ierrors" + "github.com/iotaledger/hive.go/kvstore" "github.com/iotaledger/inx-app/pkg/httpserver" iotago "github.com/iotaledger/iota.go/v4" "github.com/iotaledger/iota.go/v4/api" @@ -23,6 +24,10 @@ func (r *RequestHandler) BlockByID(blockID iotago.BlockID) (*iotago.Block, error func (r *RequestHandler) BlockMetadataByBlockID(blockID iotago.BlockID) (*api.BlockMetadataResponse, error) { blockMetadata, err := r.protocol.Engines.Main.Get().BlockRetainer.BlockMetadata(blockID) if err != nil { + if ierrors.Is(err, kvstore.ErrKeyNotFound) { + return nil, ierrors.Wrapf(echo.ErrNotFound, "block not found: %s", blockID.ToHex()) + } + return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to get block metadata %s: %s", blockID.ToHex(), err) } diff --git a/pkg/requesthandler/commitments.go b/pkg/requesthandler/commitments.go index 5cdcf6abc..928f80945 100644 --- a/pkg/requesthandler/commitments.go +++ b/pkg/requesthandler/commitments.go @@ -19,7 +19,7 @@ func (r *RequestHandler) GetCommitmentBySlot(slot iotago.SlotIndex) (*model.Comm commitment, err := r.protocol.Engines.Main.Get().Storage.Commitments().Load(slot) if err != nil { - return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to load commitment, slot: %d, error: %w", slot, err) + return nil, ierrors.Join(echo.ErrInternalServerError, ierrors.Wrapf(err, "failed to load commitment, slot: %d", slot)) } return commitment, nil @@ -38,11 +38,11 @@ func (r *RequestHandler) GetCommitmentByID(commitmentID iotago.CommitmentID) (*m commitment, err := r.protocol.Engines.Main.Get().Storage.Commitments().Load(commitmentID.Slot()) if err != nil { - return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to load commitment, commitmentID: %s, slot: %d, error: %w", commitmentID, commitmentID.Slot(), err) + return nil, ierrors.Join(echo.ErrInternalServerError, ierrors.Wrapf(err, "failed to load commitment, commitmentID: %s, slot: %d", commitmentID, commitmentID.Slot())) } if commitment.ID() != commitmentID { - return nil, ierrors.Wrapf(echo.ErrBadRequest, "commitment in the store for slot %d does not match the given commitmentID (%s != %s)", commitmentID.Slot(), commitment.ID(), commitmentID) + return nil, ierrors.Join(echo.ErrBadRequest, ierrors.Wrapf(err, "commitment in the store for slot %d does not match the given commitmentID (%s != %s)", commitmentID.Slot(), commitment.ID(), commitmentID)) } return commitment, nil @@ -55,7 +55,7 @@ func (r *RequestHandler) GetLatestCommitment() *model.Commitment { func (r *RequestHandler) GetUTXOChanges(commitmentID iotago.CommitmentID) (*api.UTXOChangesResponse, error) { diffs, err := r.protocol.Engines.Main.Get().Ledger.SlotDiffs(commitmentID.Slot()) if err != nil { - return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to get slot diffs, commitmentID: %s, slot: %d, error: %w", commitmentID, commitmentID.Slot(), err) + return nil, ierrors.Join(echo.ErrInternalServerError, ierrors.Wrapf(err, "failed to get slot diffs, commitmentID: %s, slot: %d", commitmentID, commitmentID.Slot())) } createdOutputs := make(iotago.OutputIDs, len(diffs.Outputs)) @@ -79,7 +79,7 @@ func (r *RequestHandler) GetUTXOChanges(commitmentID iotago.CommitmentID) (*api. func (r *RequestHandler) GetUTXOChangesFull(commitmentID iotago.CommitmentID) (*api.UTXOChangesFullResponse, error) { diffs, err := r.protocol.Engines.Main.Get().Ledger.SlotDiffs(commitmentID.Slot()) if err != nil { - return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to get slot diffs, commitmentID: %s, slot: %d, error: %w", commitmentID, commitmentID.Slot(), err) + return nil, ierrors.Join(echo.ErrInternalServerError, ierrors.Wrapf(err, "failed to get slot diffs, commitmentID: %s, slot: %d", commitmentID, commitmentID.Slot())) } createdOutputs := make([]*api.OutputWithID, len(diffs.Outputs)) diff --git a/pkg/requesthandler/utxo.go b/pkg/requesthandler/utxo.go index c9bd951df..e0b492d58 100644 --- a/pkg/requesthandler/utxo.go +++ b/pkg/requesthandler/utxo.go @@ -4,6 +4,7 @@ import ( "github.com/labstack/echo/v4" "github.com/iotaledger/hive.go/ierrors" + "github.com/iotaledger/hive.go/kvstore" "github.com/iotaledger/iota-core/pkg/protocol/engine/utxoledger" iotago "github.com/iotaledger/iota.go/v4" "github.com/iotaledger/iota.go/v4/api" @@ -12,6 +13,10 @@ import ( func (r *RequestHandler) OutputFromOutputID(outputID iotago.OutputID) (*api.OutputResponse, error) { output, err := r.protocol.Engines.Main.Get().Ledger.Output(outputID) if err != nil { + if ierrors.Is(err, kvstore.ErrKeyNotFound) { + return nil, ierrors.Wrapf(echo.ErrNotFound, "output %s not found in the Ledger", outputID.ToHex()) + } + return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to get output %s from the Ledger: %s", outputID.ToHex(), err) } @@ -24,6 +29,10 @@ func (r *RequestHandler) OutputFromOutputID(outputID iotago.OutputID) (*api.Outp func (r *RequestHandler) OutputMetadataFromOutputID(outputID iotago.OutputID) (*api.OutputMetadata, error) { output, spent, err := r.protocol.Engines.Main.Get().Ledger.OutputOrSpent(outputID) if err != nil { + if ierrors.Is(err, kvstore.ErrKeyNotFound) { + return nil, ierrors.Wrapf(echo.ErrNotFound, "output %s not found in the Ledger", outputID.ToHex()) + } + return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to get output %s from the Ledger: %s", outputID.ToHex(), err) } @@ -37,6 +46,10 @@ func (r *RequestHandler) OutputMetadataFromOutputID(outputID iotago.OutputID) (* func (r *RequestHandler) OutputWithMetadataFromOutputID(outputID iotago.OutputID) (*api.OutputWithMetadataResponse, error) { output, spent, err := r.protocol.Engines.Main.Get().Ledger.OutputOrSpent(outputID) if err != nil { + if ierrors.Is(err, kvstore.ErrKeyNotFound) { + return nil, ierrors.Wrapf(echo.ErrNotFound, "output %s not found in the Ledger", outputID.ToHex()) + } + return nil, ierrors.Wrapf(echo.ErrInternalServerError, "failed to get output %s from the Ledger: %s", outputID.ToHex(), err) } diff --git a/pkg/retainer/blockretainer/block_retainer.go b/pkg/retainer/blockretainer/block_retainer.go index 2a77371ab..27363bd06 100644 --- a/pkg/retainer/blockretainer/block_retainer.go +++ b/pkg/retainer/blockretainer/block_retainer.go @@ -105,18 +105,18 @@ func (r *BlockRetainer) getBlockMetadata(blockID iotago.BlockID) (*slotstore.Blo return nil, err } - data, found := store.BlockMetadata(blockID) - if !found { - return nil, ierrors.Errorf("block %s not found", blockID.String()) + data, err := store.BlockMetadata(blockID) + if err != nil { + return nil, ierrors.Wrapf(err, "block %s not found", blockID.String()) } return data, nil } func (r *BlockRetainer) BlockMetadata(blockID iotago.BlockID) (*api.BlockMetadataResponse, error) { - blockStatus := r.blockState(blockID) - if blockStatus == api.BlockStateUnknown { - return nil, ierrors.Errorf("block %s not found", blockID.ToHex()) + blockStatus, err := r.blockState(blockID) + if err != nil { + return nil, ierrors.Wrapf(err, "block %s not found", blockID.ToHex()) } // we do not expose accepted flag @@ -130,25 +130,24 @@ func (r *BlockRetainer) BlockMetadata(blockID iotago.BlockID) (*api.BlockMetadat }, nil } -func (r *BlockRetainer) blockState(blockID iotago.BlockID) api.BlockState { +func (r *BlockRetainer) blockState(blockID iotago.BlockID) (api.BlockState, error) { blockMetadata, err := r.getBlockMetadata(blockID) if err != nil { - r.errorHandler(ierrors.Wrapf(err, "could not get block data for slot %d", blockID.Slot())) - return api.BlockStateUnknown + return api.BlockStateUnknown, err } switch blockMetadata.State { case api.BlockStatePending, api.BlockStateDropped: if blockID.Slot() <= r.latestCommittedSlotFunc() { - return api.BlockStateOrphaned + return api.BlockStateOrphaned, nil } case api.BlockStateAccepted, api.BlockStateConfirmed: if blockID.Slot() <= r.finalizedSlotFunc() { - return api.BlockStateFinalized + return api.BlockStateFinalized, nil } } - return blockMetadata.State + return blockMetadata.State, nil } // OnBlockBooked triggers storing block in the retainer on block booked event. diff --git a/pkg/storage/prunable/slotstore/block_metadata.go b/pkg/storage/prunable/slotstore/block_metadata.go index 220c55389..b57851c47 100644 --- a/pkg/storage/prunable/slotstore/block_metadata.go +++ b/pkg/storage/prunable/slotstore/block_metadata.go @@ -98,11 +98,11 @@ func (r *BlockMetadataStore) StoreBlockDropped(blockID iotago.BlockID) error { return r.blockMetadataStore.Set(blockID, blockMetadata) } -func (r *BlockMetadataStore) BlockMetadata(blockID iotago.BlockID) (*BlockMetadata, bool) { +func (r *BlockMetadataStore) BlockMetadata(blockID iotago.BlockID) (*BlockMetadata, error) { blockMetadata, err := r.blockMetadataStore.Get(blockID) if err != nil { - return nil, false + return nil, err } - return blockMetadata, true + return blockMetadata, nil } diff --git a/tools/docker-network/tests/committeerotation_test.go b/tools/docker-network/tests/committeerotation_test.go index a96745491..80d560486 100644 --- a/tools/docker-network/tests/committeerotation_test.go +++ b/tools/docker-network/tests/committeerotation_test.go @@ -245,10 +245,10 @@ func Test_Delegation(t *testing.T) { account := d.CreateAccount() // delegate all faucet funds to V2, V2 should replace V3 - delegationStartEpoch := d.DelegateToValidator(account.ID, d.Node("V2")) - d.AssertCommittee(delegationStartEpoch+1, d.AccountsFromNodes(d.Nodes("V1", "V2", "V4")...)) + _, delegationOutput := d.DelegateToValidator(account.ID, d.Node("V2").AccountAddress(t)) + d.AssertCommittee(delegationOutput.StartEpoch+1, d.AccountsFromNodes(d.Nodes("V1", "V2", "V4")...)) // delegate all faucet funds to V3, V3 should replace V1 - delegationStartEpoch = d.DelegateToValidator(account.ID, d.Node("V3")) - d.AssertCommittee(delegationStartEpoch+1, d.AccountsFromNodes(d.Nodes("V2", "V3", "V4")...)) + _, delegationOutput = d.DelegateToValidator(account.ID, d.Node("V3").AccountAddress(t)) + d.AssertCommittee(delegationOutput.StartEpoch+1, d.AccountsFromNodes(d.Nodes("V2", "V3", "V4")...)) } diff --git a/tools/docker-network/tests/coreapi.go b/tools/docker-network/tests/coreapi.go new file mode 100644 index 000000000..bafa2d80e --- /dev/null +++ b/tools/docker-network/tests/coreapi.go @@ -0,0 +1,252 @@ +//go:build dockertests + +package tests + +import ( + "context" + "fmt" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/iotaledger/hive.go/ds/types" + "github.com/iotaledger/hive.go/lo" + iotago "github.com/iotaledger/iota.go/v4" + "github.com/iotaledger/iota.go/v4/api" +) + +type coreAPIAssets map[iotago.SlotIndex]*coreAPISlotAssets + +func (a coreAPIAssets) setupAssetsForSlot(slot iotago.SlotIndex) { + _, ok := a[slot] + if !ok { + a[slot] = newAssetsPerSlot() + } +} + +func (a coreAPIAssets) assertCommitments(t *testing.T) { + for _, asset := range a { + asset.assertCommitments(t) + } +} + +func (a coreAPIAssets) assertBICs(t *testing.T) { + for _, asset := range a { + asset.assertBICs(t) + } +} + +func (a coreAPIAssets) forEachBlock(t *testing.T, f func(*testing.T, *iotago.Block)) { + for _, asset := range a { + for _, block := range asset.dataBlocks { + f(t, block) + } + for _, block := range asset.valueBlocks { + f(t, block) + } + } +} + +func (a coreAPIAssets) forEachTransaction(t *testing.T, f func(*testing.T, *iotago.SignedTransaction, iotago.BlockID)) { + for _, asset := range a { + for i, tx := range asset.transactions { + blockID := asset.valueBlocks[i].MustID() + f(t, tx, blockID) + } + } +} + +func (a coreAPIAssets) forEachReattachment(t *testing.T, f func(*testing.T, iotago.BlockID)) { + for _, asset := range a { + for _, reattachment := range asset.reattachments { + f(t, reattachment) + } + } +} + +func (a coreAPIAssets) forEachOutput(t *testing.T, f func(*testing.T, iotago.OutputID, iotago.Output)) { + for _, asset := range a { + for outID, out := range asset.basicOutputs { + f(t, outID, out) + } + for outID, out := range asset.faucetOutputs { + f(t, outID, out) + } + for outID, out := range asset.delegationOutputs { + f(t, outID, out) + } + } +} + +func (a coreAPIAssets) forEachSlot(t *testing.T, f func(*testing.T, iotago.SlotIndex, map[string]iotago.CommitmentID)) { + for slot, slotAssets := range a { + f(t, slot, slotAssets.commitmentPerNode) + } +} + +func (a coreAPIAssets) forEachCommitment(t *testing.T, f func(*testing.T, map[string]iotago.CommitmentID)) { + for _, asset := range a { + f(t, asset.commitmentPerNode) + } +} + +func (a coreAPIAssets) forEachAccountAddress(t *testing.T, f func(t *testing.T, accountAddress *iotago.AccountAddress, commitmentPerNode map[string]iotago.CommitmentID, bicPerNode map[string]iotago.BlockIssuanceCredits)) { + for _, asset := range a { + if asset.accountAddress == nil { + // no account created in this slot + continue + } + f(t, asset.accountAddress, asset.commitmentPerNode, asset.bicPerNode) + } +} + +func (a coreAPIAssets) assertUTXOOutputIDsInSlot(t *testing.T, slot iotago.SlotIndex, createdOutputs iotago.OutputIDs, spentOutputs iotago.OutputIDs) { + created := make(map[iotago.OutputID]types.Empty) + spent := make(map[iotago.OutputID]types.Empty) + for _, outputID := range createdOutputs { + created[outputID] = types.Void + } + + for _, outputID := range spentOutputs { + spent[outputID] = types.Void + } + + for outID := range a[slot].basicOutputs { + _, ok := created[outID] + require.True(t, ok, "Output ID not found in created outputs: %s, for slot %d", outID, slot) + } + + for outID := range a[slot].faucetOutputs { + _, ok := spent[outID] + require.True(t, ok, "Output ID not found in spent outputs: %s, for slot %d", outID, slot) + } +} + +func (a coreAPIAssets) assertUTXOOutputsInSlot(t *testing.T, slot iotago.SlotIndex, created []*api.OutputWithID, spent []*api.OutputWithID) { + createdMap := make(map[iotago.OutputID]iotago.Output) + spentMap := make(map[iotago.OutputID]iotago.Output) + for _, output := range created { + createdMap[output.OutputID] = output.Output + } + for _, output := range spent { + spentMap[output.OutputID] = output.Output + } + + for outID, out := range a[slot].basicOutputs { + _, ok := createdMap[outID] + require.True(t, ok, "Output ID not found in created outputs: %s, for slot %d", outID, slot) + require.Equal(t, out, createdMap[outID], "Output not equal for ID: %s, for slot %d", outID, slot) + } + + for outID, out := range a[slot].faucetOutputs { + _, ok := spentMap[outID] + require.True(t, ok, "Output ID not found in spent outputs: %s, for slot %d", outID, slot) + require.Equal(t, out, spentMap[outID], "Output not equal for ID: %s, for slot %d", outID, slot) + } +} + +type coreAPISlotAssets struct { + accountAddress *iotago.AccountAddress + dataBlocks []*iotago.Block + valueBlocks []*iotago.Block + transactions []*iotago.SignedTransaction + reattachments []iotago.BlockID + basicOutputs map[iotago.OutputID]iotago.Output + faucetOutputs map[iotago.OutputID]iotago.Output + delegationOutputs map[iotago.OutputID]iotago.Output + + commitmentPerNode map[string]iotago.CommitmentID + bicPerNode map[string]iotago.BlockIssuanceCredits +} + +func (a *coreAPISlotAssets) assertCommitments(t *testing.T) { + prevCommitment := a.commitmentPerNode["V1"] + for _, commitmentID := range a.commitmentPerNode { + if prevCommitment == iotago.EmptyCommitmentID { + require.Fail(t, "commitment is empty") + } + + require.Equal(t, commitmentID, prevCommitment) + prevCommitment = commitmentID + } +} + +func (a *coreAPISlotAssets) assertBICs(t *testing.T) { + prevBIC := a.bicPerNode["V1"] + for _, bic := range a.bicPerNode { + require.Equal(t, bic, prevBIC) + prevBIC = bic + } +} + +func newAssetsPerSlot() *coreAPISlotAssets { + return &coreAPISlotAssets{ + dataBlocks: make([]*iotago.Block, 0), + valueBlocks: make([]*iotago.Block, 0), + transactions: make([]*iotago.SignedTransaction, 0), + reattachments: make([]iotago.BlockID, 0), + basicOutputs: make(map[iotago.OutputID]iotago.Output), + faucetOutputs: make(map[iotago.OutputID]iotago.Output), + delegationOutputs: make(map[iotago.OutputID]iotago.Output), + commitmentPerNode: make(map[string]iotago.CommitmentID), + bicPerNode: make(map[string]iotago.BlockIssuanceCredits), + } +} + +func (d *DockerTestFramework) prepareAssets(totalAssetsNum int) (coreAPIAssets, iotago.SlotIndex) { + assets := make(coreAPIAssets) + ctx := context.Background() + + latestSlot := iotago.SlotIndex(0) + + for i := 0; i < totalAssetsNum; i++ { + // account + account := d.CreateAccount() + assets.setupAssetsForSlot(account.OutputID.Slot()) + assets[account.OutputID.Slot()].accountAddress = account.Address + + // data block + block := d.CreateTaggedDataBlock(account.ID, []byte("tag")) + blockSlot := lo.PanicOnErr(block.ID()).Slot() + assets.setupAssetsForSlot(blockSlot) + assets[blockSlot].dataBlocks = append(assets[blockSlot].dataBlocks, block) + d.SubmitBlock(ctx, block) + + // transaction + valueBlock, signedTx, faucetOutput := d.CreateBasicOutputBlock(account.ID) + valueBlockSlot := valueBlock.MustID().Slot() + assets.setupAssetsForSlot(valueBlockSlot) + // transaction and outputs are stored with the earliest included block + assets[valueBlockSlot].valueBlocks = append(assets[valueBlockSlot].valueBlocks, valueBlock) + assets[valueBlockSlot].transactions = append(assets[valueBlockSlot].transactions, signedTx) + basicOutputID := iotago.OutputIDFromTransactionIDAndIndex(signedTx.Transaction.MustID(), 0) + assets[valueBlockSlot].basicOutputs[basicOutputID] = signedTx.Transaction.Outputs[0] + assets[valueBlockSlot].faucetOutputs[faucetOutput.ID] = faucetOutput.Output + d.SubmitBlock(ctx, valueBlock) + d.AwaitTransactionPayloadAccepted(ctx, signedTx.Transaction.MustID()) + + // issue reattachment after the fisrt one is already included + issuerResp, congestionResp := d.PrepareBlockIssuance(ctx, d.wallet.DefaultClient(), account.Address) + secondAttachment := d.SubmitPayload(ctx, signedTx, account.Address.AccountID(), congestionResp, issuerResp) + assets[valueBlockSlot].reattachments = append(assets[valueBlockSlot].reattachments, secondAttachment) + + // delegation + delegationOutputID, delegationOutput := d.DelegateToValidator(account.ID, d.Node("V1").AccountAddress(d.Testing)) + assets.setupAssetsForSlot(delegationOutputID.CreationSlot()) + assets[delegationOutputID.CreationSlot()].delegationOutputs[delegationOutputID] = delegationOutput + + latestSlot = lo.Max[iotago.SlotIndex](latestSlot, blockSlot, valueBlockSlot, delegationOutputID.CreationSlot(), secondAttachment.Slot()) + + fmt.Printf("Assets for slot %d\n: dataBlock: %s block: %s\ntx: %s\nbasic output: %s, faucet output: %s\n delegation output: %s\n", + valueBlockSlot, block.MustID().String(), valueBlock.MustID().String(), lo.PanicOnErr(signedTx.ID()).String(), + basicOutputID.String(), faucetOutput.ID.String(), delegationOutputID.String()) + } + + return assets, latestSlot +} + +func (d *DockerTestFramework) requestFromClients(testFunc func(*testing.T, string)) { + for alias := range d.nodes { + testFunc(d.Testing, alias) + } +} diff --git a/tools/docker-network/tests/coreapi_test.go b/tools/docker-network/tests/coreapi_test.go new file mode 100644 index 000000000..f543963fd --- /dev/null +++ b/tools/docker-network/tests/coreapi_test.go @@ -0,0 +1,554 @@ +//go:build dockertests + +package tests + +import ( + "context" + "fmt" + "net/http" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/iotaledger/hive.go/lo" + iotago "github.com/iotaledger/iota.go/v4" + "github.com/iotaledger/iota.go/v4/api" + "github.com/iotaledger/iota.go/v4/tpkg" +) + +func Test_CoreAPI(t *testing.T) { + d := NewDockerTestFramework(t, + WithProtocolParametersOptions( + iotago.WithTimeProviderOptions(5, time.Now().Unix(), 10, 4), + iotago.WithLivenessOptions(10, 10, 2, 4, 8), + iotago.WithRewardsOptions(8, 10, 2, 384), + iotago.WithTargetCommitteeSize(4), + )) + defer d.Stop() + + d.AddValidatorNode("V1", "docker-network-inx-validator-1-1", "http://localhost:8050", "rms1pzg8cqhfxqhq7pt37y8cs4v5u4kcc48lquy2k73ehsdhf5ukhya3y5rx2w6") + d.AddValidatorNode("V2", "docker-network-inx-validator-2-1", "http://localhost:8060", "rms1pqm4xk8e9ny5w5rxjkvtp249tfhlwvcshyr3pc0665jvp7g3hc875k538hl") + d.AddValidatorNode("V3", "docker-network-inx-validator-3-1", "http://localhost:8070", "rms1pp4wuuz0y42caz48vv876qfpmffswsvg40zz8v79sy8cp0jfxm4kunflcgt") + d.AddValidatorNode("V4", "docker-network-inx-validator-4-1", "http://localhost:8040", "rms1pr8cxs3dzu9xh4cduff4dd4cxdthpjkpwmz2244f75m0urslrsvtsshrrjw") + d.AddNode("node5", "docker-network-node-5-1", "http://localhost:8090") + + runErr := d.Run() + require.NoError(t, runErr) + + d.WaitUntilNetworkReady() + + assetsPerSlot, lastSlot := d.prepareAssets(5) + + fmt.Println("Await finalisation of slot", lastSlot) + d.AwaitFinalization(lastSlot) + + tests := []struct { + name string + testFunc func(t *testing.T, nodeAlias string) + }{ + { + name: "Test_Info", + testFunc: func(t *testing.T, nodeAlias string) { + resp, err := d.wallet.Clients[nodeAlias].Info(context.Background()) + require.NoError(t, err) + require.NotNil(t, resp) + }, + }, + { + name: "Test_BlockByBlockID", + testFunc: func(t *testing.T, nodeAlias string) { + assetsPerSlot.forEachBlock(t, func(t *testing.T, block *iotago.Block) { + respBlock, err := d.wallet.Clients[nodeAlias].BlockByBlockID(context.Background(), block.MustID()) + require.NoError(t, err) + require.NotNil(t, respBlock) + require.Equal(t, block.MustID(), respBlock.MustID(), "BlockID of retrieved block does not match: %s != %s", block.MustID(), respBlock.MustID()) + }) + }, + }, + { + name: "Test_BlockMetadataByBlockID", + testFunc: func(t *testing.T, nodeAlias string) { + assetsPerSlot.forEachBlock(t, func(t *testing.T, block *iotago.Block) { + resp, err := d.wallet.Clients[nodeAlias].BlockMetadataByBlockID(context.Background(), block.MustID()) + require.NoError(t, err) + require.NotNil(t, resp) + require.Equal(t, block.MustID(), resp.BlockID, "BlockID of retrieved block does not match: %s != %s", block.MustID(), resp.BlockID) + require.Equal(t, api.BlockStateFinalized, resp.BlockState) + }) + + assetsPerSlot.forEachReattachment(t, func(t *testing.T, blockID iotago.BlockID) { + resp, err := d.wallet.Clients[nodeAlias].BlockMetadataByBlockID(context.Background(), blockID) + require.NoError(t, err) + require.NotNil(t, resp) + require.Equal(t, blockID, resp.BlockID, "BlockID of retrieved block does not match: %s != %s", blockID, resp.BlockID) + require.Equal(t, api.BlockStateFinalized, resp.BlockState) + }) + }, + }, + { + name: "Test_BlockWithMetadata", + testFunc: func(t *testing.T, nodeAlias string) { + assetsPerSlot.forEachBlock(t, func(t *testing.T, block *iotago.Block) { + resp, err := d.wallet.Clients[nodeAlias].BlockWithMetadataByBlockID(context.Background(), block.MustID()) + require.NoError(t, err) + require.NotNil(t, resp) + require.Equal(t, block.MustID(), resp.Block.MustID(), "BlockID of retrieved block does not match: %s != %s", block.MustID(), resp.Block.MustID()) + require.Equal(t, api.BlockStateFinalized, resp.Metadata.BlockState) + }) + }, + }, + { + name: "Test_BlockIssuance", + testFunc: func(t *testing.T, nodeAlias string) { + resp, err := d.wallet.Clients[nodeAlias].BlockIssuance(context.Background()) + require.NoError(t, err) + require.NotNil(t, resp) + + require.GreaterOrEqual(t, len(resp.StrongParents), 1, "There should be at least 1 strong parent provided") + }, + }, + { + name: "Test_CommitmentBySlot", + testFunc: func(t *testing.T, nodeAlias string) { + assetsPerSlot.forEachSlot(t, func(t *testing.T, slot iotago.SlotIndex, commitmentsPerNode map[string]iotago.CommitmentID) { + resp, err := d.wallet.Clients[nodeAlias].CommitmentBySlot(context.Background(), slot) + require.NoError(t, err) + require.NotNil(t, resp) + commitmentsPerNode[nodeAlias] = resp.MustID() + }) + }, + }, + { + name: "Test_CommitmentByID", + testFunc: func(t *testing.T, nodeAlias string) { + assetsPerSlot.forEachCommitment(t, func(t *testing.T, commitmentsPerNode map[string]iotago.CommitmentID) { + resp, err := d.wallet.Clients[nodeAlias].CommitmentByID(context.Background(), commitmentsPerNode[nodeAlias]) + require.NoError(t, err) + require.NotNil(t, resp) + require.Equal(t, commitmentsPerNode[nodeAlias], resp.MustID(), "Commitment does not match commitment got for the same slot from the same node: %s != %s", commitmentsPerNode[nodeAlias], resp.MustID()) + }) + }, + }, + { + name: "Test_CommitmentUTXOChangesByID", + testFunc: func(t *testing.T, nodeAlias string) { + assetsPerSlot.forEachCommitment(t, func(t *testing.T, commitmentsPerNode map[string]iotago.CommitmentID) { + resp, err := d.wallet.Clients[nodeAlias].CommitmentUTXOChangesByID(context.Background(), commitmentsPerNode[nodeAlias]) + require.NoError(t, err) + require.NotNil(t, resp) + assetsPerSlot.assertUTXOOutputIDsInSlot(t, commitmentsPerNode[nodeAlias].Slot(), resp.CreatedOutputs, resp.ConsumedOutputs) + require.Equal(t, commitmentsPerNode[nodeAlias], resp.CommitmentID, "CommitmentID of retrieved UTXO changes does not match: %s != %s", commitmentsPerNode[nodeAlias], resp.CommitmentID) + }) + }, + }, + { + "Test_CommitmentUTXOChangesFullByID", + func(t *testing.T, nodeAlias string) { + assetsPerSlot.forEachCommitment(t, func(t *testing.T, commitmentsPerNode map[string]iotago.CommitmentID) { + resp, err := d.wallet.Clients[nodeAlias].CommitmentUTXOChangesFullByID(context.Background(), commitmentsPerNode[nodeAlias]) + require.NoError(t, err) + require.NotNil(t, resp) + assetsPerSlot.assertUTXOOutputsInSlot(t, commitmentsPerNode[nodeAlias].Slot(), resp.CreatedOutputs, resp.ConsumedOutputs) + require.Equal(t, commitmentsPerNode[nodeAlias], resp.CommitmentID, "CommitmentID of retrieved UTXO changes does not match: %s != %s", commitmentsPerNode[nodeAlias], resp.CommitmentID) + }) + }, + }, + { + name: "Test_CommitmentUTXOChangesBySlot", + testFunc: func(t *testing.T, nodeAlias string) { + assetsPerSlot.forEachCommitment(t, func(t *testing.T, commitmentsPerNode map[string]iotago.CommitmentID) { + resp, err := d.wallet.Clients[nodeAlias].CommitmentUTXOChangesBySlot(context.Background(), commitmentsPerNode[nodeAlias].Slot()) + require.NoError(t, err) + require.NotNil(t, resp) + assetsPerSlot.assertUTXOOutputIDsInSlot(t, commitmentsPerNode[nodeAlias].Slot(), resp.CreatedOutputs, resp.ConsumedOutputs) + require.Equal(t, commitmentsPerNode[nodeAlias], resp.CommitmentID, "CommitmentID of retrieved UTXO changes does not match: %s != %s", commitmentsPerNode[nodeAlias], resp.CommitmentID) + }) + }, + }, + { + name: "Test_CommitmentUTXOChangesFullBySlot", + testFunc: func(t *testing.T, nodeAlias string) { + assetsPerSlot.forEachCommitment(t, func(t *testing.T, commitmentsPerNode map[string]iotago.CommitmentID) { + resp, err := d.wallet.Clients[nodeAlias].CommitmentUTXOChangesFullBySlot(context.Background(), commitmentsPerNode[nodeAlias].Slot()) + require.NoError(t, err) + require.NotNil(t, resp) + assetsPerSlot.assertUTXOOutputsInSlot(t, commitmentsPerNode[nodeAlias].Slot(), resp.CreatedOutputs, resp.ConsumedOutputs) + require.Equal(t, commitmentsPerNode[nodeAlias], resp.CommitmentID, "CommitmentID of retrieved UTXO changes does not match: %s != %s", commitmentsPerNode[nodeAlias], resp.CommitmentID) + }) + }, + }, + { + name: "Test_OutputByID", + testFunc: func(t *testing.T, nodeAlias string) { + assetsPerSlot.forEachOutput(t, func(t *testing.T, outputID iotago.OutputID, output iotago.Output) { + resp, err := d.wallet.Clients[nodeAlias].OutputByID(context.Background(), outputID) + require.NoError(t, err) + require.NotNil(t, resp) + require.EqualValues(t, output, resp, "Output created is different than retrieved from the API: %s != %s", output, resp) + }) + }, + }, + { + name: "Test_OutputMetadata", + testFunc: func(t *testing.T, nodeAlias string) { + assetsPerSlot.forEachOutput(t, func(t *testing.T, outputID iotago.OutputID, output iotago.Output) { + resp, err := d.wallet.Clients[nodeAlias].OutputMetadataByID(context.Background(), outputID) + require.NoError(t, err) + require.NotNil(t, resp) + require.EqualValues(t, outputID, resp.OutputID, "OutputID of retrieved output does not match: %s != %s", outputID, resp.OutputID) + require.EqualValues(t, outputID.TransactionID(), resp.Included.TransactionID, "TransactionID of retrieved output does not match: %s != %s", outputID.TransactionID(), resp.Included.TransactionID) + }) + }, + }, + { + name: "Test_OutputWithMetadata", + testFunc: func(t *testing.T, nodeAlias string) { + assetsPerSlot.forEachOutput(t, func(t *testing.T, outputID iotago.OutputID, output iotago.Output) { + out, outMetadata, err := d.wallet.Clients[nodeAlias].OutputWithMetadataByID(context.Background(), outputID) + require.NoError(t, err) + require.NotNil(t, outMetadata) + require.NotNil(t, out) + require.EqualValues(t, outputID, outMetadata.OutputID, "OutputID of retrieved output does not match: %s != %s", outputID, outMetadata.OutputID) + require.EqualValues(t, outputID.TransactionID(), outMetadata.Included.TransactionID, "TransactionID of retrieved output does not match: %s != %s", outputID.TransactionID(), outMetadata.Included.TransactionID) + require.EqualValues(t, output, out, "OutputID of retrieved output does not match: %s != %s", output, out) + }) + }, + }, + { + name: "Test_TransactionsIncludedBlock", + testFunc: func(t *testing.T, nodeAlias string) { + assetsPerSlot.forEachTransaction(t, func(t *testing.T, transaction *iotago.SignedTransaction, firstAttachmentID iotago.BlockID) { + resp, err := d.wallet.Clients[nodeAlias].TransactionIncludedBlock(context.Background(), lo.PanicOnErr(transaction.Transaction.ID())) + require.NoError(t, err) + require.NotNil(t, resp) + require.EqualValues(t, firstAttachmentID, resp.MustID()) + }) + }, + }, + { + name: "Test_TransactionsIncludedBlockMetadata", + testFunc: func(t *testing.T, nodeAlias string) { + assetsPerSlot.forEachTransaction(t, func(t *testing.T, transaction *iotago.SignedTransaction, firstAttachmentID iotago.BlockID) { + resp, err := d.wallet.Clients[nodeAlias].TransactionIncludedBlockMetadata(context.Background(), lo.PanicOnErr(transaction.Transaction.ID())) + require.NoError(t, err) + require.NotNil(t, resp) + require.EqualValues(t, api.BlockStateFinalized, resp.BlockState) + require.EqualValues(t, firstAttachmentID, resp.BlockID, "Inclusion BlockID of retrieved transaction does not match: %s != %s", firstAttachmentID, resp.BlockID) + }) + }, + }, + { + name: "Test_TransactionsMetadata", + testFunc: func(t *testing.T, nodeAlias string) { + assetsPerSlot.forEachTransaction(t, func(t *testing.T, transaction *iotago.SignedTransaction, firstAttachmentID iotago.BlockID) { + resp, err := d.wallet.Clients[nodeAlias].TransactionMetadata(context.Background(), lo.PanicOnErr(transaction.Transaction.ID())) + require.NoError(t, err) + require.NotNil(t, resp) + require.Equal(t, api.TransactionStateFinalized, resp.TransactionState) + require.EqualValues(t, resp.EarliestAttachmentSlot, firstAttachmentID.Slot()) + }) + }, + }, + { + name: "Test_Congestion", + testFunc: func(t *testing.T, nodeAlias string) { + assetsPerSlot.forEachAccountAddress(t, func( + t *testing.T, + accountAddress *iotago.AccountAddress, + commitmentPerNode map[string]iotago.CommitmentID, + bicPerNoode map[string]iotago.BlockIssuanceCredits, + ) { + resp, err := d.wallet.Clients[nodeAlias].Congestion(context.Background(), accountAddress, 0) + require.NoError(t, err) + require.NotNil(t, resp) + + // node allows to get account only for the slot newer than lastCommittedSlot - MCA, we need fresh commitment + infoRes, err := d.wallet.Clients[nodeAlias].Info(context.Background()) + require.NoError(t, err) + commitment, err := d.wallet.Clients[nodeAlias].CommitmentBySlot(context.Background(), infoRes.Status.LatestCommitmentID.Slot()) + require.NoError(t, err) + + resp, err = d.wallet.Clients[nodeAlias].Congestion(context.Background(), accountAddress, 0, commitment.MustID()) + require.NoError(t, err) + require.NotNil(t, resp) + // later we check if all nodes have returned the same BIC value for this account + bicPerNoode[nodeAlias] = resp.BlockIssuanceCredits + }) + }, + }, + { + name: "Test_Validators", + testFunc: func(t *testing.T, nodeAlias string) { + pageSize := uint64(3) + resp, err := d.wallet.Clients[nodeAlias].Validators(context.Background(), pageSize) + require.NoError(t, err) + require.NotNil(t, resp) + //TODO after finishing validators endpoint and including registered validators + //require.Equal(t, int(pageSize), len(resp.Validators), "There should be exactly %d validators returned on the first page", pageSize) + + resp, err = d.wallet.Clients[nodeAlias].Validators(context.Background(), pageSize, resp.Cursor) + require.NoError(t, err) + require.NotNil(t, resp) + //TODO after finishing validators endpoint and including registered validators + //require.Equal(t, 1, len(resp.Validators), "There should be only one validator returned on the last page") + }, + }, + { + name: "Test_ValidatorsAll", + testFunc: func(t *testing.T, nodeAlias string) { + resp, all, err := d.wallet.Clients[nodeAlias].ValidatorsAll(context.Background()) + require.NoError(t, err) + require.True(t, all) + require.Equal(t, 4, len(resp.Validators)) + }, + }, + { + name: "Test_Rewards", + testFunc: func(t *testing.T, nodeAlias string) { + assetsPerSlot.forEachOutput(t, func(t *testing.T, outputID iotago.OutputID, output iotago.Output) { + if output.Type() != iotago.OutputDelegation { + return + } + + resp, err := d.wallet.Clients[nodeAlias].Rewards(context.Background(), outputID) + require.NoError(t, err) + require.NotNil(t, resp) + // rewards are zero, because we do not wait for the epoch end + require.EqualValues(t, 0, resp.Rewards) + }) + }, + }, + { + name: "Test_Committee", + testFunc: func(t *testing.T, nodeAlias string) { + resp, err := d.wallet.Clients[nodeAlias].Committee(context.Background()) + require.NoError(t, err) + require.NotNil(t, resp) + require.EqualValues(t, 4, len(resp.Committee)) + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + d.requestFromClients(test.testFunc) + }) + } + + // check if the same values were returned by all nodes for the same slot + assetsPerSlot.assertCommitments(t) + assetsPerSlot.assertBICs(t) +} + +func Test_CoreAPI_BadRequests(t *testing.T) { + d := NewDockerTestFramework(t, + WithProtocolParametersOptions( + iotago.WithTimeProviderOptions(5, time.Now().Unix(), 10, 4), + iotago.WithLivenessOptions(10, 10, 2, 4, 8), + iotago.WithRewardsOptions(8, 10, 2, 384), + iotago.WithTargetCommitteeSize(4), + )) + defer d.Stop() + + d.AddValidatorNode("V1", "docker-network-inx-validator-1-1", "http://localhost:8050", "rms1pzg8cqhfxqhq7pt37y8cs4v5u4kcc48lquy2k73ehsdhf5ukhya3y5rx2w6") + d.AddValidatorNode("V2", "docker-network-inx-validator-2-1", "http://localhost:8060", "rms1pqm4xk8e9ny5w5rxjkvtp249tfhlwvcshyr3pc0665jvp7g3hc875k538hl") + d.AddValidatorNode("V3", "docker-network-inx-validator-3-1", "http://localhost:8070", "rms1pp4wuuz0y42caz48vv876qfpmffswsvg40zz8v79sy8cp0jfxm4kunflcgt") + d.AddValidatorNode("V4", "docker-network-inx-validator-4-1", "http://localhost:8040", "rms1pr8cxs3dzu9xh4cduff4dd4cxdthpjkpwmz2244f75m0urslrsvtsshrrjw") + d.AddNode("node5", "docker-network-node-5-1", "http://localhost:8090") + + runErr := d.Run() + require.NoError(t, runErr) + + d.WaitUntilNetworkReady() + + tests := []struct { + name string + testFunc func(t *testing.T, nodeAlias string) + }{ + { + name: "Test_BlockByBlockID_Failure", + testFunc: func(t *testing.T, nodeAlias string) { + blockID := tpkg.RandBlockID() + respBlock, err := d.wallet.Clients[nodeAlias].BlockByBlockID(context.Background(), blockID) + require.Error(t, err) + require.True(t, isStatusCode(err, http.StatusNotFound)) + require.Nil(t, respBlock) + }, + }, + { + name: "Test_BlockMetadataByBlockID_Failure", + testFunc: func(t *testing.T, nodeAlias string) { + blockID := tpkg.RandBlockID() + resp, err := d.wallet.Clients[nodeAlias].BlockMetadataByBlockID(context.Background(), blockID) + require.Error(t, err) + require.True(t, isStatusCode(err, http.StatusNotFound)) + require.Nil(t, resp) + }, + }, + { + name: "Test_BlockWithMetadata_Failure", + testFunc: func(t *testing.T, nodeAlias string) { + blockID := tpkg.RandBlockID() + resp, err := d.wallet.Clients[nodeAlias].BlockWithMetadataByBlockID(context.Background(), blockID) + require.Error(t, err) + require.True(t, isStatusCode(err, http.StatusNotFound)) + require.Nil(t, resp) + }, + }, + { + name: "Test_CommitmentBySlot_Failure", + testFunc: func(t *testing.T, nodeAlias string) { + slot := iotago.SlotIndex(1000_000_000) + resp, err := d.wallet.Clients[nodeAlias].CommitmentBySlot(context.Background(), slot) + require.Error(t, err) + require.True(t, isStatusCode(err, http.StatusBadRequest)) + require.Nil(t, resp) + }, + }, + { + name: "Test_CommitmentByID_Failure", + testFunc: func(t *testing.T, nodeAlias string) { + committmentID := tpkg.RandCommitmentID() + resp, err := d.wallet.Clients[nodeAlias].CommitmentByID(context.Background(), committmentID) + require.Error(t, err) + require.True(t, isStatusCode(err, http.StatusBadRequest)) + require.Nil(t, resp) + }, + }, + { + name: "Test_CommitmentUTXOChangesByID_Failure", + testFunc: func(t *testing.T, nodeAlias string) { + committmentID := tpkg.RandCommitmentID() + resp, err := d.wallet.Clients[nodeAlias].CommitmentUTXOChangesByID(context.Background(), committmentID) + require.Error(t, err) + require.True(t, isStatusCode(err, http.StatusBadRequest)) + require.Nil(t, resp) + }, + }, + { + "Test_CommitmentUTXOChangesFullByID_Failure", + func(t *testing.T, nodeAlias string) { + committmentID := tpkg.RandCommitmentID() + + resp, err := d.wallet.Clients[nodeAlias].CommitmentUTXOChangesFullByID(context.Background(), committmentID) + require.Error(t, err) + require.True(t, isStatusCode(err, http.StatusBadRequest)) + require.Nil(t, resp) + }, + }, + { + name: "Test_CommitmentUTXOChangesBySlot_Failure", + testFunc: func(t *testing.T, nodeAlias string) { + slot := iotago.SlotIndex(1000_000_000) + resp, err := d.wallet.Clients[nodeAlias].CommitmentUTXOChangesBySlot(context.Background(), slot) + require.Error(t, err) + require.True(t, isStatusCode(err, http.StatusBadRequest)) + require.Nil(t, resp) + }, + }, + { + name: "Test_CommitmentUTXOChangesFullBySlot_Failure", + testFunc: func(t *testing.T, nodeAlias string) { + slot := iotago.SlotIndex(1000_000_000) + + resp, err := d.wallet.Clients[nodeAlias].CommitmentUTXOChangesFullBySlot(context.Background(), slot) + require.Error(t, err) + require.True(t, isStatusCode(err, http.StatusBadRequest)) + require.Nil(t, resp) + }, + }, + { + name: "Test_OutputByID_Failure", + testFunc: func(t *testing.T, nodeAlias string) { + outputID := tpkg.RandOutputID(0) + resp, err := d.wallet.Clients[nodeAlias].OutputByID(context.Background(), outputID) + require.Error(t, err) + require.True(t, isStatusCode(err, http.StatusNotFound)) + require.Nil(t, resp) + }, + }, + { + name: "Test_OutputMetadata_Failure", + testFunc: func(t *testing.T, nodeAlias string) { + outputID := tpkg.RandOutputID(0) + + resp, err := d.wallet.Clients[nodeAlias].OutputMetadataByID(context.Background(), outputID) + require.Error(t, err) + require.True(t, isStatusCode(err, http.StatusNotFound)) + require.Nil(t, resp) + }, + }, + { + name: "Test_OutputWithMetadata_Failure", + testFunc: func(t *testing.T, nodeAlias string) { + outputID := tpkg.RandOutputID(0) + + out, outMetadata, err := d.wallet.Clients[nodeAlias].OutputWithMetadataByID(context.Background(), outputID) + require.Error(t, err) + require.Nil(t, out) + require.Nil(t, outMetadata) + require.True(t, isStatusCode(err, http.StatusNotFound)) + }, + }, + { + name: "Test_TransactionsIncludedBlock_Failure", + testFunc: func(t *testing.T, nodeAlias string) { + txID := tpkg.RandTransactionID() + resp, err := d.wallet.Clients[nodeAlias].TransactionIncludedBlock(context.Background(), txID) + require.Error(t, err) + require.True(t, isStatusCode(err, http.StatusInternalServerError)) + require.Nil(t, resp) + }, + }, + { + name: "Test_TransactionsIncludedBlockMetadata_Failure", + testFunc: func(t *testing.T, nodeAlias string) { + txID := tpkg.RandTransactionID() + + resp, err := d.wallet.Clients[nodeAlias].TransactionIncludedBlockMetadata(context.Background(), txID) + require.Error(t, err) + require.True(t, isStatusCode(err, http.StatusInternalServerError)) + require.Nil(t, resp) + }, + }, + { + name: "Test_TransactionsMetadata_Failure", + testFunc: func(t *testing.T, nodeAlias string) { + txID := tpkg.RandTransactionID() + + resp, err := d.wallet.Clients[nodeAlias].TransactionMetadata(context.Background(), txID) + require.Error(t, err) + require.True(t, isStatusCode(err, http.StatusNotFound)) + require.Nil(t, resp) + }, + }, + { + name: "Test_Congestion_Failure", + testFunc: func(t *testing.T, nodeAlias string) { + accountAddress := tpkg.RandAccountAddress() + commitmentID := tpkg.RandCommitmentID() + resp, err := d.wallet.Clients[nodeAlias].Congestion(context.Background(), accountAddress, 0, commitmentID) + require.Error(t, err) + require.True(t, isStatusCode(err, http.StatusBadRequest)) + require.Nil(t, resp) + }, + }, + { + name: "Test_Rewards_Failure", + testFunc: func(t *testing.T, nodeAlias string) { + outputID := tpkg.RandOutputID(0) + resp, err := d.wallet.Clients[nodeAlias].Rewards(context.Background(), outputID) + require.Error(t, err) + require.True(t, isStatusCode(err, http.StatusNotFound)) + require.Nil(t, resp) + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + d.requestFromClients(test.testFunc) + }) + } +} diff --git a/tools/docker-network/tests/dockerframework.go b/tools/docker-network/tests/dockerframework.go index bf79ac7ca..6fde74dd2 100644 --- a/tools/docker-network/tests/dockerframework.go +++ b/tools/docker-network/tests/dockerframework.go @@ -30,7 +30,7 @@ import ( ) var ( - // need to build snapshotfile in tools/docker-network + // need to build snapshotfile in tools/docker-network. snapshotFilePath = "../docker-network.snapshot" keyManager = func() *wallet.KeyManager { genesisSeed, err := base58.Decode("7R1itJx5hVuo9w9hjg5cwKFmek4HMSoBDgJZN8hKGxih") @@ -56,6 +56,15 @@ type Node struct { IssueCandidacyPayload bool } +func (n *Node) AccountAddress(t *testing.T) *iotago.AccountAddress { + _, addr, err := iotago.ParseBech32(n.AccountAddressBech32) + require.NoError(t, err) + accAddress, ok := addr.(*iotago.AccountAddress) + require.True(t, ok) + + return accAddress +} + type DockerTestFramework struct { Testing *testing.T @@ -87,7 +96,7 @@ func NewDockerTestFramework(t *testing.T, opts ...options.Option[DockerTestFrame }, opts, func(d *DockerTestFramework) { d.optsProtocolParameterOptions = append(DefaultProtocolParametersOptions, d.optsProtocolParameterOptions...) protocolParams := iotago.NewV3SnapshotProtocolParameters(d.optsProtocolParameterOptions...) - api := iotago.V3API(protocolParams) + testAPI := iotago.V3API(protocolParams) d.logDirectoryPath = createLogDirectory(t.Name()) d.snapshotPath = snapshotFilePath @@ -95,9 +104,9 @@ func NewDockerTestFramework(t *testing.T, opts ...options.Option[DockerTestFrame []options.Option[snapshotcreator.Options]{ snapshotcreator.WithDatabaseVersion(protocol.DatabaseVersion), snapshotcreator.WithFilePath(d.snapshotPath), - snapshotcreator.WithProtocolParameters(api.ProtocolParameters()), + snapshotcreator.WithProtocolParameters(testAPI.ProtocolParameters()), snapshotcreator.WithRootBlocks(map[iotago.BlockID]iotago.CommitmentID{ - api.ProtocolParameters().GenesisBlockID(): iotago.NewEmptyCommitment(api).MustID(), + testAPI.ProtocolParameters().GenesisBlockID(): iotago.NewEmptyCommitment(testAPI).MustID(), }), snapshotcreator.WithGenesisKeyManager(keyManager()), }...) @@ -337,7 +346,8 @@ func (d *DockerTestFramework) StartIssueCandidacyPayload(nodes ...*Node) { node.IssueCandidacyPayload = true } - d.DockerComposeUp(true) + err := d.DockerComposeUp(true) + require.NoError(d.Testing, err) } func (d *DockerTestFramework) StopIssueCandidacyPayload(nodes ...*Node) { @@ -349,105 +359,123 @@ func (d *DockerTestFramework) StopIssueCandidacyPayload(nodes ...*Node) { node.IssueCandidacyPayload = false } - d.DockerComposeUp(true) + err := d.DockerComposeUp(true) + require.NoError(d.Testing, err) } // CreateTaggedDataBlock creates a block of a tagged data payload. -func (d *DockerTestFramework) CreateTaggedDataBlock(issuerId iotago.AccountID, tag []byte) *iotago.Block { - issuer := d.wallet.Account(issuerId) +func (d *DockerTestFramework) CreateTaggedDataBlock(issuerID iotago.AccountID, tag []byte) *iotago.Block { + issuer := d.wallet.Account(issuerID) ctx := context.TODO() clt := d.wallet.DefaultClient() issuerResp, congestionResp := d.PrepareBlockIssuance(ctx, clt, issuer.Address) - return d.CreateBlock(ctx, &iotago.TaggedData{ + return d.CreateBlock(&iotago.TaggedData{ Tag: tag, - }, issuerId, congestionResp, issuerResp) + }, issuerID, congestionResp, issuerResp) +} + +func (d *DockerTestFramework) CreateBasicOutputBlock(issuerAccountID iotago.AccountID) (*iotago.Block, *iotago.SignedTransaction, *OutputData) { + clt := d.wallet.DefaultClient() + ctx := context.Background() + + fundsOutputID := d.RequestFaucetFunds(ctx, iotago.AddressEd25519) + + input := d.wallet.Output(fundsOutputID) + signedTx := d.wallet.CreateBasicOutputFromInput(input, issuerAccountID) + + issuerResp, congestionResp := d.PrepareBlockIssuance(ctx, clt, issuerAccountID.ToAddress().(*iotago.AccountAddress)) + block := d.CreateBlock(signedTx, issuerAccountID, congestionResp, issuerResp) + + return block, signedTx, input } // CreateDelegationBlockFromInput consumes the given basic output, then build a block of a transaction that includes a delegation output, in order to delegate the given validator. -func (d *DockerTestFramework) CreateDelegationBlockFromInput(issuerId iotago.AccountID, validator *Node, inputId iotago.OutputID) (iotago.DelegationID, iotago.OutputID, *iotago.Block) { - issuer := d.wallet.Account(issuerId) +func (d *DockerTestFramework) CreateDelegationBlockFromInput(issuerID iotago.AccountID, accountAdddress *iotago.AccountAddress, inputID iotago.OutputID) (iotago.DelegationID, iotago.OutputID, *iotago.Block) { + issuer := d.wallet.Account(issuerID) ctx := context.TODO() clt := d.wallet.DefaultClient() issuerResp, congestionResp := d.PrepareBlockIssuance(ctx, clt, issuer.Address) - signedTx := d.wallet.CreateDelegationFromInput(issuerId, validator, inputId, issuerResp) - outputId := iotago.OutputIDFromTransactionIDAndIndex(signedTx.Transaction.MustID(), 0) + signedTx := d.wallet.CreateDelegationFromInput(issuerID, accountAdddress, inputID, issuerResp) + outputID := iotago.OutputIDFromTransactionIDAndIndex(signedTx.Transaction.MustID(), 0) - return iotago.DelegationIDFromOutputID(outputId), - outputId, - d.CreateBlock(ctx, signedTx, issuerId, congestionResp, issuerResp) + return iotago.DelegationIDFromOutputID(outputID), + outputID, + d.CreateBlock(signedTx, issuerID, congestionResp, issuerResp) } // CreateFoundryBlockFromInput consumes the given basic output, then build a block of a transaction that includes a foundry output with the given mintedAmount and maxSupply. -func (d *DockerTestFramework) CreateFoundryBlockFromInput(issuerId iotago.AccountID, inputId iotago.OutputID, mintedAmount iotago.BaseToken, maxSupply iotago.BaseToken) (iotago.FoundryID, iotago.OutputID, *iotago.Block) { - issuer := d.wallet.Account(issuerId) +func (d *DockerTestFramework) CreateFoundryBlockFromInput(issuerID iotago.AccountID, inputID iotago.OutputID, mintedAmount iotago.BaseToken, maxSupply iotago.BaseToken) (iotago.FoundryID, iotago.OutputID, *iotago.Block) { + issuer := d.wallet.Account(issuerID) ctx := context.TODO() clt := d.wallet.DefaultClient() issuerResp, congestionResp := d.PrepareBlockIssuance(ctx, clt, issuer.Address) - signedTx := d.wallet.CreateFoundryAndNativeTokensFromInput(issuerId, inputId, mintedAmount, maxSupply, issuerResp) - txId, err := signedTx.Transaction.ID() + signedTx := d.wallet.CreateFoundryAndNativeTokensFromInput(issuerID, inputID, mintedAmount, maxSupply, issuerResp) + txID, err := signedTx.Transaction.ID() require.NoError(d.Testing, err) + //nolint:forcetypeassert return signedTx.Transaction.Outputs[1].(*iotago.FoundryOutput).MustFoundryID(), - iotago.OutputIDFromTransactionIDAndIndex(txId, 1), - d.CreateBlock(ctx, signedTx, issuerId, congestionResp, issuerResp) + iotago.OutputIDFromTransactionIDAndIndex(txID, 1), + d.CreateBlock(signedTx, issuerID, congestionResp, issuerResp) } // CreateNFTBlockFromInput consumes the given basic output, then build a block of a transaction that includes a NFT output with the given NFT output options. -func (d *DockerTestFramework) CreateNFTBlockFromInput(issuerId iotago.AccountID, inputId iotago.OutputID, opts ...options.Option[builder.NFTOutputBuilder]) (iotago.NFTID, iotago.OutputID, *iotago.Block) { - issuer := d.wallet.Account(issuerId) +func (d *DockerTestFramework) CreateNFTBlockFromInput(issuerID iotago.AccountID, inputID iotago.OutputID, opts ...options.Option[builder.NFTOutputBuilder]) (iotago.NFTID, iotago.OutputID, *iotago.Block) { + issuer := d.wallet.Account(issuerID) ctx := context.TODO() clt := d.wallet.DefaultClient() issuerResp, congestionResp := d.PrepareBlockIssuance(ctx, clt, issuer.Address) - signedTx := d.wallet.CreateNFTFromInput(issuerId, inputId, issuerResp, opts...) - outputId := iotago.OutputIDFromTransactionIDAndIndex(signedTx.Transaction.MustID(), 0) + signedTx := d.wallet.CreateNFTFromInput(issuerID, inputID, issuerResp, opts...) + outputID := iotago.OutputIDFromTransactionIDAndIndex(signedTx.Transaction.MustID(), 0) - return iotago.NFTIDFromOutputID(outputId), - outputId, - d.CreateBlock(ctx, signedTx, issuerId, congestionResp, issuerResp) + return iotago.NFTIDFromOutputID(outputID), + outputID, + d.CreateBlock(signedTx, issuerID, congestionResp, issuerResp) } // CreateFoundryTransitionBlockFromInput consumes the given foundry output, then build block by increasing the minted amount by 1. -func (d *DockerTestFramework) CreateFoundryTransitionBlockFromInput(issuerId iotago.AccountID, inputId iotago.OutputID) (iotago.FoundryID, iotago.OutputID, *iotago.Block) { +func (d *DockerTestFramework) CreateFoundryTransitionBlockFromInput(issuerID iotago.AccountID, inputID iotago.OutputID) (iotago.FoundryID, iotago.OutputID, *iotago.Block) { ctx := context.TODO() clt := d.wallet.DefaultClient() - issuer := d.wallet.Account(issuerId) + issuer := d.wallet.Account(issuerID) issuerResp, congestionResp := d.PrepareBlockIssuance(ctx, clt, issuer.Address) - signedTx := d.wallet.TransitionFoundry(issuerId, inputId, issuerResp) - txId, err := signedTx.Transaction.ID() + signedTx := d.wallet.TransitionFoundry(issuerID, inputID, issuerResp) + txID, err := signedTx.Transaction.ID() require.NoError(d.Testing, err) + //nolint:forcetypeassert return signedTx.Transaction.Outputs[1].(*iotago.FoundryOutput).MustFoundryID(), - iotago.OutputIDFromTransactionIDAndIndex(txId, 1), - d.CreateBlock(ctx, signedTx, issuerId, congestionResp, issuerResp) + iotago.OutputIDFromTransactionIDAndIndex(txID, 1), + d.CreateBlock(signedTx, issuerID, congestionResp, issuerResp) } // CreateAccountBlockFromInput consumes the given output, which should be either an basic output with implicit address, then build block with the given account output options. Note that after the returned transaction is issued, remember to update the account information in the wallet with AddAccount(). -func (d *DockerTestFramework) CreateAccountBlockFromInput(inputId iotago.OutputID, opts ...options.Option[builder.AccountOutputBuilder]) (*AccountData, iotago.OutputID, *iotago.Block) { +func (d *DockerTestFramework) CreateAccountBlockFromInput(inputID iotago.OutputID) (*AccountData, iotago.OutputID, *iotago.Block) { ctx := context.TODO() clt := d.wallet.DefaultClient() - input := d.wallet.Output(inputId) + input := d.wallet.Output(inputID) // check if the given input is an BasicOutput with implicit address implicitOutput, ok := input.Output.(*iotago.BasicOutput) require.True(d.Testing, ok) require.Equal(d.Testing, iotago.AddressImplicitAccountCreation, implicitOutput.UnlockConditionSet().Address().Address.Type()) - accAddress := iotago.AccountAddressFromOutputID(inputId) + accAddress := iotago.AccountAddressFromOutputID(inputID) issuerResp, congestionResp := d.PrepareBlockIssuance(ctx, clt, accAddress) fullAccount, signedTx := d.wallet.TransitionImplicitAccountToAccountOutput(input.ID, issuerResp) - txId, err := signedTx.Transaction.ID() + txID, err := signedTx.Transaction.ID() require.NoError(d.Testing, err) return fullAccount, - iotago.OutputIDFromTransactionIDAndIndex(txId, 0), - d.CreateBlock(ctx, signedTx, fullAccount.ID, congestionResp, issuerResp) + iotago.OutputIDFromTransactionIDAndIndex(txID, 0), + d.CreateBlock(signedTx, fullAccount.ID, congestionResp, issuerResp) } // CreateImplicitAccount requests faucet funds and creates an implicit account. It already wait until the transaction is committed and the created account is useable. @@ -499,23 +527,26 @@ func (d *DockerTestFramework) CreateAccount(opts ...options.Option[builder.Accou } // DelegateToValidator requests faucet funds and delegate the UTXO output to the validator. -func (d *DockerTestFramework) DelegateToValidator(fromId iotago.AccountID, validator *Node) iotago.EpochIndex { - from := d.wallet.Account(fromId) - clt := d.wallet.Clients[validator.Name] +func (d *DockerTestFramework) DelegateToValidator(fromID iotago.AccountID, accountAddress *iotago.AccountAddress) (iotago.OutputID, *iotago.DelegationOutput) { + from := d.wallet.Account(fromID) + clt := d.wallet.DefaultClient() // requesting faucet funds as delegation input ctx := context.TODO() fundsOutputID := d.RequestFaucetFunds(ctx, iotago.AddressEd25519) issuerResp, congestionResp := d.PrepareBlockIssuance(ctx, clt, from.Address) - signedTx := d.wallet.CreateDelegationFromInput(fromId, validator, fundsOutputID, issuerResp) + signedTx := d.wallet.CreateDelegationFromInput(fromID, accountAddress, fundsOutputID, issuerResp) d.SubmitPayload(ctx, signedTx, from.ID, congestionResp, issuerResp) d.AwaitTransactionPayloadAccepted(ctx, signedTx.Transaction.MustID()) - delegationOutput := signedTx.Transaction.Outputs[0].(*iotago.DelegationOutput) + delegationOutput, ok := signedTx.Transaction.Outputs[0].(*iotago.DelegationOutput) + require.True(d.Testing, ok) + + delegationOutputID := iotago.OutputIDFromTransactionIDAndIndex(signedTx.Transaction.MustID(), 0) - return delegationOutput.StartEpoch + return delegationOutputID, delegationOutput } // PrepareBlockIssuance prepares the BlockIssuance and Congestion response, and increase BIC of the issuer if necessary. @@ -530,16 +561,16 @@ func (d *DockerTestFramework) PrepareBlockIssuance(ctx context.Context, clt *nod } // AllotManaTo requests faucet funds then uses it to allots mana from one account to another. -func (d *DockerTestFramework) AllotManaTo(fromId iotago.AccountID, toId iotago.AccountID, manaToAllot iotago.Mana) { - from := d.wallet.Account(fromId) - to := d.wallet.Account(toId) +func (d *DockerTestFramework) AllotManaTo(fromID iotago.AccountID, toID iotago.AccountID, manaToAllot iotago.Mana) { + from := d.wallet.Account(fromID) + to := d.wallet.Account(toID) // requesting faucet funds for allotment ctx := context.TODO() fundsOutputID := d.RequestFaucetFunds(ctx, iotago.AddressEd25519) clt := d.wallet.DefaultClient() issuerResp, congestionResp := d.PrepareBlockIssuance(ctx, clt, from.Address) - signedTx := d.wallet.AllotManaFromAccount(fromId, toId, manaToAllot, fundsOutputID, issuerResp) + signedTx := d.wallet.AllotManaFromAccount(fromID, toID, manaToAllot, fundsOutputID) blkID := d.SubmitPayload(ctx, signedTx, from.ID, congestionResp, issuerResp) fmt.Println("Allot mana transaction sent, blkID:", blkID.ToHex(), ", txID:", signedTx.Transaction.MustID().ToHex(), ", slot:", blkID.Slot()) @@ -561,18 +592,18 @@ func (d *DockerTestFramework) AllotManaTo(fromId iotago.AccountID, toId iotago.A } // CreateNativeToken request faucet funds then use it to create native token for the account, and returns the updated Account. -func (d *DockerTestFramework) CreateNativeToken(fromId iotago.AccountID, mintedAmount iotago.BaseToken, maxSupply iotago.BaseToken) { +func (d *DockerTestFramework) CreateNativeToken(fromID iotago.AccountID, mintedAmount iotago.BaseToken, maxSupply iotago.BaseToken) { require.GreaterOrEqual(d.Testing, maxSupply, mintedAmount) ctx := context.TODO() clt := d.wallet.DefaultClient() - from := d.wallet.Account(fromId) + from := d.wallet.Account(fromID) // requesting faucet funds for native token creation fundsOutputID := d.RequestFaucetFunds(ctx, iotago.AddressEd25519) issuerResp, congestionResp := d.PrepareBlockIssuance(ctx, clt, from.Address) - signedTx := d.wallet.CreateFoundryAndNativeTokensFromInput(fromId, fundsOutputID, mintedAmount, maxSupply, issuerResp) + signedTx := d.wallet.CreateFoundryAndNativeTokensFromInput(fromID, fundsOutputID, mintedAmount, maxSupply, issuerResp) blkID := d.SubmitPayload(ctx, signedTx, from.ID, congestionResp, issuerResp) @@ -584,8 +615,9 @@ func (d *DockerTestFramework) CreateNativeToken(fromId iotago.AccountID, mintedA // wait for the account to be committed d.AwaitCommitment(blkID.Slot()) - from = d.wallet.Account(fromId) + from = d.wallet.Account(fromID) d.AssertIndexerAccount(from) + //nolint:forcetypeassert d.AssertIndexerFoundry(signedTx.Transaction.Outputs[1].(*iotago.FoundryOutput).MustFoundryID()) } @@ -611,7 +643,7 @@ func (d *DockerTestFramework) RequestFaucetFunds(ctx context.Context, addressTyp Output: output, }) - fmt.Println("Faucet funds received, txID:", outputID.TransactionID().ToHex(), ", amount:", output.BaseTokenAmount(), ", mana:", output.StoredMana()) + fmt.Printf("Faucet funds received, txID: %s, amount: %d, mana: %d", outputID.TransactionID().ToHex(), output.BaseTokenAmount(), output.StoredMana()) return outputID } @@ -621,7 +653,7 @@ func (d *DockerTestFramework) Stop() { defer fmt.Println("Stop the network.....done") _ = exec.Command("docker", "compose", "down").Run() - _ = exec.Command("rm", d.snapshotPath).Run() + _ = exec.Command("rm", d.snapshotPath).Run() //nolint:gosec } func (d *DockerTestFramework) StopContainer(containerName ...string) error { @@ -696,12 +728,12 @@ func (d *DockerTestFramework) GetContainersConfigs() { } } -func (d *DockerTestFramework) CreateBlock(ctx context.Context, payload iotago.Payload, issuerId iotago.AccountID, congestionResp *api.CongestionResponse, issuerResp *api.IssuanceBlockHeaderResponse) *iotago.Block { +func (d *DockerTestFramework) CreateBlock(payload iotago.Payload, issuerID iotago.AccountID, congestionResp *api.CongestionResponse, issuerResp *api.IssuanceBlockHeaderResponse) *iotago.Block { clt := d.wallet.DefaultClient() issuingTime := time.Now() apiForSlot := clt.APIForSlot(clt.LatestAPI().TimeProvider().SlotFromTime(issuingTime)) blockBuilder := builder.NewBasicBlockBuilder(apiForSlot) - issuer := d.wallet.Account(issuerId) + issuer := d.wallet.Account(issuerID) commitmentID, err := issuerResp.LatestCommitment.ID() require.NoError(d.Testing, err) @@ -715,7 +747,7 @@ func (d *DockerTestFramework) CreateBlock(ctx context.Context, payload iotago.Pa ShallowLikeParents(issuerResp.ShallowLikeParents). Payload(payload). CalculateAndSetMaxBurnedMana(congestionResp.ReferenceManaCost). - Sign(issuerId, lo.Return1(d.wallet.KeyPair(issuer.AddressIndex))) + Sign(issuerID, lo.Return1(d.wallet.KeyPair(issuer.AddressIndex))) blk, err := blockBuilder.Build() require.NoError(d.Testing, err) @@ -730,10 +762,10 @@ func (d *DockerTestFramework) SubmitBlock(ctx context.Context, blk *iotago.Block require.NoError(d.Testing, err) } -func (d *DockerTestFramework) SubmitPayload(ctx context.Context, payload iotago.Payload, issuerId iotago.AccountID, congestionResp *api.CongestionResponse, issuerResp *api.IssuanceBlockHeaderResponse) iotago.BlockID { +func (d *DockerTestFramework) SubmitPayload(ctx context.Context, payload iotago.Payload, issuerID iotago.AccountID, congestionResp *api.CongestionResponse, issuerResp *api.IssuanceBlockHeaderResponse) iotago.BlockID { clt := d.wallet.DefaultClient() - blk := d.CreateBlock(ctx, payload, issuerId, congestionResp, issuerResp) + blk := d.CreateBlock(payload, issuerID, congestionResp, issuerResp) blkID, err := clt.SubmitBlock(ctx, blk) require.NoError(d.Testing, err) diff --git a/tools/docker-network/tests/eventapi.go b/tools/docker-network/tests/eventapi.go index 0077ed3c6..85b13e206 100644 --- a/tools/docker-network/tests/eventapi.go +++ b/tools/docker-network/tests/eventapi.go @@ -192,6 +192,7 @@ func (d *DockerTestFramework) AssertTransactionMetadataByTransactionID(ctx conte if txID.Compare(metadata.TransactionID) == 0 { fmt.Println(metadata.TransactionState) finishChan <- struct{}{} + return } @@ -222,17 +223,14 @@ func (d *DockerTestFramework) AssertBlockMetadataConfirmedBlocks(ctx context.Con }() } -func (d *DockerTestFramework) AssertOutput(ctx context.Context, eventClt *nodeclient.EventAPIClient, outputId iotago.OutputID, finishChan chan struct{}) { - outputMetadataChan, subInfo := eventClt.OutputWithMetadataByOutputID(outputId) +func (d *DockerTestFramework) AssertOutput(ctx context.Context, eventClt *nodeclient.EventAPIClient, outputID iotago.OutputID, finishChan chan struct{}) { + outputMetadataChan, subInfo := eventClt.OutputWithMetadataByOutputID(outputID) require.Nil(d.Testing, subInfo.Error()) go func() { defer subInfo.Close() d.assertOutputMetadataTopics(ctx, "AssertOutput", outputMetadataChan, func(resp *api.OutputWithMetadataResponse) bool { - if outputId.Compare(resp.Metadata.OutputID) == 0 { - return true - } - return false + return outputID.Compare(resp.Metadata.OutputID) == 0 }, finishChan) }() } @@ -273,90 +271,102 @@ func (d *DockerTestFramework) AssertOutputsWithMetadataByUnlockConditionAndAddre return true } } + return false }, finishChan) }() } -func (d *DockerTestFramework) AssertDelegationOutput(ctx context.Context, eventClt *nodeclient.EventAPIClient, delegationId iotago.DelegationID, finishChan chan struct{}) { - outputMetadataChan, subInfo := eventClt.OutputsWithMetadataByDelegationID(delegationId) +func (d *DockerTestFramework) AssertDelegationOutput(ctx context.Context, eventClt *nodeclient.EventAPIClient, delegationID iotago.DelegationID, finishChan chan struct{}) { + outputMetadataChan, subInfo := eventClt.OutputsWithMetadataByDelegationID(delegationID) require.Nil(d.Testing, subInfo.Error()) go func() { defer subInfo.Close() d.assertOutputMetadataTopics(ctx, "AssertDelegationOutput", outputMetadataChan, func(resp *api.OutputWithMetadataResponse) bool { if resp.Output.Type() == iotago.OutputDelegation { - o := resp.Output.(*iotago.DelegationOutput) + o, ok := resp.Output.(*iotago.DelegationOutput) + require.True(d.Testing, ok) actualDelegationID := o.DelegationID if actualDelegationID.Empty() { actualDelegationID = iotago.DelegationIDFromOutputID(resp.Metadata.OutputID) } - return delegationId.Matches(actualDelegationID) + return delegationID.Matches(actualDelegationID) } + return false }, finishChan) }() } -func (d *DockerTestFramework) AssertFoundryOutput(ctx context.Context, eventClt *nodeclient.EventAPIClient, foundryId iotago.FoundryID, finishChan chan struct{}) { - outputMetadataChan, subInfo := eventClt.OutputsWithMetadataByFoundryID(foundryId) +func (d *DockerTestFramework) AssertFoundryOutput(ctx context.Context, eventClt *nodeclient.EventAPIClient, foundryID iotago.FoundryID, finishChan chan struct{}) { + outputMetadataChan, subInfo := eventClt.OutputsWithMetadataByFoundryID(foundryID) require.Nil(d.Testing, subInfo.Error()) go func() { defer subInfo.Close() d.assertOutputMetadataTopics(ctx, "AssertFoundryOutput", outputMetadataChan, func(resp *api.OutputWithMetadataResponse) bool { if resp.Output.Type() == iotago.OutputFoundry { - o := resp.Output.(*iotago.FoundryOutput) - return foundryId.Matches(o.MustFoundryID()) + o, ok := resp.Output.(*iotago.FoundryOutput) + require.True(d.Testing, ok) + + return foundryID.Matches(o.MustFoundryID()) } + return false }, finishChan) }() } -func (d *DockerTestFramework) AssertAccountOutput(ctx context.Context, eventClt *nodeclient.EventAPIClient, accountId iotago.AccountID, finishChan chan struct{}) { - outputMetadataChan, subInfo := eventClt.OutputsWithMetadataByAccountID(accountId) +func (d *DockerTestFramework) AssertAccountOutput(ctx context.Context, eventClt *nodeclient.EventAPIClient, accountID iotago.AccountID, finishChan chan struct{}) { + outputMetadataChan, subInfo := eventClt.OutputsWithMetadataByAccountID(accountID) require.Nil(d.Testing, subInfo.Error()) go func() { defer subInfo.Close() d.assertOutputMetadataTopics(ctx, "AssertAccountOutput", outputMetadataChan, func(resp *api.OutputWithMetadataResponse) bool { if resp.Output.Type() == iotago.OutputAccount { - o := resp.Output.(*iotago.AccountOutput) + o, ok := resp.Output.(*iotago.AccountOutput) + require.True(d.Testing, ok) actualAccountID := o.AccountID if actualAccountID.Empty() { actualAccountID = iotago.AccountIDFromOutputID(resp.Metadata.OutputID) } - return accountId.Matches(o.AccountID) + + return accountID.Matches(actualAccountID) } + return false }, finishChan) }() } -func (d *DockerTestFramework) AssertAnchorOutput(ctx context.Context, eventClt *nodeclient.EventAPIClient, anchorId iotago.AnchorID, finishChan chan struct{}) { - outputMetadataChan, subInfo := eventClt.OutputsWithMetadataByAnchorID(anchorId) +func (d *DockerTestFramework) AssertAnchorOutput(ctx context.Context, eventClt *nodeclient.EventAPIClient, anchorID iotago.AnchorID, finishChan chan struct{}) { + outputMetadataChan, subInfo := eventClt.OutputsWithMetadataByAnchorID(anchorID) require.Nil(d.Testing, subInfo.Error()) go func() { defer subInfo.Close() d.assertOutputMetadataTopics(ctx, "AssertNFTOutput", outputMetadataChan, func(resp *api.OutputWithMetadataResponse) bool { if resp.Output.Type() == iotago.OutputAnchor { - o := resp.Output.(*iotago.AnchorOutput) + o, ok := resp.Output.(*iotago.AnchorOutput) + require.True(d.Testing, ok) actualAnchorID := o.AnchorID if actualAnchorID.Empty() { actualAnchorID = iotago.AnchorIDFromOutputID(resp.Metadata.OutputID) } - return anchorId.Matches(o.AnchorID) + + return anchorID.Matches(actualAnchorID) } + return false }, finishChan) }() } -func (d *DockerTestFramework) AssertNFTOutput(ctx context.Context, eventClt *nodeclient.EventAPIClient, nftId iotago.NFTID, finishChan chan struct{}) { - outputMetadataChan, subInfo := eventClt.OutputsWithMetadataByNFTID(nftId) +func (d *DockerTestFramework) AssertNFTOutput(ctx context.Context, eventClt *nodeclient.EventAPIClient, nftID iotago.NFTID, finishChan chan struct{}) { + outputMetadataChan, subInfo := eventClt.OutputsWithMetadataByNFTID(nftID) require.Nil(d.Testing, subInfo.Error()) go func() { @@ -368,8 +378,10 @@ func (d *DockerTestFramework) AssertNFTOutput(ctx context.Context, eventClt *nod if actualNFTID.Empty() { actualNFTID = iotago.NFTIDFromOutputID(resp.Metadata.OutputID) } - return nftId.Matches(actualNFTID) + + return nftID.Matches(actualNFTID) } + return false }, finishChan) }() @@ -391,10 +403,12 @@ func (d *DockerTestFramework) assertCommitmentsTopics(ctx context.Context, calle require.ElementsMatch(d.Testing, expectedSlots, slots) finishChan <- struct{}{} + return } case <-ctx.Done(): - fmt.Println("topic", callerName, "does not get expected commitments, recieved slots:", slots) + fmt.Println("topic", callerName, "does not get expected commitments, received slots:", slots) + return } } @@ -417,11 +431,13 @@ func (d *DockerTestFramework) assertBlocksTopics(ctx context.Context, callerName if len(blkIDs) == len(expectedBlocks) { require.ElementsMatch(d.Testing, expectedBlockIDsSlice, blkIDs) finishChan <- struct{}{} + return } } case <-ctx.Done(): fmt.Println("topic", callerName, "does not get expected Blocks, received blocks:", blkIDs) + return } } @@ -445,11 +461,13 @@ func (d *DockerTestFramework) assertBlockMetadataTopics(ctx context.Context, cal if len(blkIDs) == len(expectedBlocks) { require.ElementsMatch(d.Testing, expectedBlockIDsSlice, blkIDs) finishChan <- struct{}{} + return } } case <-ctx.Done(): fmt.Println("topic", callerName, "does not get expected BlockMetadata, received blocks:", blkIDs) + return } } @@ -464,10 +482,12 @@ func (d *DockerTestFramework) assertOutputMetadataTopics(ctx context.Context, ca case outputMetadata := <-receivedChan: if matchFunc(outputMetadata) { finishChan <- struct{}{} + return } case <-ctx.Done(): fmt.Println("topic", callerName, "does not get expected outputs") + return } } diff --git a/tools/docker-network/tests/eventapi_test.go b/tools/docker-network/tests/eventapi_test.go index 02b78f59b..ed87a064e 100644 --- a/tools/docker-network/tests/eventapi_test.go +++ b/tools/docker-network/tests/eventapi_test.go @@ -68,12 +68,12 @@ func test_Commitments(t *testing.T, d *DockerTestFramework) { // prepare the expected commitments to be received expectedLatestSlots := make([]iotago.SlotIndex, 0) for i := infoResp.Status.LatestCommitmentID.Slot() + 2; i < infoResp.Status.LatestCommitmentID.Slot()+6; i++ { - expectedLatestSlots = append(expectedLatestSlots, iotago.SlotIndex(i)) + expectedLatestSlots = append(expectedLatestSlots, i) } expectedFinalizedSlots := make([]iotago.SlotIndex, 0) for i := infoResp.Status.LatestFinalizedSlot + 2; i < infoResp.Status.LatestFinalizedSlot+6; i++ { - expectedFinalizedSlots = append(expectedFinalizedSlots, iotago.SlotIndex(i)) + expectedFinalizedSlots = append(expectedFinalizedSlots, i) } assertions := []func(){ @@ -87,7 +87,7 @@ func test_Commitments(t *testing.T, d *DockerTestFramework) { } // wait until all topics receives all expected objects - err = AwaitEventAPITopics(t, d.optsWaitFor, cancel, finish, totalTopics) + err = AwaitEventAPITopics(d.optsWaitFor, cancel, finish, totalTopics) require.NoError(t, err) } @@ -104,7 +104,7 @@ func test_ValidationBlocks(t *testing.T, d *DockerTestFramework) { defer eventClt.Close() // prepare the expected commitments to be received - validators := make(map[string]struct{}, 0) + validators := make(map[string]struct{}) nodes := d.Nodes("V1", "V2", "V3", "V4") for _, node := range nodes { validators[node.AccountAddressBech32] = struct{}{} @@ -122,7 +122,7 @@ func test_ValidationBlocks(t *testing.T, d *DockerTestFramework) { } // wait until all topics receives all expected objects - err = AwaitEventAPITopics(t, d.optsWaitFor, cancel, finish, totalTopics) + err = AwaitEventAPITopics(d.optsWaitFor, cancel, finish, totalTopics) require.NoError(t, err) } @@ -140,7 +140,7 @@ func test_BasicTaggedDataBlocks(t *testing.T, d *DockerTestFramework) { account := d.CreateAccount() // prepare data blocks to send - expectedBlocks := make(map[string]*iotago.Block, 0) + expectedBlocks := make(map[string]*iotago.Block) for i := 0; i < 10; i++ { blk := d.CreateTaggedDataBlock(account.ID, []byte("tag")) expectedBlocks[blk.MustID().ToHex()] = blk @@ -162,7 +162,7 @@ func test_BasicTaggedDataBlocks(t *testing.T, d *DockerTestFramework) { } // wait until all topics starts listening - err = AwaitEventAPITopics(t, d.optsWaitFor, cancel, finish, totalTopics) + err = AwaitEventAPITopics(d.optsWaitFor, cancel, finish, totalTopics) require.NoError(t, err) // issue blocks @@ -174,7 +174,7 @@ func test_BasicTaggedDataBlocks(t *testing.T, d *DockerTestFramework) { }() // wait until all topics receives all expected objects - err = AwaitEventAPITopics(t, d.optsWaitFor, cancel, finish, totalTopics) + err = AwaitEventAPITopics(d.optsWaitFor, cancel, finish, totalTopics) require.NoError(t, err) } @@ -193,7 +193,7 @@ func test_DelegationTransactionBlocks(t *testing.T, d *DockerTestFramework) { fundsOutputID := d.RequestFaucetFunds(ctx, iotago.AddressEd25519) // prepare data blocks to send - delegationId, outputId, blk := d.CreateDelegationBlockFromInput(account.ID, d.Node("V2"), fundsOutputID) + delegationId, outputId, blk := d.CreateDelegationBlockFromInput(account.ID, d.Node("V2").AccountAddress(t), fundsOutputID) expectedBlocks := map[string]*iotago.Block{ blk.MustID().ToHex(): blk, } @@ -224,7 +224,7 @@ func test_DelegationTransactionBlocks(t *testing.T, d *DockerTestFramework) { // d.AssertTransactionMetadataIncludedBlocks(ctx, eventClt, outputId.TransactionID(), finish) // wait until all topics starts listening - err = AwaitEventAPITopics(t, d.optsWaitFor, cancel, finish, totalTopics) + err = AwaitEventAPITopics(d.optsWaitFor, cancel, finish, totalTopics) require.NoError(t, err) // issue blocks @@ -236,7 +236,7 @@ func test_DelegationTransactionBlocks(t *testing.T, d *DockerTestFramework) { }() // wait until all topics receives all expected objects - err = AwaitEventAPITopics(t, d.optsWaitFor, cancel, finish, totalTopics) + err = AwaitEventAPITopics(d.optsWaitFor, cancel, finish, totalTopics) require.NoError(t, err) } @@ -282,11 +282,12 @@ func test_AccountTransactionBlocks(t *testing.T, d *DockerTestFramework) { assertion() } + // TODO test transactioMetadataTopics // d.AssertTransactionMetadataByTransactionID(ctx, eventClt, outputId.TransactionID(), finish) // d.AssertTransactionMetadataIncludedBlocks(ctx, eventClt, outputId.TransactionID(), finish) // wait until all topics starts listening - err = AwaitEventAPITopics(t, d.optsWaitFor, cancel, finish, totalTopics) + err = AwaitEventAPITopics(d.optsWaitFor, cancel, finish, totalTopics) require.NoError(t, err) // issue blocks @@ -301,7 +302,7 @@ func test_AccountTransactionBlocks(t *testing.T, d *DockerTestFramework) { d.wallet.AddAccount(fullAccount.ID, fullAccount) // wait until all topics receives all expected objects - err = AwaitEventAPITopics(t, d.optsWaitFor, cancel, finish, totalTopics) + err = AwaitEventAPITopics(d.optsWaitFor, cancel, finish, totalTopics) require.NoError(t, err) } } @@ -354,7 +355,7 @@ func test_FoundryTransactionBlocks(t *testing.T, d *DockerTestFramework) { // d.AssertTransactionMetadataIncludedBlocks(ctx, eventClt, outputId.TransactionID(), finish) // wait until all topics starts listening - err = AwaitEventAPITopics(t, d.optsWaitFor, cancel, finish, totalTopics) + err = AwaitEventAPITopics(d.optsWaitFor, cancel, finish, totalTopics) require.NoError(t, err) // issue blocks @@ -366,7 +367,7 @@ func test_FoundryTransactionBlocks(t *testing.T, d *DockerTestFramework) { }() // wait until all topics receives all expected objects - err = AwaitEventAPITopics(t, d.optsWaitFor, cancel, finish, totalTopics) + err = AwaitEventAPITopics(d.optsWaitFor, cancel, finish, totalTopics) require.NoError(t, err) } } @@ -413,11 +414,8 @@ func test_NFTTransactionBlocks(t *testing.T, d *DockerTestFramework) { assertion() } - // d.AssertTransactionMetadataByTransactionID(ctx, eventClt, outputId.TransactionID(), finish) - // d.AssertTransactionMetadataIncludedBlocks(ctx, eventClt, outputId.TransactionID(), finish) - // wait until all topics starts listening - err = AwaitEventAPITopics(t, d.optsWaitFor, cancel, finish, totalTopics) + err = AwaitEventAPITopics(d.optsWaitFor, cancel, finish, totalTopics) require.NoError(t, err) // issue blocks @@ -429,7 +427,7 @@ func test_NFTTransactionBlocks(t *testing.T, d *DockerTestFramework) { }() // wait until all topics receives all expected objects - err = AwaitEventAPITopics(t, d.optsWaitFor, cancel, finish, totalTopics) + err = AwaitEventAPITopics(d.optsWaitFor, cancel, finish, totalTopics) require.NoError(t, err) } } diff --git a/tools/docker-network/tests/options.go b/tools/docker-network/tests/options.go index 314eeee00..7cb79d3d9 100644 --- a/tools/docker-network/tests/options.go +++ b/tools/docker-network/tests/options.go @@ -16,7 +16,7 @@ var DefaultProtocolParametersOptions = []options.Option[iotago.V3ProtocolParamet iotago.WithNetworkOptions("docker", iotago.PrefixTestnet), } -// DefaultSnapshotOptions are the default snapshot options for the docker network. +// DefaultAccountOptions are the default snapshot options for the docker network. func DefaultAccountOptions(protocolParams *iotago.V3ProtocolParameters) []options.Option[snapshotcreator.Options] { return []options.Option[snapshotcreator.Options]{ snapshotcreator.WithAccounts(presets.AccountsDockerFunc(protocolParams)...), @@ -60,7 +60,6 @@ func WithTick(tick time.Duration) options.Option[DockerTestFramework] { } } -// ///// Account Output options 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...) diff --git a/tools/docker-network/tests/utils.go b/tools/docker-network/tests/utils.go index ce2ef9d97..eaffcfd24 100644 --- a/tools/docker-network/tests/utils.go +++ b/tools/docker-network/tests/utils.go @@ -10,8 +10,9 @@ import ( "io" "net/http" "os" + "regexp" "sort" - "testing" + "strconv" "time" "github.com/stretchr/testify/require" @@ -95,7 +96,7 @@ func (d *DockerTestFramework) AssertIndexerFoundry(foundryID iotago.FoundryID) { func (d *DockerTestFramework) AssertValidatorExists(accountAddr *iotago.AccountAddress) { d.Eventually(func() error { for _, node := range d.Nodes() { - _, err := d.wallet.Clients[node.Name].StakingAccount(context.TODO(), accountAddr) + _, err := d.wallet.Clients[node.Name].Validator(context.TODO(), accountAddr) if err != nil { return err } @@ -112,12 +113,12 @@ func (d *DockerTestFramework) AssertCommittee(expectedEpoch iotago.EpochIndex, e sort.Strings(expectedCommitteeMember) status := d.NodeStatus("V1") - api := d.wallet.DefaultClient().CommittedAPI() - expectedSlotStart := api.TimeProvider().EpochStart(expectedEpoch) + testAPI := d.wallet.DefaultClient().CommittedAPI() + expectedSlotStart := testAPI.TimeProvider().EpochStart(expectedEpoch) require.Greater(d.Testing, expectedSlotStart, status.LatestAcceptedBlockSlot) slotToWait := expectedSlotStart - status.LatestAcceptedBlockSlot - secToWait := time.Duration(slotToWait) * time.Duration(api.ProtocolParameters().SlotDurationInSeconds()) * time.Second + secToWait := time.Duration(slotToWait) * time.Duration(testAPI.ProtocolParameters().SlotDurationInSeconds()) * time.Second fmt.Println("Wait for ", secToWait, "until expected epoch: ", expectedEpoch) time.Sleep(secToWait) @@ -227,6 +228,17 @@ func (d *DockerTestFramework) AwaitCommitment(targetSlot iotago.SlotIndex) { } } +func (d *DockerTestFramework) AwaitFinalization(targetSlot iotago.SlotIndex) { + d.Eventually(func() error { + currentFinalisedSlot := d.NodeStatus("V1").LatestFinalizedSlot + if targetSlot > currentFinalisedSlot { + return ierrors.Errorf("finalized slot %d is not reached yet", targetSlot) + } + + return nil + }) +} + func (d *DockerTestFramework) AwaitAddressUnspentOutputAccepted(ctx context.Context, addr iotago.Address) (outputID iotago.OutputID, output iotago.Output, err error) { indexerClt, err := d.wallet.DefaultClient().Indexer(ctx) require.NoError(d.Testing, err) @@ -305,7 +317,7 @@ func createLogDirectory(testName string) string { return dir } -func AwaitEventAPITopics(t *testing.T, duration time.Duration, cancleFunc context.CancelFunc, receiveChan chan struct{}, numOfTopics int) error { +func AwaitEventAPITopics(duration time.Duration, cancleFunc context.CancelFunc, receiveChan chan struct{}, numOfTopics int) error { counter := 0 timer := time.NewTimer(duration) defer timer.Stop() @@ -334,5 +346,33 @@ func getDelegationStartEpoch(api iotago.API, commitmentSlot iotago.SlotIndex) io if pastBoundedSlot <= registrationSlot { return pastBoundedEpoch + 1 } + return pastBoundedEpoch + 2 } + +func isStatusCode(err error, status int) bool { + if err == nil { + return false + } + code, err := extractStatusCode(err.Error()) + if err != nil { + return false + } + + return code == status +} + +func extractStatusCode(errorMessage string) (int, error) { + re := regexp.MustCompile(`code=(\d+)`) + matches := re.FindStringSubmatch(errorMessage) + if len(matches) != 2 { + return 0, ierrors.Errorf("unable to extract status code from error message") + } + + statusCode, err := strconv.Atoi(matches[1]) + if err != nil { + return 0, err + } + + return statusCode, nil +} diff --git a/tools/docker-network/tests/wallet.go b/tools/docker-network/tests/wallet.go index af09ab9e0..dd2bdd5ed 100644 --- a/tools/docker-network/tests/wallet.go +++ b/tools/docker-network/tests/wallet.go @@ -7,6 +7,7 @@ import ( "math/big" "sync/atomic" "testing" + "time" "github.com/stretchr/testify/require" @@ -76,12 +77,12 @@ func (w *DockerWallet) DefaultClient() *nodeclient.Client { return w.Clients["V1"] } -func (w *DockerWallet) AddOutput(outputId iotago.OutputID, output *OutputData) { - w.outputs[outputId] = output +func (w *DockerWallet) AddOutput(outputID iotago.OutputID, output *OutputData) { + w.outputs[outputID] = output } -func (w *DockerWallet) AddAccount(accountId iotago.AccountID, data *AccountData) { - w.accounts[accountId] = data +func (w *DockerWallet) AddAccount(accountID iotago.AccountID, data *AccountData) { + w.accounts[accountID] = data } func (w *DockerWallet) Output(outputName iotago.OutputID) *OutputData { @@ -93,10 +94,10 @@ func (w *DockerWallet) Output(outputName iotago.OutputID) *OutputData { return output } -func (w *DockerWallet) Account(accountId iotago.AccountID) *AccountData { - acc, exists := w.accounts[accountId] +func (w *DockerWallet) Account(accountID iotago.AccountID) *AccountData { + acc, exists := w.accounts[accountID] if !exists { - panic(ierrors.Errorf("account %s not registered in wallet", accountId.ToHex())) + panic(ierrors.Errorf("account %s not registered in wallet", accountID.ToHex())) } return acc @@ -130,10 +131,10 @@ func (w *DockerWallet) AddressSigner(indexes ...uint32) iotago.AddressSigner { return w.keyManager.AddressSigner(indexes...) } -func (w *DockerWallet) AllotManaFromAccount(fromId iotago.AccountID, toId iotago.AccountID, manaToAllot iotago.Mana, inputId iotago.OutputID, issuerResp *api.IssuanceBlockHeaderResponse) *iotago.SignedTransaction { - from := w.Account(fromId) - to := w.Account(toId) - input := w.Output(inputId) +func (w *DockerWallet) AllotManaFromAccount(fromID iotago.AccountID, toID iotago.AccountID, manaToAllot iotago.Mana, inputID iotago.OutputID) *iotago.SignedTransaction { + from := w.Account(fromID) + to := w.Account(toID) + input := w.Output(inputID) currentSlot := w.DefaultClient().LatestAPI().TimeProvider().CurrentSlot() apiForSlot := w.DefaultClient().APIForSlot(currentSlot) @@ -164,9 +165,9 @@ func (w *DockerWallet) AllotManaFromAccount(fromId iotago.AccountID, toId iotago Build() require.NoError(w.Testing, err) - allotmentOutputId := iotago.OutputIDFromTransactionIDAndIndex(signedTx.Transaction.MustID(), 0) - w.AddOutput(allotmentOutputId, &OutputData{ - ID: allotmentOutputId, + allotmentOutputID := iotago.OutputIDFromTransactionIDAndIndex(signedTx.Transaction.MustID(), 0) + w.AddOutput(allotmentOutputID, &OutputData{ + ID: allotmentOutputID, Output: basicOutput, Address: input.Address, AddressIndex: input.AddressIndex, @@ -175,9 +176,9 @@ func (w *DockerWallet) AllotManaFromAccount(fromId iotago.AccountID, toId iotago return signedTx } -func (w *DockerWallet) AllotManaFromInput(toId iotago.AccountID, inputId iotago.OutputID, issuerResp *api.IssuanceBlockHeaderResponse) *iotago.SignedTransaction { - to := w.Account(toId) - input := w.Output(inputId) +func (w *DockerWallet) AllotManaFromInput(toID iotago.AccountID, inputID iotago.OutputID) *iotago.SignedTransaction { + to := w.Account(toID) + input := w.Output(inputID) currentSlot := w.DefaultClient().LatestAPI().TimeProvider().CurrentSlot() apiForSlot := w.DefaultClient().APIForSlot(currentSlot) @@ -195,10 +196,11 @@ func (w *DockerWallet) AllotManaFromInput(toId iotago.AccountID, inputId iotago. AllotAllMana(currentSlot, to.ID, 0). SetCreationSlot(currentSlot). Build() + require.NoError(w.Testing, err) - delegationOutputId := iotago.OutputIDFromTransactionIDAndIndex(signedTx.Transaction.MustID(), 0) - w.AddOutput(delegationOutputId, &OutputData{ - ID: delegationOutputId, + delegationOutputID := iotago.OutputIDFromTransactionIDAndIndex(signedTx.Transaction.MustID(), 0) + w.AddOutput(delegationOutputID, &OutputData{ + ID: delegationOutputID, Output: basicOutput, Address: input.Address, AddressIndex: input.AddressIndex, @@ -207,8 +209,8 @@ func (w *DockerWallet) AllotManaFromInput(toId iotago.AccountID, inputId iotago. return signedTx } -func (w *DockerWallet) TransitionImplicitAccountToAccountOutput(inputId iotago.OutputID, issuerResp *api.IssuanceBlockHeaderResponse, opts ...options.Option[builder.AccountOutputBuilder]) (*AccountData, *iotago.SignedTransaction) { - input := w.Output(inputId) +func (w *DockerWallet) TransitionImplicitAccountToAccountOutput(inputID iotago.OutputID, issuerResp *api.IssuanceBlockHeaderResponse, opts ...options.Option[builder.AccountOutputBuilder]) (*AccountData, *iotago.SignedTransaction) { + input := w.Output(inputID) accountID := iotago.AccountIDFromOutputID(input.ID) accountAddress, ok := accountID.ToAddress().(*iotago.AccountAddress) @@ -220,6 +222,7 @@ func (w *DockerWallet) TransitionImplicitAccountToAccountOutput(inputId iotago.O // transition to a full account with new Ed25519 address and staking feature accEd25519AddrIndex, accEd25519Addr := w.Address() accPrivateKey, _ := w.KeyPair(accEd25519AddrIndex) + //nolint:forcetypeassert accBlockIssuerKey := iotago.Ed25519PublicKeyHashBlockIssuerKeyFromPublicKey(hiveEd25519.PublicKey(accPrivateKey.Public().(ed25519.PublicKey))) accountOutput := options.Apply(builder.NewAccountOutputBuilder(accEd25519Addr, input.Output.BaseTokenAmount()), opts, func(b *builder.AccountOutputBuilder) { @@ -241,9 +244,9 @@ func (w *DockerWallet) TransitionImplicitAccountToAccountOutput(inputId iotago.O Build() require.NoError(w.Testing, err) - accountOutputId := iotago.OutputIDFromTransactionIDAndIndex(signedTx.Transaction.MustID(), 0) - w.AddOutput(accountOutputId, &OutputData{ - ID: accountOutputId, + accountOutputID := iotago.OutputIDFromTransactionIDAndIndex(signedTx.Transaction.MustID(), 0) + w.AddOutput(accountOutputID, &OutputData{ + ID: accountOutputID, Output: accountOutput, Address: accEd25519Addr, AddressIndex: accEd25519AddrIndex, @@ -254,26 +257,23 @@ func (w *DockerWallet) TransitionImplicitAccountToAccountOutput(inputId iotago.O Address: accountAddress, AddressIndex: accEd25519AddrIndex, Output: accountOutput, - OutputID: accountOutputId, + OutputID: accountOutputID, } return accountInfo, signedTx } -func (w *DockerWallet) CreateDelegationFromInput(issuerId iotago.AccountID, validator *Node, inputId iotago.OutputID, issuerResp *api.IssuanceBlockHeaderResponse) *iotago.SignedTransaction { - input := w.Output(inputId) - - _, validatorAccountAddr, err := iotago.ParseBech32(validator.AccountAddressBech32) - require.NoError(w.Testing, err) +func (w *DockerWallet) CreateDelegationFromInput(issuerID iotago.AccountID, validatorAccountAddr *iotago.AccountAddress, inputID iotago.OutputID, issuerResp *api.IssuanceBlockHeaderResponse) *iotago.SignedTransaction { + input := w.Output(inputID) currentSlot := w.DefaultClient().LatestAPI().TimeProvider().CurrentSlot() apiForSlot := w.DefaultClient().APIForSlot(currentSlot) // construct delegation transaction - delegationOutput := builder.NewDelegationOutputBuilder(validatorAccountAddr.(*iotago.AccountAddress), input.Address, input.Output.BaseTokenAmount()). + //nolint:forcetypeassert + delegationOutput := builder.NewDelegationOutputBuilder(validatorAccountAddr, input.Address, input.Output.BaseTokenAmount()). StartEpoch(getDelegationStartEpoch(apiForSlot, issuerResp.LatestCommitment.Slot)). DelegatedAmount(input.Output.BaseTokenAmount()).MustBuild() - signedTx, err := builder.NewTransactionBuilder(apiForSlot, w.AddressSigner(input.AddressIndex)). AddInput(&builder.TxInput{ UnlockTarget: input.Address, @@ -283,13 +283,13 @@ func (w *DockerWallet) CreateDelegationFromInput(issuerId iotago.AccountID, vali AddOutput(delegationOutput). SetCreationSlot(currentSlot). AddCommitmentInput(&iotago.CommitmentInput{CommitmentID: lo.Return1(issuerResp.LatestCommitment.ID())}). - AllotAllMana(currentSlot, issuerId, 0). + AllotAllMana(currentSlot, issuerID, 0). Build() require.NoError(w.Testing, err) - delegationOutputId := iotago.OutputIDFromTransactionIDAndIndex(signedTx.Transaction.MustID(), 0) - w.AddOutput(delegationOutputId, &OutputData{ - ID: delegationOutputId, + delegationOutputID := iotago.OutputIDFromTransactionIDAndIndex(signedTx.Transaction.MustID(), 0) + w.AddOutput(delegationOutputID, &OutputData{ + ID: delegationOutputID, Output: delegationOutput, Address: input.Address, AddressIndex: input.AddressIndex, @@ -298,10 +298,10 @@ func (w *DockerWallet) CreateDelegationFromInput(issuerId iotago.AccountID, vali return signedTx } -func (w *DockerWallet) CreateFoundryAndNativeTokensFromInput(issuerId iotago.AccountID, inputId iotago.OutputID, mintedAmount iotago.BaseToken, maxSupply iotago.BaseToken, issuerResp *api.IssuanceBlockHeaderResponse) *iotago.SignedTransaction { - input := w.Output(inputId) +func (w *DockerWallet) CreateFoundryAndNativeTokensFromInput(issuerID iotago.AccountID, inputID iotago.OutputID, mintedAmount iotago.BaseToken, maxSupply iotago.BaseToken, issuerResp *api.IssuanceBlockHeaderResponse) *iotago.SignedTransaction { + input := w.Output(inputID) - issuer := w.Account(issuerId) + issuer := w.Account(issuerID) currentSlot := w.DefaultClient().LatestAPI().TimeProvider().CurrentSlot() apiForSlot := w.DefaultClient().APIForSlot(currentSlot) @@ -338,21 +338,22 @@ func (w *DockerWallet) CreateFoundryAndNativeTokensFromInput(issuerId iotago.Acc AddOutput(accTransitionOutput). AddOutput(foundryOutput). SetCreationSlot(currentSlot). - AddBlockIssuanceCreditInput(&iotago.BlockIssuanceCreditInput{AccountID: issuerId}). + AddBlockIssuanceCreditInput(&iotago.BlockIssuanceCreditInput{AccountID: issuerID}). AddCommitmentInput(&iotago.CommitmentInput{CommitmentID: lo.Return1(issuerResp.LatestCommitment.ID())}). - AllotAllMana(currentSlot, issuerId, 0). + AllotAllMana(currentSlot, issuerID, 0). Build() require.NoError(w.Testing, err) - foundryOutputId := iotago.OutputIDFromTransactionIDAndIndex(signedTx.Transaction.MustID(), 1) - w.AddOutput(foundryOutputId, &OutputData{ - ID: foundryOutputId, + foundryOutputID := iotago.OutputIDFromTransactionIDAndIndex(signedTx.Transaction.MustID(), 1) + w.AddOutput(foundryOutputID, &OutputData{ + ID: foundryOutputID, Output: foundryOutput, Address: issuer.Address, }) - w.AddAccount(issuerId, &AccountData{ - ID: issuerId, + //nolint:forcetypeassert + w.AddAccount(issuerID, &AccountData{ + ID: issuerID, Address: issuer.Address, AddressIndex: issuer.AddressIndex, Output: signedTx.Transaction.Outputs[0].(*iotago.AccountOutput), @@ -363,9 +364,9 @@ func (w *DockerWallet) CreateFoundryAndNativeTokensFromInput(issuerId iotago.Acc } // TransitionFoundry transitions a FoundryOutput by increasing the native token amount on the output by one. -func (w *DockerWallet) TransitionFoundry(issuerId iotago.AccountID, inputId iotago.OutputID, issuerResp *api.IssuanceBlockHeaderResponse) *iotago.SignedTransaction { - issuer := w.Account(issuerId) - input := w.Output(inputId) +func (w *DockerWallet) TransitionFoundry(issuerID iotago.AccountID, inputID iotago.OutputID, issuerResp *api.IssuanceBlockHeaderResponse) *iotago.SignedTransaction { + issuer := w.Account(issuerID) + input := w.Output(inputID) inputFoundry, isFoundry := input.Output.(*iotago.FoundryOutput) require.True(w.Testing, isFoundry) @@ -411,19 +412,20 @@ func (w *DockerWallet) TransitionFoundry(issuerId iotago.AccountID, inputId iota SetCreationSlot(currentSlot). AddBlockIssuanceCreditInput(&iotago.BlockIssuanceCreditInput{AccountID: issuer.ID}). AddCommitmentInput(&iotago.CommitmentInput{CommitmentID: lo.Return1(issuerResp.LatestCommitment.ID())}). - AllotAllMana(currentSlot, issuerId, 0). + AllotAllMana(currentSlot, issuerID, 0). Build() require.NoError(w.Testing, err) - foundryOutputId := iotago.OutputIDFromTransactionIDAndIndex(signedTx.Transaction.MustID(), 1) - w.AddOutput(foundryOutputId, &OutputData{ - ID: foundryOutputId, + foundryOutputID := iotago.OutputIDFromTransactionIDAndIndex(signedTx.Transaction.MustID(), 1) + w.AddOutput(foundryOutputID, &OutputData{ + ID: foundryOutputID, Output: outputFoundry, Address: issuer.Address, }) - w.AddAccount(issuerId, &AccountData{ - ID: issuerId, + //nolint:forcetypeassert + w.AddAccount(issuerID, &AccountData{ + ID: issuerID, Address: issuer.Address, AddressIndex: issuer.AddressIndex, Output: signedTx.Transaction.Outputs[0].(*iotago.AccountOutput), @@ -433,8 +435,8 @@ func (w *DockerWallet) TransitionFoundry(issuerId iotago.AccountID, inputId iota return signedTx } -func (w *DockerWallet) CreateNFTFromInput(issuerId iotago.AccountID, inputId iotago.OutputID, issuerResp *api.IssuanceBlockHeaderResponse, opts ...options.Option[builder.NFTOutputBuilder]) *iotago.SignedTransaction { - input := w.Output(inputId) +func (w *DockerWallet) CreateNFTFromInput(issuerID iotago.AccountID, inputID iotago.OutputID, issuerResp *api.IssuanceBlockHeaderResponse, opts ...options.Option[builder.NFTOutputBuilder]) *iotago.SignedTransaction { + input := w.Output(inputID) currentSlot := w.DefaultClient().LatestAPI().TimeProvider().CurrentSlot() apiForSlot := w.DefaultClient().APIForSlot(currentSlot) @@ -452,15 +454,15 @@ func (w *DockerWallet) CreateNFTFromInput(issuerId iotago.AccountID, inputId iot }). AddOutput(nftOutput). SetCreationSlot(currentSlot). - AddBlockIssuanceCreditInput(&iotago.BlockIssuanceCreditInput{AccountID: issuerId}). + AddBlockIssuanceCreditInput(&iotago.BlockIssuanceCreditInput{AccountID: issuerID}). AddCommitmentInput(&iotago.CommitmentInput{CommitmentID: lo.Return1(issuerResp.LatestCommitment.ID())}). - AllotAllMana(currentSlot, issuerId, 0). + AllotAllMana(currentSlot, issuerID, 0). Build() require.NoError(w.Testing, err) - nftOutputId := iotago.OutputIDFromTransactionIDAndIndex(signedTx.Transaction.MustID(), 0) - w.AddOutput(nftOutputId, &OutputData{ - ID: nftOutputId, + nftOutputID := iotago.OutputIDFromTransactionIDAndIndex(signedTx.Transaction.MustID(), 0) + w.AddOutput(nftOutputID, &OutputData{ + ID: nftOutputID, Output: nftOutput, Address: nftAddress, AddressIndex: nftAddressIndex, @@ -468,3 +470,23 @@ func (w *DockerWallet) CreateNFTFromInput(issuerId iotago.AccountID, inputId iot return signedTx } + +func (w *DockerWallet) CreateBasicOutputFromInput(input *OutputData, issuerAccountID iotago.AccountID) *iotago.SignedTransaction { + currentSlot := w.DefaultClient().LatestAPI().TimeProvider().SlotFromTime(time.Now()) + apiForSlot := w.DefaultClient().APIForSlot(currentSlot) + _, ed25519Addr := w.Address() + basicOutput := builder.NewBasicOutputBuilder(ed25519Addr, input.Output.BaseTokenAmount()).MustBuild() + signedTx, err := builder.NewTransactionBuilder(apiForSlot, w.AddressSigner(input.AddressIndex)). + AddInput(&builder.TxInput{ + UnlockTarget: input.Address, + InputID: input.ID, + Input: input.Output, + }). + AddOutput(basicOutput). + SetCreationSlot(currentSlot). + AllotAllMana(currentSlot, issuerAccountID, 0). + Build() + require.NoError(w.Testing, err) + + return signedTx +} diff --git a/tools/gendoc/go.mod b/tools/gendoc/go.mod index 2d118cf37..c18412a13 100644 --- a/tools/gendoc/go.mod +++ b/tools/gendoc/go.mod @@ -73,7 +73,7 @@ require ( github.com/iotaledger/inx-app v1.0.0-rc.3.0.20240307101848-db58eb9353ec // indirect github.com/iotaledger/inx/go v1.0.0-rc.2.0.20240307100839-48553e1d2022 // indirect github.com/iotaledger/iota-crypto-demo v0.0.0-20240216103559-27ca8dffd1e7 // indirect - github.com/iotaledger/iota.go/v4 v4.0.0-20240307091827-db3c503615a6 // indirect + github.com/iotaledger/iota.go/v4 v4.0.0-20240307175623-0904c71fcb38 // indirect github.com/ipfs/boxo v0.18.0 // indirect github.com/ipfs/go-cid v0.4.1 // indirect github.com/ipfs/go-datastore v0.6.0 // indirect diff --git a/tools/gendoc/go.sum b/tools/gendoc/go.sum index 62c3e8765..3e21cae0c 100644 --- a/tools/gendoc/go.sum +++ b/tools/gendoc/go.sum @@ -322,8 +322,8 @@ github.com/iotaledger/inx/go v1.0.0-rc.2.0.20240307100839-48553e1d2022 h1:I178Sa github.com/iotaledger/inx/go v1.0.0-rc.2.0.20240307100839-48553e1d2022/go.mod h1:jTFxIWiMUdAwO263jlJCSWcNLqEkgYEVOFXfjp5aNJM= github.com/iotaledger/iota-crypto-demo v0.0.0-20240216103559-27ca8dffd1e7 h1:t6k4MqiUov0FrBb2o2JhKlOVSdlPbIQWM8ivYHL0G0g= github.com/iotaledger/iota-crypto-demo v0.0.0-20240216103559-27ca8dffd1e7/go.mod h1:do+N3LpeDEi9qselEC4XcjqGoRc7cWGiqBtIeBOKEMs= -github.com/iotaledger/iota.go/v4 v4.0.0-20240307091827-db3c503615a6 h1:dzOaHlQDJecS3SDqwaUDQfTYjZDvwqRdn1/6bCjFpgM= -github.com/iotaledger/iota.go/v4 v4.0.0-20240307091827-db3c503615a6/go.mod h1:8UQOTI7CC5R/3TurawUFuBZbkb37RzW8m4q8Hp7ct30= +github.com/iotaledger/iota.go/v4 v4.0.0-20240307175623-0904c71fcb38 h1:NizJ3CALLCcJowtAtkNuDlpE4gd4qjaWZkp/kTZfeYk= +github.com/iotaledger/iota.go/v4 v4.0.0-20240307175623-0904c71fcb38/go.mod h1:8UQOTI7CC5R/3TurawUFuBZbkb37RzW8m4q8Hp7ct30= github.com/ipfs/boxo v0.18.0 h1:MOL9/AgoV3e7jlVMInicaSdbgralfqSsbkc31dZ9tmw= github.com/ipfs/boxo v0.18.0/go.mod h1:pIZgTWdm3k3pLF9Uq6MB8JEcW07UDwNJjlXW1HELW80= github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s=