diff --git a/components/dashboard/explorer_routes.go b/components/dashboard/explorer_routes.go index fb9c6ef6e..ade3c3622 100644 --- a/components/dashboard/explorer_routes.go +++ b/components/dashboard/explorer_routes.go @@ -118,7 +118,7 @@ func createExplorerBlock(block *model.Block, cachedBlock *blocks.Block, metadata SolidificationTimestamp: 0, IssuanceTimestamp: iotaBlk.IssuingTime.Unix(), SequenceNumber: 0, - IssuerID: iotaBlk.IssuerID.String(), + IssuerID: iotaBlk.IssuerID.ToHex(), Signature: hexutil.EncodeHex(sigBytes), StrongParents: iotaBlk.Block.StrongParentIDs().ToHex(), WeakParents: iotaBlk.Block.WeakParentIDs().ToHex(), @@ -164,16 +164,16 @@ func createExplorerBlock(block *model.Block, cachedBlock *blocks.Block, metadata t.Scheduled = cachedBlock.IsScheduled() t.ObjectivelyInvalid = cachedBlock.IsInvalid() t.StrongChildren = lo.Map(cachedBlock.StrongChildren(), func(childBlock *blocks.Block) string { - return childBlock.ID().String() + return childBlock.ID().ToHex() }) t.WeakChildren = lo.Map(cachedBlock.WeakChildren(), func(childBlock *blocks.Block) string { - return childBlock.ID().String() + return childBlock.ID().ToHex() }) t.LikedInsteadChildren = lo.Map(cachedBlock.ShallowLikeChildren(), func(childBlock *blocks.Block) string { - return childBlock.ID().String() + return childBlock.ID().ToHex() }) t.ConflictIDs = lo.Map(cachedBlock.ConflictIDs().ToSlice(), func(conflictID iotago.TransactionID) string { - return conflictID.String() + return conflictID.ToHex() }) } else { switch metadata.BlockState { diff --git a/components/debugapi/transactions.go b/components/debugapi/transactions.go index 890e7660d..860f1fdd0 100644 --- a/components/debugapi/transactions.go +++ b/components/debugapi/transactions.go @@ -17,7 +17,10 @@ func init() { func storeTransactionsPerSlot(scd *notarization.SlotCommittedDetails) error { slot := scd.Commitment.Slot() - stateDiff := deps.Protocol.MainEngineInstance().Ledger.MemPool().StateDiff(slot) + stateDiff, err := deps.Protocol.MainEngineInstance().Ledger.MemPool().StateDiff(slot) + if err != nil { + return ierrors.Wrapf(err, "failed to retrieve state diff for slot %d", slot) + } mutationsTree := ads.NewSet(mapdb.NewMapDB(), iotago.TransactionID.Bytes, iotago.TransactionIDFromBytes) tcs := &TransactionsChangesResponse{ Index: slot, diff --git a/components/metrics/metrics_commitments.go b/components/metrics/metrics_commitments.go index 595f914cd..273dc6a6e 100644 --- a/components/metrics/metrics_commitments.go +++ b/components/metrics/metrics_commitments.go @@ -30,7 +30,7 @@ var CommitmentsMetrics = collector.NewCollection(commitmentsNamespace, collector.WithPruningDelay(10*time.Minute), collector.WithInitFunc(func() { deps.Protocol.Events.Engine.Notarization.SlotCommitted.Hook(func(details *notarization.SlotCommittedDetails) { - deps.Collector.Update(commitmentsNamespace, latestCommitment, float64(details.Commitment.ID().Slot()), details.Commitment.ID().String()) + deps.Collector.Update(commitmentsNamespace, latestCommitment, float64(details.Commitment.ID().Slot()), "C "+details.Commitment.ID().ToHex()) }, event.WithWorkerPool(Component.WorkerPool)) }), )), diff --git a/components/restapi/component.go b/components/restapi/component.go index c0289ef20..d6dc4b470 100644 --- a/components/restapi/component.go +++ b/components/restapi/component.go @@ -15,6 +15,7 @@ import ( "github.com/iotaledger/hive.go/app" "github.com/iotaledger/hive.go/ierrors" "github.com/iotaledger/inx-app/pkg/httpserver" + "github.com/iotaledger/iota-core/pkg/blockhandler" "github.com/iotaledger/iota-core/pkg/daemon" "github.com/iotaledger/iota-core/pkg/jwt" protocolpkg "github.com/iotaledger/iota-core/pkg/protocol" @@ -99,6 +100,12 @@ func provide(c *dig.Container) error { Component.LogPanic(err) } + if err := c.Provide(func(deps dependencies) *blockhandler.BlockHandler { + return blockhandler.New(deps.Protocol) + }); err != nil { + Component.LogPanic(err) + } + return nil } diff --git a/components/validator/issuer.go b/components/validator/issuer.go index 97c0497a6..6c05d67a3 100644 --- a/components/validator/issuer.go +++ b/components/validator/issuer.go @@ -4,18 +4,18 @@ import ( "context" "time" + "github.com/iotaledger/hive.go/ierrors" "github.com/iotaledger/iota-core/pkg/model" iotago "github.com/iotaledger/iota.go/v4" "github.com/iotaledger/iota.go/v4/builder" ) +var ErrBlockTooRecent = ierrors.New("block is too recent compared to latest commitment") + func issueValidatorBlock(ctx context.Context) { // Get the main engine instance in case it changes mid-execution. engineInstance := deps.Protocol.MainEngineInstance() - // Get the latest commitment from the engine before to avoid race conditions if something is committed after we fix block issuing time. - latestCommitment := engineInstance.Storage.Settings().LatestCommitment() - blockIssuingTime := time.Now() nextBroadcast := blockIssuingTime.Add(ParamsValidator.CommitteeBroadcastInterval) @@ -39,11 +39,28 @@ func issueValidatorBlock(ctx context.Context) { parents := engineInstance.TipSelection.SelectTips(iotago.BlockTypeValidationMaxParents) + addressableCommitment, err := getAddressableCommitment(deps.Protocol.CurrentAPI().TimeProvider().SlotFromTime(blockIssuingTime)) + if err != nil && ierrors.Is(err, ErrBlockTooRecent) { + commitment, parentID, reviveChainErr := reviveChain(blockIssuingTime) + if reviveChainErr != nil { + Component.LogError("error reviving chain: %s", reviveChainErr.Error()) + return + } + + addressableCommitment = commitment + parents = make(model.ParentReferences) + parents[iotago.StrongParentType] = []iotago.BlockID{parentID} + } else if err != nil { + Component.LogWarnf("error getting commitment: %s", err.Error()) + + return + } + // create the validation block here using the validation block builder from iota.go validationBlock, err := builder.NewValidationBlockBuilder(deps.Protocol.CurrentAPI()). IssuingTime(blockIssuingTime). - SlotCommitmentID(latestCommitment.ID()). ProtocolParametersHash(protocolParametersHash). + SlotCommitmentID(addressableCommitment.MustID()). HighestSupportedVersion(deps.Protocol.LatestAPI().Version()). LatestFinalizedSlot(engineInstance.SyncManager.LatestFinalizedSlot()). StrongParents(parents[iotago.StrongParentType]). @@ -77,5 +94,67 @@ func issueValidatorBlock(ctx context.Context) { } Component.LogDebugf("Issued validator block: %s - commitment %s %d - latest finalized slot %d", modelBlock.ID(), modelBlock.ProtocolBlock().SlotCommitmentID, modelBlock.ProtocolBlock().SlotCommitmentID.Slot(), modelBlock.ProtocolBlock().LatestFinalizedSlot) +} + +func reviveChain(issuingTime time.Time) (*iotago.Commitment, iotago.BlockID, error) { + lastCommittedSlot := deps.Protocol.MainEngineInstance().Storage.Settings().LatestCommitment().Slot() + apiForSlot := deps.Protocol.APIForSlot(lastCommittedSlot) + + // Get a rootblock as recent as possible for the parent. + parentBlockID := iotago.EmptyBlockID + for rootBlock := range deps.Protocol.MainEngineInstance().EvictionState.ActiveRootBlocks() { + if rootBlock.Slot() > parentBlockID.Slot() { + parentBlockID = rootBlock + } + + // Exit the loop if we found a rootblock in the last committed slot (which is the highest we can get). + if parentBlockID.Slot() == lastCommittedSlot { + break + } + } + + issuingSlot := apiForSlot.TimeProvider().SlotFromTime(issuingTime) + + // Force commitments until minCommittableAge relative to the block's issuing time. We basically "pretend" that + // this block was already accepted at the time of issuing so that we have a commitment to reference. + if issuingSlot < apiForSlot.ProtocolParameters().MinCommittableAge() { // Should never happen as we're beyond maxCommittableAge which is > minCommittableAge. + return nil, iotago.EmptyBlockID, ierrors.Errorf("issuing slot %d is smaller than min committable age %d", issuingSlot, apiForSlot.ProtocolParameters().MinCommittableAge()) + } + commitUntilSlot := issuingSlot - apiForSlot.ProtocolParameters().MinCommittableAge() + + if err := deps.Protocol.MainEngineInstance().Notarization.ForceCommitUntil(commitUntilSlot); err != nil { + return nil, iotago.EmptyBlockID, ierrors.Wrapf(err, "failed to force commit until slot %d", commitUntilSlot) + } + + commitment, err := deps.Protocol.MainEngineInstance().Storage.Commitments().Load(commitUntilSlot) + if err != nil { + return nil, iotago.EmptyBlockID, ierrors.Wrapf(err, "failed to commit until slot %d to revive chain", commitUntilSlot) + } + + return commitment.Commitment(), parentBlockID, nil +} + +func getAddressableCommitment(blockSlot iotago.SlotIndex) (*iotago.Commitment, error) { + protoParams := deps.Protocol.CurrentAPI().ProtocolParameters() + commitment := deps.Protocol.MainEngineInstance().Storage.Settings().LatestCommitment().Commitment() + + if blockSlot > commitment.Slot+protoParams.MaxCommittableAge() { + return nil, ierrors.Wrapf(ErrBlockTooRecent, "can't issue block: block slot %d is too far in the future, latest commitment is %d", blockSlot, commitment.Slot) + } + + if blockSlot < commitment.Slot+protoParams.MinCommittableAge() { + if blockSlot < protoParams.MinCommittableAge() || commitment.Slot < protoParams.MinCommittableAge() { + return commitment, nil + } + + commitmentSlot := commitment.Slot - protoParams.MinCommittableAge() + loadedCommitment, err := deps.Protocol.MainEngineInstance().Storage.Commitments().Load(commitmentSlot) + if err != nil { + return nil, ierrors.Wrapf(err, "error loading valid commitment of slot %d according to minCommittableAge from storage", commitmentSlot) + } + + return loadedCommitment.Commitment(), nil + } + return commitment, nil } diff --git a/go.mod b/go.mod index 37dae713d..675c5d5ac 100644 --- a/go.mod +++ b/go.mod @@ -11,19 +11,19 @@ require ( github.com/google/uuid v1.3.1 github.com/gorilla/websocket v1.5.0 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 - github.com/iotaledger/hive.go/ads v0.0.0-20231010083704-a2a91fb9e0ab - github.com/iotaledger/hive.go/app v0.0.0-20231010083704-a2a91fb9e0ab - github.com/iotaledger/hive.go/constraints v0.0.0-20231010083704-a2a91fb9e0ab - github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231010083704-a2a91fb9e0ab - github.com/iotaledger/hive.go/crypto v0.0.0-20231010083704-a2a91fb9e0ab - github.com/iotaledger/hive.go/ds v0.0.0-20231010083704-a2a91fb9e0ab - github.com/iotaledger/hive.go/ierrors v0.0.0-20231010083704-a2a91fb9e0ab - github.com/iotaledger/hive.go/kvstore v0.0.0-20231010083704-a2a91fb9e0ab - github.com/iotaledger/hive.go/lo v0.0.0-20231010083704-a2a91fb9e0ab - github.com/iotaledger/hive.go/logger v0.0.0-20231010083704-a2a91fb9e0ab - github.com/iotaledger/hive.go/runtime v0.0.0-20231010083704-a2a91fb9e0ab - github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231010083704-a2a91fb9e0ab - github.com/iotaledger/hive.go/stringify v0.0.0-20231010083704-a2a91fb9e0ab + github.com/iotaledger/hive.go/ads v0.0.0-20231010121205-23c1f3fcddd5 + github.com/iotaledger/hive.go/app v0.0.0-20231010121205-23c1f3fcddd5 + github.com/iotaledger/hive.go/constraints v0.0.0-20231010121205-23c1f3fcddd5 + github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231010121205-23c1f3fcddd5 + github.com/iotaledger/hive.go/crypto v0.0.0-20231010121205-23c1f3fcddd5 + github.com/iotaledger/hive.go/ds v0.0.0-20231010121205-23c1f3fcddd5 + github.com/iotaledger/hive.go/ierrors v0.0.0-20231010121205-23c1f3fcddd5 + github.com/iotaledger/hive.go/kvstore v0.0.0-20231010121205-23c1f3fcddd5 + github.com/iotaledger/hive.go/lo v0.0.0-20231010121205-23c1f3fcddd5 + github.com/iotaledger/hive.go/logger v0.0.0-20231010121205-23c1f3fcddd5 + github.com/iotaledger/hive.go/runtime v0.0.0-20231010121205-23c1f3fcddd5 + github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231010121205-23c1f3fcddd5 + github.com/iotaledger/hive.go/stringify v0.0.0-20231010121205-23c1f3fcddd5 github.com/iotaledger/inx-app v1.0.0-rc.3.0.20231005192108-08a985c2e217 github.com/iotaledger/inx/go v1.0.0-rc.2.0.20231005191759-16a3636128c4 github.com/iotaledger/iota.go/v4 v4.0.0-20231005184534-62e6761a7b7c diff --git a/go.sum b/go.sum index 563a6d695..8532876d3 100644 --- a/go.sum +++ b/go.sum @@ -275,32 +275,32 @@ github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJ github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7 h1:dTrD7X2PTNgli6EbS4tV9qu3QAm/kBU3XaYZV2xdzys= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7/go.mod h1:ZRdPu684P0fQ1z8sXz4dj9H5LWHhz4a9oCtvjunkSrw= -github.com/iotaledger/hive.go/ads v0.0.0-20231010083704-a2a91fb9e0ab h1:3U1ADSPHU9GahpWujnkwOlGxA+NMl9l0j3ch4ISH1S0= -github.com/iotaledger/hive.go/ads v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:IAWZ/5It5P8B41mWyJXJVcG0vuikVRaTFKQnr2D2q+c= -github.com/iotaledger/hive.go/app v0.0.0-20231010083704-a2a91fb9e0ab h1:ePgcMl1XOXoF4kNfwMGn63GXZSihv+Z2t1GWLKqkGOw= -github.com/iotaledger/hive.go/app v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:eiZgbcwTDZ7d9hEait2EAwAhixWhceW4MXmuVk2EcEw= -github.com/iotaledger/hive.go/constraints v0.0.0-20231010083704-a2a91fb9e0ab h1:u7wN87wrmlPbUpNZhHfNKN6HLLQdY/zICIiJgVCSBvM= -github.com/iotaledger/hive.go/constraints v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:dOBOM2s4se3HcWefPe8sQLUalGXJ8yVXw58oK8jke3s= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231010083704-a2a91fb9e0ab h1:u4h5GdvR1MTNZHYkLx8USCadbkMkISZyYXudH5qEhZQ= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231010083704-a2a91fb9e0ab/go.mod h1:jn3TNmiNRIiQm/rS4VD+7wFHI2+UXABHvCA3PbQxBqI= -github.com/iotaledger/hive.go/crypto v0.0.0-20231010083704-a2a91fb9e0ab h1:0wGm1JPCBNK7h9WYtFZhSxmbhPmiBEzeeonUfJmVASE= -github.com/iotaledger/hive.go/crypto v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:jP68na941d9uq7RtnA8aQ/FtIGRGz/51cU4uXrInQFU= -github.com/iotaledger/hive.go/ds v0.0.0-20231010083704-a2a91fb9e0ab h1:Wlv4bCfT6IZ3LD3jzHCgT00MdL0LR7kzhlW3zQohKKA= -github.com/iotaledger/hive.go/ds v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:ZrqsjIJS2QCgGp7Ki+l4hWJQgzfBObUCemb5Upwlx18= -github.com/iotaledger/hive.go/ierrors v0.0.0-20231010083704-a2a91fb9e0ab h1:RMKlQlTpWSSfauU3KgNyv/8WGzTTx3G4KzFuLf1EhrM= -github.com/iotaledger/hive.go/ierrors v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:HcE8B5lP96enc/OALTb2/rIIi+yOLouRoHOKRclKmC8= -github.com/iotaledger/hive.go/kvstore v0.0.0-20231010083704-a2a91fb9e0ab h1:50bOe5+XlIj0pPqcMNkOl+nf4BEITIhWuSr1j3fieLI= -github.com/iotaledger/hive.go/kvstore v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:DeP4JF4N995LteD0+/o7NsW1bO5IXURIJ27A69Ca5+Y= -github.com/iotaledger/hive.go/lo v0.0.0-20231010083704-a2a91fb9e0ab h1:o9JT//S67PRsrEir1GSdlNz2hLYfG+6SDayxmJm+jKQ= -github.com/iotaledger/hive.go/lo v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:4oKCdMEhHMLCudBz79kuvJmgSY/DhfVePNIyJhew/80= -github.com/iotaledger/hive.go/logger v0.0.0-20231010083704-a2a91fb9e0ab h1:AWU4PRv5No/ghKH02JY3/N9Xpv2he+byVLRd7DPHJGg= -github.com/iotaledger/hive.go/logger v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:sxqWRdZ1OOxwkxVczuGcW034Mpt2vFh5ebJHO++ZYeI= -github.com/iotaledger/hive.go/runtime v0.0.0-20231010083704-a2a91fb9e0ab h1:ZE7MENkq9l8B+uIt1ulFfflDPnYX4Xxk6UYbzAPr8/U= -github.com/iotaledger/hive.go/runtime v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:fXVyQ1MAwxe/EmjAnG8WcQqbzGk9EW/FsJ/n16H/f/w= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231010083704-a2a91fb9e0ab h1:ebO3VhgcS2Dd8zBe+8Lizd8RoV00TUAm06tY0HGZmIE= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231010083704-a2a91fb9e0ab/go.mod h1:IJgaaxbgKCsNat18jlJJEAxCY2oVYR3F30B+M4vJ89I= -github.com/iotaledger/hive.go/stringify v0.0.0-20231010083704-a2a91fb9e0ab h1:nDsUCc2/EPFspZcKk3T4HklhUeI+B5ed3W+HoTciYzA= -github.com/iotaledger/hive.go/stringify v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:FTo/UWzNYgnQ082GI9QVM9HFDERqf9rw9RivNpqrnTs= +github.com/iotaledger/hive.go/ads v0.0.0-20231010121205-23c1f3fcddd5 h1:lbrYweNe+DB/kh7OYlogJMQfEDRpriyIgA+MmHmb01k= +github.com/iotaledger/hive.go/ads v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:IAWZ/5It5P8B41mWyJXJVcG0vuikVRaTFKQnr2D2q+c= +github.com/iotaledger/hive.go/app v0.0.0-20231010121205-23c1f3fcddd5 h1:P2BE6RylO7mlEtJSbNyuRz6OcJTNs0MZ0qFIN5lI3Kg= +github.com/iotaledger/hive.go/app v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:q2H/9TE6hvg6TKZI0YoxtbCdrf8O1sYBVjNT2Y3yAIU= +github.com/iotaledger/hive.go/constraints v0.0.0-20231010121205-23c1f3fcddd5 h1:AsgWEa8dpgF0JX3pc+nwaoQLlc5rKEoWAgrYdN1AIRE= +github.com/iotaledger/hive.go/constraints v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:dOBOM2s4se3HcWefPe8sQLUalGXJ8yVXw58oK8jke3s= +github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231010121205-23c1f3fcddd5 h1:T/vnm1B2SFYWz7QiQsQq0AuoJCXKwW3hBCITudvh//c= +github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231010121205-23c1f3fcddd5/go.mod h1:jn3TNmiNRIiQm/rS4VD+7wFHI2+UXABHvCA3PbQxBqI= +github.com/iotaledger/hive.go/crypto v0.0.0-20231010121205-23c1f3fcddd5 h1:8TgnDcl6gIqXqgTnx4wA3LZt8vwAsVfekcKlPK5WRNY= +github.com/iotaledger/hive.go/crypto v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:jP68na941d9uq7RtnA8aQ/FtIGRGz/51cU4uXrInQFU= +github.com/iotaledger/hive.go/ds v0.0.0-20231010121205-23c1f3fcddd5 h1:xyVUPe/tZtS3nkg8fU1GB1ZyUo7D32YfrY0XNA8GZhM= +github.com/iotaledger/hive.go/ds v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:ZrqsjIJS2QCgGp7Ki+l4hWJQgzfBObUCemb5Upwlx18= +github.com/iotaledger/hive.go/ierrors v0.0.0-20231010121205-23c1f3fcddd5 h1:Q8giZLqmIJRlcUS5x1TxVlS5X+iHgZ8zOq4GsK3YWnQ= +github.com/iotaledger/hive.go/ierrors v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:HcE8B5lP96enc/OALTb2/rIIi+yOLouRoHOKRclKmC8= +github.com/iotaledger/hive.go/kvstore v0.0.0-20231010121205-23c1f3fcddd5 h1:nI6LJ32saPs1c3HwA7V3T2jo3bh4+UuhBg1EsBK9N7k= +github.com/iotaledger/hive.go/kvstore v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:DeP4JF4N995LteD0+/o7NsW1bO5IXURIJ27A69Ca5+Y= +github.com/iotaledger/hive.go/lo v0.0.0-20231010121205-23c1f3fcddd5 h1:XVKwwjBJGOUuNzormIYJMWgqyscgusR5WtxA3rxql5g= +github.com/iotaledger/hive.go/lo v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:4oKCdMEhHMLCudBz79kuvJmgSY/DhfVePNIyJhew/80= +github.com/iotaledger/hive.go/logger v0.0.0-20231010121205-23c1f3fcddd5 h1:hp+Bstpyxgeci0eDnEp0CZDbavgwnhDHkBTCf/fBQv4= +github.com/iotaledger/hive.go/logger v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:sxqWRdZ1OOxwkxVczuGcW034Mpt2vFh5ebJHO++ZYeI= +github.com/iotaledger/hive.go/runtime v0.0.0-20231010121205-23c1f3fcddd5 h1:2TxlF+6APu1ZeImwny5EAwfe/n/JVlq+1NpzBybJnCU= +github.com/iotaledger/hive.go/runtime v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:fXVyQ1MAwxe/EmjAnG8WcQqbzGk9EW/FsJ/n16H/f/w= +github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231010121205-23c1f3fcddd5 h1:SV8s8rZIHj2yhGRj8vX2AyA3MOKOWPk4rKiz0mJ/4mw= +github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231010121205-23c1f3fcddd5/go.mod h1:IJgaaxbgKCsNat18jlJJEAxCY2oVYR3F30B+M4vJ89I= +github.com/iotaledger/hive.go/stringify v0.0.0-20231010121205-23c1f3fcddd5 h1:zD60l2AnJmYLfjwhytAILQLZbkgmOPeg7IvFSY6Uv1E= +github.com/iotaledger/hive.go/stringify v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:FTo/UWzNYgnQ082GI9QVM9HFDERqf9rw9RivNpqrnTs= github.com/iotaledger/inx-app v1.0.0-rc.3.0.20231005192108-08a985c2e217 h1:DdrsW+J04ne2j6bjU1yVu+4C4CjpjGFYDEVMtmg0zyA= github.com/iotaledger/inx-app v1.0.0-rc.3.0.20231005192108-08a985c2e217/go.mod h1:OWZGwG4q2Ypd/D6LJicgdPXtw9yYkhaZEvJFhkIm2Ck= github.com/iotaledger/inx/go v1.0.0-rc.2.0.20231005191759-16a3636128c4 h1:uI+sZeZnGzAkM34JDbZBfyRIXrkkd1L0w8qVJ5rGy3E= diff --git a/pkg/model/block.go b/pkg/model/block.go index 557f715d0..d5c212153 100644 --- a/pkg/model/block.go +++ b/pkg/model/block.go @@ -62,6 +62,17 @@ func BlockFromBytes(data []byte, apiProvider iotago.APIProvider) (*Block, error) return newBlock(blockID, iotaBlock, data) } +func BlockFromBytesFunc(apiProvider iotago.APIProvider) func(data []byte) (*Block, int, error) { + return func(data []byte) (*Block, int, error) { + block, err := BlockFromBytes(data, apiProvider) + if err != nil { + return nil, 0, err + } + + return block, len(data), nil + } +} + func (blk *Block) ID() iotago.BlockID { return blk.blockID } @@ -70,6 +81,10 @@ func (blk *Block) Data() []byte { return blk.data } +func (blk *Block) Bytes() ([]byte, error) { + return blk.data, nil +} + func (blk *Block) ProtocolBlock() *iotago.ProtocolBlock { return blk.protocolBlock } diff --git a/pkg/network/protocols/core/events.go b/pkg/network/protocols/core/events.go index 47eb4e771..8577388f6 100644 --- a/pkg/network/protocols/core/events.go +++ b/pkg/network/protocols/core/events.go @@ -17,7 +17,7 @@ type Events struct { AttestationsReceived *event.Event4[*model.Commitment, []*iotago.Attestation, *merklehasher.Proof[iotago.Identifier], peer.ID] AttestationsRequestReceived *event.Event2[iotago.CommitmentID, peer.ID] WarpSyncRequestReceived *event.Event2[iotago.CommitmentID, peer.ID] - WarpSyncResponseReceived *event.Event4[iotago.CommitmentID, iotago.BlockIDs, *merklehasher.Proof[iotago.Identifier], peer.ID] + WarpSyncResponseReceived *event.Event6[iotago.CommitmentID, iotago.BlockIDs, *merklehasher.Proof[iotago.Identifier], iotago.TransactionIDs, *merklehasher.Proof[iotago.Identifier], peer.ID] Error *event.Event2[error, peer.ID] event.Group[Events, *Events] @@ -33,7 +33,7 @@ var NewEvents = event.CreateGroupConstructor(func() (newEvents *Events) { AttestationsReceived: event.New4[*model.Commitment, []*iotago.Attestation, *merklehasher.Proof[iotago.Identifier], peer.ID](), AttestationsRequestReceived: event.New2[iotago.CommitmentID, peer.ID](), WarpSyncRequestReceived: event.New2[iotago.CommitmentID, peer.ID](), - WarpSyncResponseReceived: event.New4[iotago.CommitmentID, iotago.BlockIDs, *merklehasher.Proof[iotago.Identifier], peer.ID](), + WarpSyncResponseReceived: event.New6[iotago.CommitmentID, iotago.BlockIDs, *merklehasher.Proof[iotago.Identifier], iotago.TransactionIDs, *merklehasher.Proof[iotago.Identifier], peer.ID](), Error: event.New2[error, peer.ID](), } }) diff --git a/pkg/network/protocols/core/models/message.pb.go b/pkg/network/protocols/core/models/message.pb.go index 11cc34720..66dbe6a16 100644 --- a/pkg/network/protocols/core/models/message.pb.go +++ b/pkg/network/protocols/core/models/message.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.27.1 -// protoc v3.12.4 +// protoc-gen-go v1.30.0 +// protoc v4.23.4 // source: pkg/network/protocols/core/models/message.proto package models @@ -26,6 +26,7 @@ type Packet struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Body: + // // *Packet_Block // *Packet_BlockRequest // *Packet_SlotCommitment @@ -534,9 +535,11 @@ type WarpSyncResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - CommitmentId []byte `protobuf:"bytes,1,opt,name=commitment_id,json=commitmentId,proto3" json:"commitment_id,omitempty"` - BlockIds []byte `protobuf:"bytes,2,opt,name=block_ids,json=blockIds,proto3" json:"block_ids,omitempty"` - MerkleProof []byte `protobuf:"bytes,3,opt,name=merkle_proof,json=merkleProof,proto3" json:"merkle_proof,omitempty"` + CommitmentId []byte `protobuf:"bytes,1,opt,name=commitment_id,json=commitmentId,proto3" json:"commitment_id,omitempty"` + BlockIds []byte `protobuf:"bytes,2,opt,name=block_ids,json=blockIds,proto3" json:"block_ids,omitempty"` + TangleMerkleProof []byte `protobuf:"bytes,3,opt,name=tangle_merkle_proof,json=tangleMerkleProof,proto3" json:"tangle_merkle_proof,omitempty"` + TransactionIds []byte `protobuf:"bytes,4,opt,name=transaction_ids,json=transactionIds,proto3" json:"transaction_ids,omitempty"` + MutationsMerkleProof []byte `protobuf:"bytes,5,opt,name=mutations_merkle_proof,json=mutationsMerkleProof,proto3" json:"mutations_merkle_proof,omitempty"` } func (x *WarpSyncResponse) Reset() { @@ -585,9 +588,23 @@ func (x *WarpSyncResponse) GetBlockIds() []byte { return nil } -func (x *WarpSyncResponse) GetMerkleProof() []byte { +func (x *WarpSyncResponse) GetTangleMerkleProof() []byte { if x != nil { - return x.MerkleProof + return x.TangleMerkleProof + } + return nil +} + +func (x *WarpSyncResponse) GetTransactionIds() []byte { + if x != nil { + return x.TransactionIds + } + return nil +} + +func (x *WarpSyncResponse) GetMutationsMerkleProof() []byte { + if x != nil { + return x.MutationsMerkleProof } return nil } @@ -659,19 +676,26 @@ var file_pkg_network_protocols_core_models_message_proto_rawDesc = []byte{ 0x0a, 0x0f, 0x57, 0x61, 0x72, 0x70, 0x53, 0x79, 0x6e, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, - 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x22, 0x77, 0x0a, 0x10, 0x57, 0x61, 0x72, 0x70, 0x53, 0x79, - 0x6e, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, - 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0c, 0x52, 0x0c, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, - 0x1b, 0x0a, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x73, 0x12, 0x21, 0x0a, 0x0c, - 0x6d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x0b, 0x6d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x42, - 0x43, 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x6f, - 0x74, 0x61, 0x6c, 0x65, 0x64, 0x67, 0x65, 0x72, 0x2f, 0x69, 0x6f, 0x74, 0x61, 0x2d, 0x63, 0x6f, - 0x72, 0x65, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x6d, 0x6f, - 0x64, 0x65, 0x6c, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x22, 0xe3, 0x01, 0x0a, 0x10, 0x57, 0x61, 0x72, 0x70, 0x53, + 0x79, 0x6e, 0x63, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x63, + 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x0c, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, + 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x73, 0x12, 0x2e, 0x0a, + 0x13, 0x74, 0x61, 0x6e, 0x67, 0x6c, 0x65, 0x5f, 0x6d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x5f, 0x70, + 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x11, 0x74, 0x61, 0x6e, 0x67, + 0x6c, 0x65, 0x4d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x27, 0x0a, + 0x0f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x73, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x73, 0x12, 0x34, 0x0a, 0x16, 0x6d, 0x75, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x5f, 0x6d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x14, 0x6d, 0x75, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x4d, 0x65, 0x72, 0x6b, 0x6c, 0x65, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x42, 0x43, 0x5a, 0x41, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x6f, 0x74, 0x61, 0x6c, + 0x65, 0x64, 0x67, 0x65, 0x72, 0x2f, 0x69, 0x6f, 0x74, 0x61, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, + 0x70, 0x6b, 0x67, 0x2f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2f, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x63, 0x6f, 0x6c, 0x73, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x6d, 0x6f, 0x64, 0x65, 0x6c, + 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/pkg/network/protocols/core/models/message.proto b/pkg/network/protocols/core/models/message.proto index 6ca73a616..11cfa0860 100644 --- a/pkg/network/protocols/core/models/message.proto +++ b/pkg/network/protocols/core/models/message.proto @@ -50,5 +50,7 @@ message WarpSyncRequest { message WarpSyncResponse { bytes commitment_id = 1; bytes block_ids = 2; - bytes merkle_proof = 3; -} \ No newline at end of file + bytes tangle_merkle_proof = 3; + bytes transaction_ids = 4; + bytes mutations_merkle_proof = 5; +} diff --git a/pkg/network/protocols/core/protocol.go b/pkg/network/protocols/core/protocol.go index 5652c001f..c10160474 100644 --- a/pkg/network/protocols/core/protocol.go +++ b/pkg/network/protocols/core/protocol.go @@ -127,7 +127,7 @@ func (p *Protocol) handlePacket(nbr peer.ID, packet proto.Message) (err error) { case *nwmodels.Packet_WarpSyncRequest: p.handleWarpSyncRequest(packetBody.WarpSyncRequest.GetCommitmentId(), nbr) case *nwmodels.Packet_WarpSyncResponse: - p.handleWarpSyncResponse(packetBody.WarpSyncResponse.GetCommitmentId(), packetBody.WarpSyncResponse.GetBlockIds(), packetBody.WarpSyncResponse.GetMerkleProof(), nbr) + p.handleWarpSyncResponse(packetBody.WarpSyncResponse.GetCommitmentId(), packetBody.WarpSyncResponse.GetBlockIds(), packetBody.WarpSyncResponse.GetTangleMerkleProof(), packetBody.WarpSyncResponse.GetTransactionIds(), packetBody.WarpSyncResponse.GetMutationsMerkleProof(), nbr) default: return ierrors.Errorf("unsupported packet; packet=%+v, packetBody=%T-%+v", packet, packetBody, packetBody) } diff --git a/pkg/network/protocols/core/warp_sync.go b/pkg/network/protocols/core/warp_sync.go index 3c9648bfc..ae9206edc 100644 --- a/pkg/network/protocols/core/warp_sync.go +++ b/pkg/network/protocols/core/warp_sync.go @@ -19,14 +19,16 @@ func (p *Protocol) SendWarpSyncRequest(id iotago.CommitmentID, to ...peer.ID) { }}, to...) } -func (p *Protocol) SendWarpSyncResponse(id iotago.CommitmentID, blockIDs iotago.BlockIDs, merkleProof *merklehasher.Proof[iotago.Identifier], to ...peer.ID) { +func (p *Protocol) SendWarpSyncResponse(id iotago.CommitmentID, blockIDs iotago.BlockIDs, tangleMerkleProof *merklehasher.Proof[iotago.Identifier], transactionIDs iotago.TransactionIDs, mutationsMerkleProof *merklehasher.Proof[iotago.Identifier], to ...peer.ID) { serializer := p.apiProvider.APIForSlot(id.Slot()) p.network.Send(&nwmodels.Packet{Body: &nwmodels.Packet_WarpSyncResponse{ WarpSyncResponse: &nwmodels.WarpSyncResponse{ - CommitmentId: lo.PanicOnErr(id.Bytes()), - BlockIds: lo.PanicOnErr(serializer.Encode(blockIDs)), - MerkleProof: lo.PanicOnErr(merkleProof.Bytes()), + CommitmentId: lo.PanicOnErr(id.Bytes()), + BlockIds: lo.PanicOnErr(serializer.Encode(blockIDs)), + TangleMerkleProof: lo.PanicOnErr(tangleMerkleProof.Bytes()), + TransactionIds: lo.PanicOnErr(serializer.Encode(transactionIDs)), + MutationsMerkleProof: lo.PanicOnErr(mutationsMerkleProof.Bytes()), }, }}, to...) } @@ -44,7 +46,7 @@ func (p *Protocol) handleWarpSyncRequest(commitmentIDBytes []byte, id peer.ID) { }) } -func (p *Protocol) handleWarpSyncResponse(commitmentIDBytes []byte, blockIDsBytes []byte, merkleProofBytes []byte, id peer.ID) { +func (p *Protocol) handleWarpSyncResponse(commitmentIDBytes []byte, blockIDsBytes []byte, tangleMerkleProofBytes []byte, transactionIDsBytes []byte, mutationProofBytes []byte, id peer.ID) { p.workerPool.Submit(func() { commitmentID, _, err := iotago.CommitmentIDFromBytes(commitmentIDBytes) if err != nil { @@ -60,13 +62,27 @@ func (p *Protocol) handleWarpSyncResponse(commitmentIDBytes []byte, blockIDsByte return } - merkleProof, _, err := merklehasher.ProofFromBytes[iotago.Identifier](merkleProofBytes) + tangleMerkleProof, _, err := merklehasher.ProofFromBytes[iotago.Identifier](tangleMerkleProofBytes) + if err != nil { + p.Events.Error.Trigger(ierrors.Wrapf(err, "failed to deserialize merkle proof when receiving waprsync response for commitment %s", commitmentID), id) + + return + } + + var transactionIDs iotago.TransactionIDs + if _, err = p.apiProvider.APIForSlot(commitmentID.Slot()).Decode(transactionIDsBytes, &transactionIDs, serix.WithValidation()); err != nil { + p.Events.Error.Trigger(ierrors.Wrap(err, "failed to deserialize transaction ids"), id) + + return + } + + mutationProof, _, err := merklehasher.ProofFromBytes[iotago.Identifier](mutationProofBytes) if err != nil { p.Events.Error.Trigger(ierrors.Wrapf(err, "failed to deserialize merkle proof when receiving waprsync response for commitment %s", commitmentID), id) return } - p.Events.WarpSyncResponseReceived.Trigger(commitmentID, blockIDs, merkleProof, id) + p.Events.WarpSyncResponseReceived.Trigger(commitmentID, blockIDs, tangleMerkleProof, transactionIDs, mutationProof, id) }) } diff --git a/pkg/protocol/block_dispatcher.go b/pkg/protocol/block_dispatcher.go index 79b4c52cd..b6014be6e 100644 --- a/pkg/protocol/block_dispatcher.go +++ b/pkg/protocol/block_dispatcher.go @@ -1,6 +1,7 @@ package protocol import ( + "sync/atomic" "time" "github.com/libp2p/go-libp2p/core/peer" @@ -12,7 +13,6 @@ import ( "github.com/iotaledger/hive.go/ds/types" "github.com/iotaledger/hive.go/ierrors" "github.com/iotaledger/hive.go/kvstore/mapdb" - "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/runtime/options" "github.com/iotaledger/hive.go/runtime/workerpool" "github.com/iotaledger/iota-core/pkg/core/buffer" @@ -46,9 +46,6 @@ type BlockDispatcher struct { // shutdownEvent is a reactive event that is triggered when the BlockDispatcher instance is stopped. shutdownEvent reactive.Event - - // optWarpSyncWindowSize is the optional warp sync window size. - optWarpSyncWindowSize iotago.SlotIndex } // NewBlockDispatcher creates a new BlockDispatcher instance. @@ -84,6 +81,12 @@ func (b *BlockDispatcher) Dispatch(block *model.Block, src peer.ID) error { if engine != nil && (engine.ChainID() == slotCommitment.Chain().ForkingPoint.ID() || engine.BlockRequester.HasTicker(block.ID())) { if b.inSyncWindow(engine, block) { engine.ProcessBlockFromPeer(block, src) + } else { + // Stick too new blocks into the unsolid commitment buffer so that they can be dispatched once the + // engine instance is in sync (mostly needed for tests). + if !b.unsolidCommitmentBlocks.Add(slotCommitment.ID(), types.NewTuple(block, src)) { + return ierrors.Errorf("failed to add block %s to unsolid commitment buffer", block.ID()) + } } matchingEngineFound = true @@ -151,9 +154,9 @@ func (b *BlockDispatcher) initNetworkConnection() { }, b.warpSyncWorkers) }) - b.protocol.Events.Network.WarpSyncResponseReceived.Hook(func(commitmentID iotago.CommitmentID, blockIDs iotago.BlockIDs, merkleProof *merklehasher.Proof[iotago.Identifier], src peer.ID) { + b.protocol.Events.Network.WarpSyncResponseReceived.Hook(func(commitmentID iotago.CommitmentID, blockIDs iotago.BlockIDs, tangleMerkleProof *merklehasher.Proof[iotago.Identifier], transactionIDs iotago.TransactionIDs, mutationMerkleProof *merklehasher.Proof[iotago.Identifier], src peer.ID) { b.runTask(func() { - b.protocol.HandleError(b.processWarpSyncResponse(commitmentID, blockIDs, merkleProof, src)) + b.protocol.HandleError(b.processWarpSyncResponse(commitmentID, blockIDs, tangleMerkleProof, transactionIDs, mutationMerkleProof, src)) }, b.warpSyncWorkers) }) } @@ -179,22 +182,28 @@ func (b *BlockDispatcher) processWarpSyncRequest(commitmentID iotago.CommitmentI return ierrors.Wrapf(err, "failed to get block IDs from slot %d", commitmentID.Slot()) } + transactionIDs, err := committedSlot.TransactionIDs() + if err != nil { + return ierrors.Wrapf(err, "failed to get transaction IDs from slot %d", commitmentID.Slot()) + } + roots, err := committedSlot.Roots() if err != nil { return ierrors.Wrapf(err, "failed to get roots from slot %d", commitmentID.Slot()) } - b.protocol.networkProtocol.SendWarpSyncResponse(commitmentID, blockIDs, roots.TangleProof(), src) + b.protocol.networkProtocol.SendWarpSyncResponse(commitmentID, blockIDs, roots.TangleProof(), transactionIDs, roots.MutationProof(), src) return nil } // processWarpSyncResponse processes a WarpSync response. -func (b *BlockDispatcher) processWarpSyncResponse(commitmentID iotago.CommitmentID, blockIDs iotago.BlockIDs, merkleProof *merklehasher.Proof[iotago.Identifier], _ peer.ID) error { +func (b *BlockDispatcher) processWarpSyncResponse(commitmentID iotago.CommitmentID, blockIDs iotago.BlockIDs, tangleMerkleProof *merklehasher.Proof[iotago.Identifier], transactionIDs iotago.TransactionIDs, mutationMerkleProof *merklehasher.Proof[iotago.Identifier], _ peer.ID) error { if b.processedWarpSyncRequests.Has(commitmentID) { return nil } + // First we make sure that the commitment, the provided blockIDs with tangle proof and the provided transactionIDs with mutation proof are valid. chainCommitment, exists := b.protocol.ChainManager.Commitment(commitmentID) if !exists { return ierrors.Errorf("failed to get chain commitment for %s", commitmentID) @@ -205,21 +214,131 @@ func (b *BlockDispatcher) processWarpSyncResponse(commitmentID iotago.Commitment return ierrors.Errorf("failed to get target engine for %s", commitmentID) } + // Make sure that already evicted commitments are not processed. This might happen if there's a lot of slots to process + // and old responses are still in the task queue. + if loadedCommitment, err := targetEngine.Storage.Commitments().Load(commitmentID.Slot()); err == nil && loadedCommitment.ID() == commitmentID { + return nil + } + acceptedBlocks := ads.NewSet[iotago.BlockID](mapdb.NewMapDB(), iotago.BlockID.Bytes, iotago.BlockIDFromBytes) for _, blockID := range blockIDs { _ = acceptedBlocks.Add(blockID) // a mapdb can newer return an error } - if !iotago.VerifyProof(merkleProof, iotago.Identifier(acceptedBlocks.Root()), chainCommitment.Commitment().RootsID()) { - return ierrors.Errorf("failed to verify merkle proof for %s", commitmentID) + if !iotago.VerifyProof(tangleMerkleProof, iotago.Identifier(acceptedBlocks.Root()), chainCommitment.Commitment().RootsID()) { + return ierrors.Errorf("failed to verify tangle merkle proof for %s", commitmentID) + } + + acceptedTransactionIDs := ads.NewSet[iotago.TransactionID](mapdb.NewMapDB(), iotago.TransactionID.Bytes, iotago.TransactionIDFromBytes) + for _, transactionID := range transactionIDs { + _ = acceptedTransactionIDs.Add(transactionID) // a mapdb can never return an error + } + + if !iotago.VerifyProof(mutationMerkleProof, iotago.Identifier(acceptedTransactionIDs.Root()), chainCommitment.Commitment().RootsID()) { + return ierrors.Errorf("failed to verify mutation merkle proof for %s", commitmentID) } b.pendingWarpSyncRequests.StopTicker(commitmentID) b.processedWarpSyncRequests.Add(commitmentID) + // If the engine is "dirty" we need to restore the state of the engine to the state of the chain commitment. + // As we already decided to switch and sync to this chain we should make sure that processing the blocks from the commitment + // leads to the verified commitment. + if targetEngine.Notarization.AcceptedBlocksCount(commitmentID.Slot()) > 0 { + b.protocol.activeEngineMutex.Lock() + + newEngine, err := b.protocol.EngineManager.RollbackEngine(commitmentID.Slot() - 1) + if err != nil { + return ierrors.Wrapf(err, "failed to rollback engine for slot %d", commitmentID.Slot()) + } + b.protocol.Events.MainEngineRestarted.Trigger(newEngine) + + newEngine.SetChainID(targetEngine.ChainID()) + + if err := b.protocol.EngineManager.SetActiveInstance(newEngine); err != nil { + return ierrors.Wrap(err, "failed to set active engine instance") + } + + b.protocol.linkToEngine(newEngine) + + b.protocol.mainEngine.Shutdown() + b.protocol.mainEngine = newEngine + targetEngine = newEngine + b.protocol.activeEngineMutex.Unlock() + } + + // Once all blocks are booked we + // 1. Mark all transactions as accepted + // 2. Mark all blocks as accepted + // 3. Force commitment of the slot + totalBlocks := uint32(len(blockIDs)) + var bookedBlocks atomic.Uint32 + var notarizedBlocks atomic.Uint32 + + forceCommitmentFunc := func() { + // 3. Force commitment of the slot + producedCommitment, err := targetEngine.Notarization.ForceCommit(commitmentID.Slot()) + if err != nil { + b.protocol.HandleError(err) + return + } + + // 4. Verify that the produced commitment is the same as the initially requested one + if producedCommitment.ID() != commitmentID { + b.protocol.HandleError(ierrors.Errorf("producedCommitment ID mismatch: %s != %s", producedCommitment.ID(), commitmentID)) + return + } + } + + blockBookedFunc := func(_, _ bool) { + if bookedBlocks.Add(1) != totalBlocks { + return + } + + // 1. Mark all transactions as accepted + for _, transactionID := range transactionIDs { + targetEngine.Ledger.ConflictDAG().SetAccepted(transactionID) + } + + // 2. Mark all blocks as accepted + for _, blockID := range blockIDs { + block, exists := targetEngine.BlockCache.Block(blockID) + if !exists { // this should never happen as we just booked these blocks in this slot. + continue + } + + targetEngine.BlockGadget.SetAccepted(block) + + block.Notarized().OnUpdate(func(_, _ bool) { + // Wait for all blocks to be notarized before forcing the commitment of the slot. + if notarizedBlocks.Add(1) != totalBlocks { + return + } + + forceCommitmentFunc() + }) + } + } + + if len(blockIDs) == 0 { + forceCommitmentFunc() + return nil + } + for _, blockID := range blockIDs { - targetEngine.BlockDAG.GetOrRequestBlock(blockID) + block, _ := targetEngine.BlockDAG.GetOrRequestBlock(blockID) + if block == nil { // this should never happen as we're requesting the blocks for this slot so it can't be evicted. + b.protocol.HandleError(ierrors.Errorf("failed to get block %s", blockID)) + continue + } + + // We need to make sure that we add all blocks as root blocks because we don't know which blocks are root blocks without + // blocks from future slots. We're committing the current slot which then leads to the eviction of the blocks from the + // block cache and thus if not root blocks no block in the next slot can become solid. + targetEngine.EvictionState.AddRootBlock(block.ID(), block.SlotCommitmentID()) + + block.Booked().OnUpdate(blockBookedFunc) } return nil @@ -243,21 +362,21 @@ func (b *BlockDispatcher) inSyncWindow(engine *engine.Engine, block *model.Block // warpSyncIfNecessary triggers a warp sync if necessary. func (b *BlockDispatcher) warpSyncIfNecessary(e *engine.Engine, chainCommitment *chainmanager.ChainCommitment) { - if e == nil { + if e == nil || e.WasShutdown() { return } chain := chainCommitment.Chain() latestCommitmentSlot := e.Storage.Settings().LatestCommitment().Slot() + // We don't want to warpsync if the latest commitment of the engine is very close to the latest commitment of the + // chain as the node might just be about to commit it itself. This is important for tests, as we always need to issue + // 2 slots ahead of the latest commitment of the chain to make sure that the other nodes can warp sync. if latestCommitmentSlot+1 >= chain.LatestCommitment().Commitment().Slot() { return } - maxCommittableAge := e.APIForSlot(chainCommitment.Commitment().Slot()).ProtocolParameters().MaxCommittableAge() - warpSyncWindowSize := lo.Max(maxCommittableAge, b.optWarpSyncWindowSize) - - for slotToWarpSync := latestCommitmentSlot + 1; slotToWarpSync <= latestCommitmentSlot+warpSyncWindowSize; slotToWarpSync++ { + for slotToWarpSync := latestCommitmentSlot + 1; slotToWarpSync <= latestCommitmentSlot+1; slotToWarpSync++ { commitmentToSync := chain.Commitment(slotToWarpSync) if commitmentToSync == nil { break @@ -340,12 +459,5 @@ func (b *BlockDispatcher) runTask(task func(), pool *workerpool.WorkerPool) { }) } -// WithWarpSyncWindowSize is an option for the BlockDispatcher that allows to set the warp sync window size. -func WithWarpSyncWindowSize(windowSize iotago.SlotIndex) options.Option[BlockDispatcher] { - return func(b *BlockDispatcher) { - b.optWarpSyncWindowSize = windowSize - } -} - // WarpSyncRetryInterval is the interval in which a warp sync request is retried. const WarpSyncRetryInterval = 1 * time.Minute diff --git a/pkg/protocol/engine/blockdag/blockdag.go b/pkg/protocol/engine/blockdag/blockdag.go index 9db7cd1aa..77b4164ee 100644 --- a/pkg/protocol/engine/blockdag/blockdag.go +++ b/pkg/protocol/engine/blockdag/blockdag.go @@ -16,8 +16,5 @@ type BlockDAG interface { // without requesting it. GetOrRequestBlock(blockID iotago.BlockID) (block *blocks.Block, requested bool) - // SetInvalid marks a Block as invalid. - SetInvalid(block *blocks.Block, reason error) (wasUpdated bool) - module.Interface } diff --git a/pkg/protocol/engine/blockdag/inmemoryblockdag/blockdag.go b/pkg/protocol/engine/blockdag/inmemoryblockdag/blockdag.go index b1ed127b6..aa60beafa 100644 --- a/pkg/protocol/engine/blockdag/inmemoryblockdag/blockdag.go +++ b/pkg/protocol/engine/blockdag/inmemoryblockdag/blockdag.go @@ -1,12 +1,12 @@ package inmemoryblockdag import ( - "github.com/iotaledger/hive.go/core/causalorder" + "sync/atomic" + "github.com/iotaledger/hive.go/ierrors" "github.com/iotaledger/hive.go/runtime/event" "github.com/iotaledger/hive.go/runtime/module" "github.com/iotaledger/hive.go/runtime/options" - "github.com/iotaledger/hive.go/runtime/syncutils" "github.com/iotaledger/hive.go/runtime/workerpool" "github.com/iotaledger/iota-core/pkg/core/buffer" "github.com/iotaledger/iota-core/pkg/model" @@ -26,9 +26,6 @@ type BlockDAG struct { // evictionState contains information about the current eviction state. evictionState *eviction.State - // solidifier contains the solidifier instance used to determine the solidity of Blocks. - solidifier *causalorder.CausalOrder[iotago.SlotIndex, iotago.BlockID, *blocks.Block] - latestCommitmentFunc func() *model.Commitment uncommittedSlotBlocks *buffer.UnsolidCommitmentBuffer[*blocks.Block] @@ -36,11 +33,7 @@ type BlockDAG struct { blockCache *blocks.Blocks - solidifierMutex syncutils.RWMutex - - workers *workerpool.Group - workerPool *workerpool.WorkerPool - + workers *workerpool.Group errorHandler func(error) apiProvider iotago.APIProvider @@ -61,13 +54,8 @@ func NewProvider(opts ...options.Option[BlockDAG]) module.Provider[*engine.Engin }, event.WithWorkerPool(wp)) e.Events.Notarization.LatestCommitmentUpdated.Hook(func(commitment *model.Commitment) { - unsolidBlocks := b.uncommittedSlotBlocks.GetValuesAndEvict(commitment.ID()) - - b.solidifierMutex.RLock() - defer b.solidifierMutex.RUnlock() - - for _, block := range unsolidBlocks { - b.solidifier.Queue(block) + for _, block := range b.uncommittedSlotBlocks.GetValuesAndEvict(commitment.ID()) { + b.setupBlock(block) } }, event.WithWorkerPool(wp)) @@ -83,6 +71,32 @@ func NewProvider(opts ...options.Option[BlockDAG]) module.Provider[*engine.Engin }) } +func (b *BlockDAG) setupBlock(block *blocks.Block) { + var unsolidParentsCount atomic.Int32 + unsolidParentsCount.Store(int32(len(block.Parents()))) + + block.ForEachParent(func(parent iotago.Parent) { + parentBlock, exists := b.blockCache.Block(parent.ID) + if !exists { + panic("cannot setup block without existing parent") + } + + parentBlock.Solid().OnUpdateOnce(func(_, _ bool) { + if unsolidParentsCount.Add(-1) == 0 { + if block.SetSolid() { + b.events.BlockSolid.Trigger(block) + } + } + }) + + parentBlock.Invalid().OnUpdateOnce(func(_, _ bool) { + if block.SetInvalid() { + b.events.BlockInvalid.Trigger(block, ierrors.Errorf("parent block %s is marked as invalid", parent.ID)) + } + }) + }) +} + // New is the constructor for the BlockDAG and creates a new BlockDAG instance. func New(workers *workerpool.Group, apiProvider iotago.APIProvider, evictionState *eviction.State, blockCache *blocks.Blocks, errorHandler func(error), opts ...options.Option[BlockDAG]) (newBlockDAG *BlockDAG) { return options.Apply(&BlockDAG{ @@ -91,26 +105,9 @@ func New(workers *workerpool.Group, apiProvider iotago.APIProvider, evictionStat evictionState: evictionState, blockCache: blockCache, workers: workers, - workerPool: workers.CreatePool("Solidifier", workerpool.WithWorkerCount(2)), errorHandler: errorHandler, uncommittedSlotBlocks: buffer.NewUnsolidCommitmentBuffer[*blocks.Block](int(apiProvider.CurrentAPI().ProtocolParameters().MaxCommittableAge()) * 2), - }, opts, - func(b *BlockDAG) { - b.solidifier = causalorder.New( - b.workerPool, - blockCache.Block, - (*blocks.Block).IsSolid, - b.markSolid, - b.markInvalid, - (*blocks.Block).Parents, - causalorder.WithReferenceValidator[iotago.SlotIndex, iotago.BlockID](checkReference), - ) - - blockCache.Evict.Hook(b.evictSlot) - }, - (*BlockDAG).TriggerConstructed, - (*BlockDAG).TriggerInitialized, - ) + }, opts, (*BlockDAG).TriggerConstructed, (*BlockDAG).TriggerInitialized) } var _ blockdag.BlockDAG = new(BlockDAG) @@ -134,10 +131,7 @@ func (b *BlockDAG) Attach(data *model.Block) (block *blocks.Block, wasAttached b return } - b.solidifierMutex.RLock() - defer b.solidifierMutex.RUnlock() - - b.solidifier.Queue(block) + b.setupBlock(block) } return @@ -155,15 +149,6 @@ func (b *BlockDAG) GetOrRequestBlock(blockID iotago.BlockID) (block *blocks.Bloc }) } -// SetInvalid marks a Block as invalid. -func (b *BlockDAG) SetInvalid(block *blocks.Block, reason error) (wasUpdated bool) { - if wasUpdated = block.SetInvalid(); wasUpdated { - b.events.BlockInvalid.Trigger(block, reason) - } - - return -} - func (b *BlockDAG) Shutdown() { b.TriggerStopped() b.workers.Shutdown() @@ -173,26 +158,6 @@ func (b *BlockDAG) setRetainBlockFailureFunc(retainBlockFailure func(blockID iot b.retainBlockFailure = retainBlockFailure } -// evictSlot is used to evict Blocks from committed slots from the BlockDAG. -func (b *BlockDAG) evictSlot(slot iotago.SlotIndex) { - b.solidifierMutex.Lock() - defer b.solidifierMutex.Unlock() - - b.solidifier.EvictUntil(slot) -} - -func (b *BlockDAG) markSolid(block *blocks.Block) (err error) { - if block.SetSolid() { - b.events.BlockSolid.Trigger(block) - } - - return nil -} - -func (b *BlockDAG) markInvalid(block *blocks.Block, reason error) { - b.SetInvalid(block, ierrors.Wrap(reason, "block marked as invalid in BlockDAG")) -} - // attach tries to attach the given Block to the BlockDAG. func (b *BlockDAG) attach(data *model.Block) (block *blocks.Block, wasAttached bool, err error) { shouldAttach, err := b.shouldAttach(data) @@ -265,12 +230,3 @@ func (b *BlockDAG) registerChild(child *blocks.Block, parent iotago.Parent) { parentBlock.AppendChild(child, parent.Type) } } - -// checkReference checks if the reference between the child and its parent is valid. -func checkReference(child *blocks.Block, parent *blocks.Block) (err error) { - if parent.IsInvalid() { - return ierrors.Errorf("parent %s of child %s is marked as invalid", parent.ID(), child.ID()) - } - - return nil -} diff --git a/pkg/protocol/engine/blocks/block.go b/pkg/protocol/engine/blocks/block.go index a87d53467..33de43b2e 100644 --- a/pkg/protocol/engine/blocks/block.go +++ b/pkg/protocol/engine/blocks/block.go @@ -19,14 +19,14 @@ type Block struct { // BlockDAG block missing bool missingBlockID iotago.BlockID - solid bool - invalid bool + solid reactive.Variable[bool] + invalid reactive.Variable[bool] strongChildren []*Block weakChildren []*Block shallowLikeChildren []*Block // Booker block - booked bool + booked reactive.Variable[bool] witnesses ds.Set[account.SeatIndex] // conflictIDs are the all conflictIDs of the block inherited from the parents + payloadConflictIDs. conflictIDs ds.Set[iotago.TransactionID] @@ -47,6 +47,9 @@ type Block struct { enqueued bool dropped bool + // Notarization + notarized reactive.Variable[bool] + mutex syncutils.RWMutex modelBlock *model.Block @@ -72,7 +75,6 @@ func (r *rootBlock) String() string { // NewBlock creates a new Block with the given options. func NewBlock(data *model.Block) *Block { - return &Block{ witnesses: ds.NewSet[account.SeatIndex](), conflictIDs: ds.NewSet[iotago.TransactionID](), @@ -80,7 +82,11 @@ func NewBlock(data *model.Block) *Block { acceptanceRatifiers: ds.NewSet[account.SeatIndex](), confirmationRatifiers: ds.NewSet[account.SeatIndex](), modelBlock: data, + solid: reactive.NewVariable[bool](), + invalid: reactive.NewVariable[bool](), + booked: reactive.NewVariable[bool](), accepted: reactive.NewVariable[bool](), + notarized: reactive.NewVariable[bool](), workScore: data.WorkScore(), } } @@ -98,14 +104,19 @@ func NewRootBlock(blockID iotago.BlockID, commitmentID iotago.CommitmentID, issu commitmentID: commitmentID, issuingTime: issuingTime, }, - solid: true, - booked: true, + solid: reactive.NewVariable[bool](), + invalid: reactive.NewVariable[bool](), + booked: reactive.NewVariable[bool](), preAccepted: true, accepted: reactive.NewVariable[bool](), + notarized: reactive.NewVariable[bool](), scheduled: true, } // This should be true since we commit and evict on acceptance. + b.solid.Set(true) + b.booked.Set(true) + b.notarized.Set(true) b.accepted.Set(true) return b @@ -120,7 +131,11 @@ func NewMissingBlock(blockID iotago.BlockID) *Block { payloadConflictIDs: ds.NewSet[iotago.TransactionID](), acceptanceRatifiers: ds.NewSet[account.SeatIndex](), confirmationRatifiers: ds.NewSet[account.SeatIndex](), + solid: reactive.NewVariable[bool](), + invalid: reactive.NewVariable[bool](), + booked: reactive.NewVariable[bool](), accepted: reactive.NewVariable[bool](), + notarized: reactive.NewVariable[bool](), } } @@ -243,20 +258,34 @@ func (b *Block) IsMissing() (isMissing bool) { return b.missing } +// Solid returns a reactive variable that is true if the Block is solid (the entire causal history is known). +func (b *Block) Solid() (solid reactive.Variable[bool]) { + return b.solid +} + // IsSolid returns true if the Block is solid (the entire causal history is known). func (b *Block) IsSolid() (isSolid bool) { - b.mutex.RLock() - defer b.mutex.RUnlock() + return b.solid.Get() +} - return b.solid +// SetSolid marks the Block as solid. +func (b *Block) SetSolid() (wasUpdated bool) { + return !b.solid.Set(true) +} + +// Invalid returns a reactive variable that is true if the Block was marked as invalid. +func (b *Block) Invalid() (invalid reactive.Variable[bool]) { + return b.invalid } // IsInvalid returns true if the Block was marked as invalid. func (b *Block) IsInvalid() (isInvalid bool) { - b.mutex.RLock() - defer b.mutex.RUnlock() + return b.invalid.Get() +} - return b.invalid +// SetInvalid marks the Block as invalid. +func (b *Block) SetInvalid() (wasUpdated bool) { + return !b.invalid.Set(true) } // Children returns the children of the Block. @@ -302,32 +331,6 @@ func (b *Block) ShallowLikeChildren() []*Block { return lo.CopySlice(b.shallowLikeChildren) } -// SetSolid marks the Block as solid. -func (b *Block) SetSolid() (wasUpdated bool) { - b.mutex.Lock() - defer b.mutex.Unlock() - - if wasUpdated = !b.solid; wasUpdated { - b.solid = true - } - - return -} - -// SetInvalid marks the Block as invalid. -func (b *Block) SetInvalid() (wasUpdated bool) { - b.mutex.Lock() - defer b.mutex.Unlock() - - if b.invalid { - return false - } - - b.invalid = true - - return true -} - func (b *Block) AppendChild(child *Block, childType iotago.ParentsType) { b.mutex.Lock() defer b.mutex.Unlock() @@ -358,22 +361,17 @@ func (b *Block) Update(data *model.Block) (wasPublished bool) { return true } -func (b *Block) IsBooked() (isBooked bool) { - b.mutex.RLock() - defer b.mutex.RUnlock() - +// Booked returns a reactive variable that is true if the Block was booked. +func (b *Block) Booked() reactive.Variable[bool] { return b.booked } -func (b *Block) SetBooked() (wasUpdated bool) { - b.mutex.Lock() - defer b.mutex.Unlock() - - if wasUpdated = !b.booked; wasUpdated { - b.booked = true - } +func (b *Block) IsBooked() (isBooked bool) { + return b.booked.Get() +} - return +func (b *Block) SetBooked() (wasUpdated bool) { + return !b.booked.Set(true) } func (b *Block) AddWitness(seat account.SeatIndex) (added bool) { @@ -459,6 +457,11 @@ func (b *Block) AcceptanceRatifiers() []account.SeatIndex { return b.acceptanceRatifiers.ToSlice() } +// Accepted returns a reactive variable that is true if the Block was accepted. +func (b *Block) Accepted() reactive.Variable[bool] { + return b.accepted +} + // IsAccepted returns true if the Block was accepted. func (b *Block) IsAccepted() bool { return b.accepted.Get() @@ -469,11 +472,6 @@ func (b *Block) SetAccepted() (wasUpdated bool) { return !b.accepted.Set(true) } -// Accepted returns a reactive variable that is true if the Block was accepted. -func (b *Block) Accepted() reactive.Variable[bool] { - return b.accepted -} - // IsScheduled returns true if the Block was scheduled. func (b *Block) IsScheduled() bool { b.mutex.RLock() @@ -607,15 +605,27 @@ func (b *Block) SetPreConfirmed() (wasUpdated bool) { return wasUpdated } +func (b *Block) Notarized() reactive.Variable[bool] { + return b.notarized +} + +func (b *Block) IsNotarized() (isBooked bool) { + return b.notarized.Get() +} + +func (b *Block) SetNotarized() (wasUpdated bool) { + return !b.notarized.Set(true) +} + func (b *Block) String() string { b.mutex.RLock() defer b.mutex.RUnlock() builder := stringify.NewStructBuilder("Engine.Block", stringify.NewStructField("id", b.id())) builder.AddField(stringify.NewStructField("Missing", b.missing)) - builder.AddField(stringify.NewStructField("Solid", b.solid)) - builder.AddField(stringify.NewStructField("Invalid", b.invalid)) - builder.AddField(stringify.NewStructField("Booked", b.booked)) + builder.AddField(stringify.NewStructField("Solid", b.solid.Get())) + builder.AddField(stringify.NewStructField("Invalid", b.invalid.Get())) + builder.AddField(stringify.NewStructField("Booked", b.booked.Get())) builder.AddField(stringify.NewStructField("Witnesses", b.witnesses)) builder.AddField(stringify.NewStructField("PreAccepted", b.preAccepted)) builder.AddField(stringify.NewStructField("AcceptanceRatifiers", b.acceptanceRatifiers.String())) @@ -627,6 +637,7 @@ func (b *Block) String() string { builder.AddField(stringify.NewStructField("Dropped", b.dropped)) builder.AddField(stringify.NewStructField("Skipped", b.skipped)) builder.AddField(stringify.NewStructField("Enqueued", b.enqueued)) + builder.AddField(stringify.NewStructField("Notarized", b.notarized.Get())) for index, child := range b.strongChildren { builder.AddField(stringify.NewStructField(fmt.Sprintf("strongChildren%d", index), child.ID().String())) diff --git a/pkg/protocol/engine/booker/inmemorybooker/booker.go b/pkg/protocol/engine/booker/inmemorybooker/booker.go index 2af3058e2..3d3be8df8 100644 --- a/pkg/protocol/engine/booker/inmemorybooker/booker.go +++ b/pkg/protocol/engine/booker/inmemorybooker/booker.go @@ -1,13 +1,13 @@ package inmemorybooker import ( - "github.com/iotaledger/hive.go/core/causalorder" + "sync/atomic" + "github.com/iotaledger/hive.go/ds" "github.com/iotaledger/hive.go/ierrors" "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/runtime/module" "github.com/iotaledger/hive.go/runtime/options" - "github.com/iotaledger/hive.go/runtime/workerpool" "github.com/iotaledger/iota-core/pkg/protocol/engine" "github.com/iotaledger/iota-core/pkg/protocol/engine/blocks" "github.com/iotaledger/iota-core/pkg/protocol/engine/booker" @@ -21,10 +21,6 @@ import ( type Booker struct { events *booker.Events - bookingOrder *causalorder.CausalOrder[iotago.SlotIndex, iotago.BlockID, *blocks.Block] - - workers *workerpool.Group - blockCache *blocks.Blocks conflictDAG conflictdag.ConflictDAG[iotago.TransactionID, mempool.StateID, ledger.BlockVoteRank] @@ -41,7 +37,7 @@ type Booker struct { func NewProvider(opts ...options.Option[Booker]) module.Provider[*engine.Engine, booker.Booker] { return module.Provide(func(e *engine.Engine) booker.Booker { - b := New(e.Workers.CreateGroup("Booker"), e, e.BlockCache, e.ErrorHandler("booker"), opts...) + b := New(e, e.BlockCache, e.ErrorHandler("booker"), opts...) e.HookConstructed(func() { b.ledger = e.Ledger b.ledger.HookConstructed(func() { @@ -71,37 +67,24 @@ func NewProvider(opts ...options.Option[Booker]) module.Provider[*engine.Engine, }) } -func New(workers *workerpool.Group, apiProvider iotago.APIProvider, blockCache *blocks.Blocks, errorHandler func(error), opts ...options.Option[Booker]) *Booker { +func New(apiProvider iotago.APIProvider, blockCache *blocks.Blocks, errorHandler func(error), opts ...options.Option[Booker]) *Booker { return options.Apply(&Booker{ events: booker.NewEvents(), apiProvider: apiProvider, blockCache: blockCache, - workers: workers, errorHandler: errorHandler, - }, opts, func(b *Booker) { - b.bookingOrder = causalorder.New( - workers.CreatePool("BookingOrder", workerpool.WithWorkerCount(2)), - blockCache.Block, - (*blocks.Block).IsBooked, - b.book, - b.markInvalid, - (*blocks.Block).Parents, - causalorder.WithReferenceValidator[iotago.SlotIndex, iotago.BlockID](b.isReferenceValid), - ) - - blockCache.Evict.Hook(b.evict) - }, (*Booker).TriggerConstructed) + }, opts, (*Booker).TriggerConstructed) } var _ booker.Booker = new(Booker) -// Queue checks if payload is solid and then adds the block to a Booker's CausalOrder. +// Queue checks if payload is solid and then sets up the block to react to its parents. func (b *Booker) Queue(block *blocks.Block) error { signedTransactionMetadata, containsTransaction := b.ledger.AttachTransaction(block) if !containsTransaction { - b.bookingOrder.Queue(block) + b.setupBlock(block) return nil } @@ -123,7 +106,7 @@ func (b *Booker) Queue(block *blocks.Block) error { transactionMetadata.OnBooked(func() { block.SetPayloadConflictIDs(transactionMetadata.ConflictIDs()) - b.bookingOrder.Queue(block) + b.setupBlock(block) }) }) @@ -132,15 +115,40 @@ func (b *Booker) Queue(block *blocks.Block) error { func (b *Booker) Shutdown() { b.TriggerStopped() - b.workers.Shutdown() } -func (b *Booker) setRetainBlockFailureFunc(retainBlockFailure func(iotago.BlockID, apimodels.BlockFailureReason)) { - b.retainBlockFailure = retainBlockFailure +func (b *Booker) setupBlock(block *blocks.Block) { + var unbookedParentsCount atomic.Int32 + unbookedParentsCount.Store(int32(len(block.Parents()))) + + block.ForEachParent(func(parent iotago.Parent) { + parentBlock, exists := b.blockCache.Block(parent.ID) + if !exists { + b.errorHandler(ierrors.Errorf("cannot setup block %s without existing parent %s", block.ID(), parent.ID)) + + return + } + + parentBlock.Booked().OnUpdateOnce(func(_, _ bool) { + if unbookedParentsCount.Add(-1) == 0 { + if err := b.book(block); err != nil { + if block.SetInvalid() { + b.events.BlockInvalid.Trigger(block, ierrors.Wrap(err, "failed to book block")) + } + } + } + }) + + parentBlock.Invalid().OnUpdateOnce(func(_, _ bool) { + if block.SetInvalid() { + b.events.BlockInvalid.Trigger(block, ierrors.New("block marked as invalid in Booker")) + } + }) + }) } -func (b *Booker) evict(slot iotago.SlotIndex) { - b.bookingOrder.EvictUntil(slot) +func (b *Booker) setRetainBlockFailureFunc(retainBlockFailure func(iotago.BlockID, apimodels.BlockFailureReason)) { + b.retainBlockFailure = retainBlockFailure } func (b *Booker) book(block *blocks.Block) error { @@ -149,7 +157,7 @@ func (b *Booker) book(block *blocks.Block) error { return ierrors.Wrapf(err, "failed to inherit conflicts for block %s", block.ID()) } - // The block is invalid if it carries a conflict that has been orphaned with respect to its commitment. + // The block does not inherit conflicts that have been orphaned with respect to its commitment. for it := conflictsToInherit.Iterator(); it.HasNext(); { conflictID := it.Next() @@ -171,12 +179,6 @@ func (b *Booker) book(block *blocks.Block) error { return nil } -func (b *Booker) markInvalid(block *blocks.Block, err error) { - if block.SetInvalid() { - b.events.BlockInvalid.Trigger(block, ierrors.Wrap(err, "block marked as invalid in Booker")) - } -} - func (b *Booker) inheritConflicts(block *blocks.Block) (conflictIDs ds.Set[iotago.TransactionID], err error) { conflictIDsToInherit := ds.NewSet[iotago.TransactionID]() @@ -217,13 +219,3 @@ func (b *Booker) inheritConflicts(block *blocks.Block) (conflictIDs ds.Set[iotag // Only inherit conflicts that are not yet accepted (aka merge to master). return b.conflictDAG.UnacceptedConflicts(conflictIDsToInherit), nil } - -// isReferenceValid checks if the reference between the child and its parent is valid. -func (b *Booker) isReferenceValid(child *blocks.Block, parent *blocks.Block) (err error) { - if parent.IsInvalid() { - b.retainBlockFailure(child.ID(), apimodels.BlockFailureParentInvalid) - return ierrors.Errorf("parent %s of child %s is marked as invalid", parent.ID(), child.ID()) - } - - return nil -} diff --git a/pkg/protocol/engine/committed_slot_api.go b/pkg/protocol/engine/committed_slot_api.go index 35ee99199..62d02a279 100644 --- a/pkg/protocol/engine/committed_slot_api.go +++ b/pkg/protocol/engine/committed_slot_api.go @@ -1,6 +1,7 @@ package engine import ( + "github.com/iotaledger/hive.go/ads" "github.com/iotaledger/hive.go/ierrors" "github.com/iotaledger/iota-core/pkg/model" iotago "github.com/iotaledger/iota.go/v4" @@ -71,3 +72,26 @@ func (c *CommittedSlotAPI) BlockIDs() (blockIDs iotago.BlockIDs, err error) { return blockIDs, nil } + +func (c *CommittedSlotAPI) TransactionIDs() (iotago.TransactionIDs, error) { + if c.engine.Storage.Settings().LatestCommitment().Slot() < c.CommitmentID.Slot() { + return nil, ierrors.Errorf("slot %d is not committed yet", c.CommitmentID) + } + + store, err := c.engine.Storage.Mutations(c.CommitmentID.Slot()) + if err != nil { + return nil, ierrors.Errorf("failed to get mutations of slot index %d", c.CommitmentID.Slot()) + } + + set := ads.NewSet(store, iotago.TransactionID.Bytes, iotago.TransactionIDFromBytes) + transactionIDs := make(iotago.TransactionIDs, 0, set.Size()) + + if err = set.Stream(func(txID iotago.TransactionID) error { + transactionIDs = append(transactionIDs, txID) + return nil + }); err != nil { + return nil, ierrors.Wrapf(err, "failed to iterate over mutations of slot %d", c.CommitmentID.Slot()) + } + + return transactionIDs, nil +} diff --git a/pkg/protocol/engine/consensus/blockgadget/gadget.go b/pkg/protocol/engine/consensus/blockgadget/gadget.go index 2fe562c37..26d836832 100644 --- a/pkg/protocol/engine/consensus/blockgadget/gadget.go +++ b/pkg/protocol/engine/consensus/blockgadget/gadget.go @@ -9,4 +9,5 @@ type Gadget interface { module.Interface TrackWitnessWeight(votingBlock *blocks.Block) + SetAccepted(block *blocks.Block) bool } diff --git a/pkg/protocol/engine/consensus/blockgadget/thresholdblockgadget/acceptance_ratification.go b/pkg/protocol/engine/consensus/blockgadget/thresholdblockgadget/acceptance_ratification.go index 13466584e..e7bad633e 100644 --- a/pkg/protocol/engine/consensus/blockgadget/thresholdblockgadget/acceptance_ratification.go +++ b/pkg/protocol/engine/consensus/blockgadget/thresholdblockgadget/acceptance_ratification.go @@ -58,3 +58,12 @@ func (g *Gadget) shouldAccept(block *blocks.Block) bool { return votes.IsThresholdReached(blockSeats, onlineCommitteeTotalSeats, g.optsAcceptanceThreshold) } + +func (g *Gadget) SetAccepted(block *blocks.Block) bool { + if block.SetAccepted() { + g.events.BlockAccepted.Trigger(block) + return true + } + + return false +} diff --git a/pkg/protocol/engine/engine.go b/pkg/protocol/engine/engine.go index 4489f09e9..f91f8bf94 100644 --- a/pkg/protocol/engine/engine.go +++ b/pkg/protocol/engine/engine.go @@ -71,9 +71,8 @@ type Engine struct { BlockCache *blocks.Blocks - startupAvailableBlocksWindow iotago.SlotIndex - chainID iotago.CommitmentID - mutex syncutils.RWMutex + chainID iotago.CommitmentID + mutex syncutils.RWMutex optsSnapshotPath string optsEntryPointsDepth int @@ -207,10 +206,6 @@ func New( if err := e.UpgradeOrchestrator.RestoreFromDisk(e.Storage.Settings().LatestCommitment().Slot()); err != nil { panic(ierrors.Wrap(err, "failed to restore upgrade orchestrator from disk")) } - - // When we start from disk we potentially have previously accepted blocks in window (latestCommitment, latestCommitment + maxCommittableAge] - // on disk. We store this information that we can load blocks instead of requesting them again. - e.startupAvailableBlocksWindow = e.Storage.Settings().LatestCommitment().Slot() + e.CurrentAPI().ProtocolParameters().MaxCommittableAge() } }, func(e *Engine) { @@ -261,11 +256,6 @@ func (e *Engine) Block(id iotago.BlockID) (*model.Block, bool) { return cachedBlock.ModelBlock(), !cachedBlock.IsMissing() } - // The block should've been in the block cache, so there's no need to check the storage. - if !exists && id.Slot() > e.startupAvailableBlocksWindow && id.Slot() > e.Storage.Settings().LatestCommitment().Slot() { - return nil, false - } - s, err := e.Storage.Blocks(id.Slot()) if err != nil { e.errorHandler(ierrors.Wrap(err, "failed to get block storage")) @@ -465,25 +455,9 @@ func (e *Engine) setupBlockRequester() { e.Events.EvictionState.SlotEvicted.Hook(e.BlockRequester.EvictUntil) - wp := e.Workers.CreatePool("BlockMissingAttachFromStorage", workerpool.WithWorkerCount(1)) // We need to hook to make sure that the request is created before the block arrives to avoid a race condition // where we try to delete the request again before it is created. Thus, continuing to request forever. e.Events.BlockDAG.BlockMissing.Hook(func(block *blocks.Block) { - if block.ID().Slot() < e.startupAvailableBlocksWindow { - // We shortcut requesting blocks that are in the storage in case we did shut down and restart. - // We can safely ignore all errors. - if blockStorage, err := e.Storage.Blocks(block.ID().Slot()); err == nil { - if storedBlock, _ := blockStorage.Load(block.ID()); storedBlock != nil { - // We need to attach the block to the DAG in a separate worker pool to avoid a deadlock with the block cache - // as the BlockMissing event is triggered within a GetOrCreate call. - wp.Submit(func() { - _, _, _ = e.BlockDAG.Attach(storedBlock) - }) - - return - } - } - } e.BlockRequester.StartTicker(block.ID()) }) e.Events.BlockDAG.MissingBlockAttached.Hook(func(block *blocks.Block) { @@ -518,7 +492,7 @@ func (e *Engine) EarliestRootCommitment(lastFinalizedSlot iotago.SlotIndex) (ear rootCommitment, err := e.Storage.Commitments().Load(earliestRootCommitmentSlot) if err != nil { - panic(fmt.Sprintln("could not load earliest commitment after engine initialization", err)) + panic(fmt.Sprintf("could not load earliest commitment %d after engine initialization: %s", earliestRootCommitmentSlot, err)) } return rootCommitment diff --git a/pkg/protocol/engine/ledger/ledger/ledger.go b/pkg/protocol/engine/ledger/ledger/ledger.go index 333e3f99d..11f876470 100644 --- a/pkg/protocol/engine/ledger/ledger/ledger.go +++ b/pkg/protocol/engine/ledger/ledger/ledger.go @@ -68,7 +68,7 @@ func NewProvider() module.Provider[*engine.Engine, ledger.Ledger] { l.setRetainTransactionFailureFunc(e.Retainer.RetainTransactionFailure) - l.memPool = mempoolv1.New(NewVM(l), l.resolveState, e.Workers.CreateGroup("MemPool"), l.conflictDAG, l.apiProvider, l.errorHandler, mempoolv1.WithForkAllTransactions[ledger.BlockVoteRank](true)) + l.memPool = mempoolv1.New(NewVM(l), l.resolveState, e.Storage.Mutations, e.Workers.CreateGroup("MemPool"), l.conflictDAG, l.apiProvider, l.errorHandler, mempoolv1.WithForkAllTransactions[ledger.BlockVoteRank](true)) e.EvictionState.Events.SlotEvicted.Hook(l.memPool.Evict) l.manaManager = mana.NewManager(l.apiProvider, l.resolveAccountOutput, l.accountsLedger.Account) @@ -148,7 +148,10 @@ func (l *Ledger) CommitSlot(slot iotago.SlotIndex) (stateRoot iotago.Identifier, panic(ierrors.Errorf("there is a gap in the ledgerstate %d vs %d", ledgerIndex+1, slot)) } - stateDiff := l.memPool.StateDiff(slot) + stateDiff, err := l.memPool.StateDiff(slot) + if err != nil { + return iotago.Identifier{}, iotago.Identifier{}, iotago.Identifier{}, ierrors.Errorf("failed to retrieve state diff for slot %d: %w", slot, err) + } // collect outputs and allotments from the "uncompacted" stateDiff // outputs need to be processed in the "uncompacted" version of the state diff, as we need to be able to store diff --git a/pkg/protocol/engine/mempool/conflictdag/conflictdag.go b/pkg/protocol/engine/mempool/conflictdag/conflictdag.go index 2b60acc01..2513f6392 100644 --- a/pkg/protocol/engine/mempool/conflictdag/conflictdag.go +++ b/pkg/protocol/engine/mempool/conflictdag/conflictdag.go @@ -20,6 +20,7 @@ type ConflictDAG[ConflictID, ResourceID IDType, VoteRank VoteRankType[VoteRank]] ConflictingConflicts(conflictID ConflictID) (conflictingConflicts ds.Set[ConflictID], exists bool) CastVotes(vote *vote.Vote[VoteRank], conflictIDs ds.Set[ConflictID]) error AcceptanceState(conflictIDs ds.Set[ConflictID]) acceptance.State + SetAccepted(conflictID ConflictID) UnacceptedConflicts(conflictIDs ds.Set[ConflictID]) ds.Set[ConflictID] AllConflictsSupported(seat account.SeatIndex, conflictIDs ds.Set[ConflictID]) bool EvictConflict(conflictID ConflictID) diff --git a/pkg/protocol/engine/mempool/conflictdag/conflictdagv1/conflictdag.go b/pkg/protocol/engine/mempool/conflictdag/conflictdagv1/conflictdag.go index da09d98db..88306cc12 100644 --- a/pkg/protocol/engine/mempool/conflictdag/conflictdagv1/conflictdag.go +++ b/pkg/protocol/engine/mempool/conflictdag/conflictdagv1/conflictdag.go @@ -363,6 +363,15 @@ func (c *ConflictDAG[ConflictID, ResourceID, VoteRank]) AcceptanceState(conflict return lowestObservedState } +func (c *ConflictDAG[ConflictID, ResourceID, VoteRank]) SetAccepted(conflictID ConflictID) { + c.mutex.RLock() + defer c.mutex.RUnlock() + + if conflict, exists := c.conflictsByID.Get(conflictID); exists { + conflict.setAcceptanceState(acceptance.Accepted) + } +} + // UnacceptedConflicts takes a set of ConflictIDs and removes all the accepted Conflicts (leaving only the // pending or rejected ones behind). func (c *ConflictDAG[ConflictID, ResourceID, VoteRank]) UnacceptedConflicts(conflictIDs ds.Set[ConflictID]) ds.Set[ConflictID] { diff --git a/pkg/protocol/engine/mempool/mempool.go b/pkg/protocol/engine/mempool/mempool.go index 0b4535afc..bb0ba1019 100644 --- a/pkg/protocol/engine/mempool/mempool.go +++ b/pkg/protocol/engine/mempool/mempool.go @@ -25,7 +25,7 @@ type MemPool[VoteRank conflictdag.VoteRankType[VoteRank]] interface { TransactionMetadataByAttachment(blockID iotago.BlockID) (transaction TransactionMetadata, exists bool) - StateDiff(slot iotago.SlotIndex) StateDiff + StateDiff(slot iotago.SlotIndex) (StateDiff, error) Evict(slot iotago.SlotIndex) } diff --git a/pkg/protocol/engine/mempool/tests/testframework.go b/pkg/protocol/engine/mempool/tests/testframework.go index b929cf4c1..55edeacf8 100644 --- a/pkg/protocol/engine/mempool/tests/testframework.go +++ b/pkg/protocol/engine/mempool/tests/testframework.go @@ -143,7 +143,10 @@ func (t *TestFramework) AttachTransaction(signedTransactionAlias, transactionAli } func (t *TestFramework) CommitSlot(slot iotago.SlotIndex) { - stateDiff := t.Instance.StateDiff(slot) + stateDiff, err := t.Instance.StateDiff(slot) + if err != nil { + panic(err) + } stateDiff.CreatedStates().ForEach(func(_ mempool.StateID, state mempool.StateMetadata) bool { t.ledgerState.AddOutputState(state.State()) @@ -370,7 +373,8 @@ func (t *TestFramework) requireMarkedBooked(transactionAliases ...string) { } func (t *TestFramework) AssertStateDiff(slot iotago.SlotIndex, spentOutputAliases, createdOutputAliases, transactionAliases []string) { - stateDiff := t.Instance.StateDiff(slot) + stateDiff, err := t.Instance.StateDiff(slot) + require.NoError(t.test, err) require.Equal(t.test, len(spentOutputAliases), stateDiff.DestroyedStates().Size()) require.Equal(t.test, len(createdOutputAliases), stateDiff.CreatedStates().Size()) diff --git a/pkg/protocol/engine/mempool/v1/mempool.go b/pkg/protocol/engine/mempool/v1/mempool.go index ba3efc2f8..aa82b1185 100644 --- a/pkg/protocol/engine/mempool/v1/mempool.go +++ b/pkg/protocol/engine/mempool/v1/mempool.go @@ -7,6 +7,7 @@ import ( "github.com/iotaledger/hive.go/ds" "github.com/iotaledger/hive.go/ds/shrinkingmap" "github.com/iotaledger/hive.go/ierrors" + "github.com/iotaledger/hive.go/kvstore" "github.com/iotaledger/hive.go/lo" "github.com/iotaledger/hive.go/runtime/event" "github.com/iotaledger/hive.go/runtime/options" @@ -27,6 +28,8 @@ type MemPool[VoteRank conflictdag.VoteRankType[VoteRank]] struct { // resolveState is the function that is used to request state from the ledger. resolveState mempool.StateResolver + mutationsFunc func(iotago.SlotIndex) (kvstore.KVStore, error) + // attachments is the storage that is used to keep track of the attachments of transactions. attachments *memstorage.IndexedStorage[iotago.SlotIndex, iotago.BlockID, *SignedTransactionMetadata] @@ -77,6 +80,7 @@ type MemPool[VoteRank conflictdag.VoteRankType[VoteRank]] struct { func New[VoteRank conflictdag.VoteRankType[VoteRank]]( vm mempool.VM, stateResolver mempool.StateResolver, + mutationsFunc func(iotago.SlotIndex) (kvstore.KVStore, error), workers *workerpool.Group, conflictDAG conflictdag.ConflictDAG[iotago.TransactionID, mempool.StateID, VoteRank], apiProvider iotago.APIProvider, @@ -86,6 +90,7 @@ func New[VoteRank conflictdag.VoteRankType[VoteRank]]( return options.Apply(&MemPool[VoteRank]{ vm: vm, resolveState: stateResolver, + mutationsFunc: mutationsFunc, attachments: memstorage.NewIndexedStorage[iotago.SlotIndex, iotago.BlockID, *SignedTransactionMetadata](), cachedTransactions: shrinkingmap.New[iotago.TransactionID, *TransactionMetadata](), cachedSignedTransactions: shrinkingmap.New[iotago.SignedTransactionID, *SignedTransactionMetadata](), @@ -172,12 +177,24 @@ func (m *MemPool[VoteRank]) TransactionMetadataByAttachment(blockID iotago.Block } // StateDiff returns the state diff for the given slot. -func (m *MemPool[VoteRank]) StateDiff(slot iotago.SlotIndex) mempool.StateDiff { - if stateDiff, exists := m.stateDiffs.Get(slot); exists { - return stateDiff +func (m *MemPool[VoteRank]) StateDiff(slot iotago.SlotIndex) (mempool.StateDiff, error) { + m.evictionMutex.RLock() + defer m.evictionMutex.RUnlock() + + return m.stateDiff(slot) +} + +func (m *MemPool[VoteRank]) stateDiff(slot iotago.SlotIndex) (*StateDiff, error) { + if m.lastEvictedSlot >= slot { + return nil, ierrors.Errorf("slot %d is older than last evicted slot %d", slot, m.lastEvictedSlot) + } + + kv, err := m.mutationsFunc(slot) + if err != nil { + return nil, ierrors.Wrapf(err, "failed to get state diff for slot %d", slot) } - return NewStateDiff(slot) + return lo.Return1(m.stateDiffs.GetOrCreate(slot, func() *StateDiff { return NewStateDiff(slot, kv) })), nil } // Evict evicts the slot with the given slot from the MemPool. @@ -407,10 +424,13 @@ func (m *MemPool[VoteRank]) updateStateDiffs(transaction *TransactionMetadata, p } if transaction.IsAccepted() && newIndex != 0 { - if stateDiff, evicted := m.stateDiff(newIndex); !evicted { - if err := stateDiff.AddTransaction(transaction, m.errorHandler); err != nil { - return ierrors.Wrapf(err, "failed to add transaction to state diff, txID: %s", transaction.ID()) - } + stateDiff, err := m.stateDiff(newIndex) + if err != nil { + return ierrors.Wrapf(err, "failed to get state diff for slot %d", newIndex) + } + + if err = stateDiff.AddTransaction(transaction, m.errorHandler); err != nil { + return ierrors.Wrapf(err, "failed to add transaction to state diff, txID: %s", transaction.ID()) } } @@ -425,21 +445,17 @@ func (m *MemPool[VoteRank]) setup() { }) } -func (m *MemPool[VoteRank]) stateDiff(slot iotago.SlotIndex) (stateDiff *StateDiff, evicted bool) { - if m.lastEvictedSlot >= slot { - return nil, true - } - - return lo.Return1(m.stateDiffs.GetOrCreate(slot, func() *StateDiff { return NewStateDiff(slot) })), false -} - func (m *MemPool[VoteRank]) setupTransaction(transaction *TransactionMetadata) { transaction.OnAccepted(func() { + // Transactions can only become accepted if there is at least one attachment included. if slot := transaction.EarliestIncludedAttachment().Slot(); slot != 0 { - if stateDiff, evicted := m.stateDiff(slot); !evicted { - if err := stateDiff.AddTransaction(transaction, m.errorHandler); err != nil { - m.errorHandler(ierrors.Wrapf(err, "failed to add transaction to state diff, txID: %s", transaction.ID())) - } + stateDiff, err := m.stateDiff(slot) + if err != nil { + m.errorHandler(ierrors.Wrapf(err, "failed to get state diff for slot %d", slot)) + } + + if err := stateDiff.AddTransaction(transaction, m.errorHandler); err != nil { + m.errorHandler(ierrors.Wrapf(err, "failed to add transaction to state diff, txID: %s", transaction.ID())) } } }) diff --git a/pkg/protocol/engine/mempool/v1/mempool_test.go b/pkg/protocol/engine/mempool/v1/mempool_test.go index 25aa9746c..df8f3a793 100644 --- a/pkg/protocol/engine/mempool/v1/mempool_test.go +++ b/pkg/protocol/engine/mempool/v1/mempool_test.go @@ -9,6 +9,8 @@ import ( "github.com/stretchr/testify/require" "github.com/iotaledger/hive.go/ds/shrinkingmap" + "github.com/iotaledger/hive.go/kvstore" + "github.com/iotaledger/hive.go/kvstore/mapdb" "github.com/iotaledger/hive.go/runtime/memanalyzer" "github.com/iotaledger/hive.go/runtime/workerpool" "github.com/iotaledger/iota-core/pkg/core/account" @@ -34,11 +36,15 @@ func TestMemPoolV1_InterfaceWithForkingEverything(t *testing.T) { func TestMempoolV1_ResourceCleanup(t *testing.T) { workers := workerpool.NewGroup(t.Name()) + mutationsFunc := func(index iotago.SlotIndex) (kvstore.KVStore, error) { + return mapdb.NewMapDB(), nil + } + ledgerState := ledgertests.New(ledgertests.NewMockedState(iotago.TransactionID{}, 0)) conflictDAG := conflictdagv1.New[iotago.TransactionID, mempool.StateID, vote.MockedRank](func() int { return 0 }) memPoolInstance := New[vote.MockedRank](new(mempooltests.VM), func(reference mempool.StateReference) *promise.Promise[mempool.State] { return ledgerState.ResolveOutputState(reference) - }, workers, conflictDAG, api.SingleVersionProvider(tpkg.TestAPI), func(error) {}) + }, mutationsFunc, workers, conflictDAG, api.SingleVersionProvider(tpkg.TestAPI), func(error) {}) tf := mempooltests.NewTestFramework(t, memPoolInstance, conflictDAG, ledgerState, workers) @@ -141,9 +147,13 @@ func newTestFramework(t *testing.T) *mempooltests.TestFramework { ledgerState := ledgertests.New(ledgertests.NewMockedState(iotago.TransactionID{}, 0)) conflictDAG := conflictdagv1.New[iotago.TransactionID, mempool.StateID, vote.MockedRank](account.NewAccounts().SelectCommittee().SeatCount) + mutationsFunc := func(index iotago.SlotIndex) (kvstore.KVStore, error) { + return mapdb.NewMapDB(), nil + } + return mempooltests.NewTestFramework(t, New[vote.MockedRank](new(mempooltests.VM), func(reference mempool.StateReference) *promise.Promise[mempool.State] { return ledgerState.ResolveOutputState(reference) - }, workers, conflictDAG, api.SingleVersionProvider(tpkg.TestAPI), func(error) {}), conflictDAG, ledgerState, workers) + }, mutationsFunc, workers, conflictDAG, api.SingleVersionProvider(tpkg.TestAPI), func(error) {}), conflictDAG, ledgerState, workers) } func newForkingTestFramework(t *testing.T) *mempooltests.TestFramework { @@ -152,7 +162,11 @@ func newForkingTestFramework(t *testing.T) *mempooltests.TestFramework { ledgerState := ledgertests.New(ledgertests.NewMockedState(iotago.TransactionID{}, 0)) conflictDAG := conflictdagv1.New[iotago.TransactionID, mempool.StateID, vote.MockedRank](account.NewAccounts().SelectCommittee().SeatCount) + mutationsFunc := func(index iotago.SlotIndex) (kvstore.KVStore, error) { + return mapdb.NewMapDB(), nil + } + return mempooltests.NewTestFramework(t, New[vote.MockedRank](new(mempooltests.VM), func(reference mempool.StateReference) *promise.Promise[mempool.State] { return ledgerState.ResolveOutputState(reference) - }, workers, conflictDAG, api.SingleVersionProvider(tpkg.TestAPI), func(error) {}, WithForkAllTransactions[vote.MockedRank](true)), conflictDAG, ledgerState, workers) + }, mutationsFunc, workers, conflictDAG, api.SingleVersionProvider(tpkg.TestAPI), func(error) {}, WithForkAllTransactions[vote.MockedRank](true)), conflictDAG, ledgerState, workers) } diff --git a/pkg/protocol/engine/mempool/v1/state_diff.go b/pkg/protocol/engine/mempool/v1/state_diff.go index 8a561cf47..f1c665dd7 100644 --- a/pkg/protocol/engine/mempool/v1/state_diff.go +++ b/pkg/protocol/engine/mempool/v1/state_diff.go @@ -5,7 +5,7 @@ import ( "github.com/iotaledger/hive.go/ds/orderedmap" "github.com/iotaledger/hive.go/ds/shrinkingmap" "github.com/iotaledger/hive.go/ierrors" - "github.com/iotaledger/hive.go/kvstore/mapdb" + "github.com/iotaledger/hive.go/kvstore" "github.com/iotaledger/iota-core/pkg/protocol/engine/mempool" iotago "github.com/iotaledger/iota.go/v4" ) @@ -24,14 +24,14 @@ type StateDiff struct { mutations ads.Set[iotago.TransactionID] } -func NewStateDiff(slot iotago.SlotIndex) *StateDiff { +func NewStateDiff(slot iotago.SlotIndex, kv kvstore.KVStore) *StateDiff { return &StateDiff{ slot: slot, spentOutputs: shrinkingmap.New[mempool.StateID, mempool.StateMetadata](), createdOutputs: shrinkingmap.New[mempool.StateID, mempool.StateMetadata](), executedTransactions: orderedmap.New[iotago.TransactionID, mempool.TransactionMetadata](), stateUsageCounters: shrinkingmap.New[mempool.StateID, int](), - mutations: ads.NewSet(mapdb.NewMapDB(), iotago.TransactionID.Bytes, iotago.TransactionIDFromBytes), + mutations: ads.NewSet(kv, iotago.TransactionID.Bytes, iotago.TransactionIDFromBytes), } } diff --git a/pkg/protocol/engine/notarization/notarization.go b/pkg/protocol/engine/notarization/notarization.go index 2a8d8c409..e84b75088 100644 --- a/pkg/protocol/engine/notarization/notarization.go +++ b/pkg/protocol/engine/notarization/notarization.go @@ -2,11 +2,18 @@ package notarization import ( "github.com/iotaledger/hive.go/runtime/module" + "github.com/iotaledger/iota-core/pkg/model" + iotago "github.com/iotaledger/iota.go/v4" ) type Notarization interface { // IsBootstrapped returns if notarization finished committing all pending slots up to the current acceptance time. IsBootstrapped() bool + ForceCommit(slot iotago.SlotIndex) (*model.Commitment, error) + ForceCommitUntil(commitUntilSlot iotago.SlotIndex) error + + AcceptedBlocksCount(index iotago.SlotIndex) int + module.Interface } diff --git a/pkg/protocol/engine/notarization/slotnotarization/manager.go b/pkg/protocol/engine/notarization/slotnotarization/manager.go index 34ec3af74..daea67ee2 100644 --- a/pkg/protocol/engine/notarization/slotnotarization/manager.go +++ b/pkg/protocol/engine/notarization/slotnotarization/manager.go @@ -6,6 +6,7 @@ import ( "github.com/iotaledger/hive.go/ierrors" "github.com/iotaledger/hive.go/runtime/event" "github.com/iotaledger/hive.go/runtime/module" + "github.com/iotaledger/hive.go/runtime/syncutils" "github.com/iotaledger/hive.go/runtime/workerpool" "github.com/iotaledger/hive.go/serializer/v2/serix" "github.com/iotaledger/iota-core/pkg/model" @@ -25,8 +26,9 @@ type Manager struct { events *notarization.Events slotMutations *SlotMutations - workers *workerpool.Group - errorHandler func(error) + workers *workerpool.Group + errorHandler func(error) + acceptedBlockProcessedDetach func() attestation attestation.Attestations ledger ledger.Ledger @@ -39,6 +41,8 @@ type Manager struct { minCommittableAge iotago.SlotIndex apiProvider iotago.APIProvider + commitmentMutex syncutils.Mutex + module.Module } @@ -59,12 +63,14 @@ func NewProvider() module.Provider[*engine.Engine, notarization.Notarization] { wpBlocks := m.workers.CreatePool("Blocks", workerpool.WithWorkerCount(1)) // Using just 1 worker to avoid contention - e.Events.AcceptedBlockProcessed.Hook(func(block *blocks.Block) { + m.acceptedBlockProcessedDetach = e.Events.AcceptedBlockProcessed.Hook(func(block *blocks.Block) { if err := m.notarizeAcceptedBlock(block); err != nil { m.errorHandler(ierrors.Wrapf(err, "failed to add accepted block %s to slot", block.ID())) } - m.tryCommitUntil(block) - }, event.WithWorkerPool(wpBlocks)) + m.tryCommitUntil(block.ID().Slot()) + + block.SetNotarized() + }, event.WithWorkerPool(wpBlocks)).Unhook e.Events.Notarization.LinkTo(m.events) @@ -88,14 +94,41 @@ func NewManager(minCommittableAge iotago.SlotIndex, workers *workerpool.Group, e func (m *Manager) Shutdown() { m.TriggerStopped() + // Alternative 2 + if m.acceptedBlockProcessedDetach != nil { + m.acceptedBlockProcessedDetach() + } m.workers.Shutdown() } // tryCommitUntil tries to create slot commitments until the new provided acceptance time. -func (m *Manager) tryCommitUntil(block *blocks.Block) { - if index := block.ID().Slot(); index > m.storage.Settings().LatestCommitment().Slot() { - m.tryCommitSlotUntil(index) +func (m *Manager) tryCommitUntil(commitUntilSlot iotago.SlotIndex) { + if slot := commitUntilSlot; slot > m.storage.Settings().LatestCommitment().Slot() { + m.tryCommitSlotUntil(slot) + } +} + +func (m *Manager) ForceCommit(slot iotago.SlotIndex) (*model.Commitment, error) { + if m.WasStopped() { + return nil, ierrors.New("notarization manager was stopped") + } + + commitment, err := m.createCommitment(slot) + if err != nil { + return nil, ierrors.Wrapf(err, "failed to create commitment for slot %d", slot) } + + return commitment, nil +} + +func (m *Manager) ForceCommitUntil(commitUntilSlot iotago.SlotIndex) error { + for i := m.storage.Settings().LatestCommitment().Slot() + 1; i <= commitUntilSlot; i++ { + if _, err := m.ForceCommit(i); err != nil { + return ierrors.Wrapf(err, "failed to force commit slot %d", i) + } + } + + return nil } // IsBootstrapped returns if the Manager finished committing all pending slots up to the current acceptance time. @@ -127,7 +160,8 @@ func (m *Manager) tryCommitSlotUntil(acceptedBlockIndex iotago.SlotIndex) { return } - if !m.createCommitment(i) { + if _, err := m.createCommitment(i); err != nil { + m.errorHandler(ierrors.Wrapf(err, "failed to create commitment for slot %d", i)) return } } @@ -137,43 +171,40 @@ func (m *Manager) isCommittable(index, acceptedBlockIndex iotago.SlotIndex) bool return index+m.minCommittableAge <= acceptedBlockIndex } -func (m *Manager) createCommitment(index iotago.SlotIndex) (success bool) { +func (m *Manager) createCommitment(slot iotago.SlotIndex) (*model.Commitment, error) { + m.commitmentMutex.Lock() + defer m.commitmentMutex.Unlock() + latestCommitment := m.storage.Settings().LatestCommitment() - if index != latestCommitment.Slot()+1 { - m.errorHandler(ierrors.Errorf("cannot create commitment for slot %d, latest commitment is for slot %d", index, latestCommitment.Slot())) - return false + if slot != latestCommitment.Slot()+1 { + return nil, ierrors.Errorf("cannot create commitment for slot %d, latest commitment is for slot %d", slot, latestCommitment.Slot()) } // Set createIfMissing to true to make sure that this is never nil. Will get evicted later on anyway. - acceptedBlocks := m.slotMutations.AcceptedBlocks(index, true) + acceptedBlocks := m.slotMutations.AcceptedBlocks(slot, true) if err := acceptedBlocks.Commit(); err != nil { - m.errorHandler(ierrors.Wrap(err, "failed to commit accepted blocks")) - return false + return nil, ierrors.Wrap(err, "failed to commit accepted blocks") } - cumulativeWeight, attestationsRoot, err := m.attestation.Commit(index) + cumulativeWeight, attestationsRoot, err := m.attestation.Commit(slot) if err != nil { - m.errorHandler(ierrors.Wrap(err, "failed to commit attestations")) - return false + return nil, ierrors.Wrap(err, "failed to commit attestations") } - stateRoot, mutationRoot, accountRoot, err := m.ledger.CommitSlot(index) + stateRoot, mutationRoot, accountRoot, err := m.ledger.CommitSlot(slot) if err != nil { - m.errorHandler(ierrors.Wrap(err, "failed to commit ledger")) - return false + return nil, ierrors.Wrap(err, "failed to commit ledger") } - committeeRoot, rewardsRoot, err := m.sybilProtection.CommitSlot(index) + committeeRoot, rewardsRoot, err := m.sybilProtection.CommitSlot(slot) if err != nil { - m.errorHandler(ierrors.Wrap(err, "failed to commit sybil protection")) - return false + return nil, ierrors.Wrap(err, "failed to commit sybil protection") } - apiForSlot := m.apiProvider.APIForSlot(index) + apiForSlot := m.apiProvider.APIForSlot(slot) - protocolParametersAndVersionsHash, err := m.upgradeOrchestrator.Commit(index) + protocolParametersAndVersionsHash, err := m.upgradeOrchestrator.Commit(slot) if err != nil { - m.errorHandler(ierrors.Wrapf(err, "failed to commit protocol parameters and versions in upgrade orchestrator for slot %d", index)) - return false + return nil, ierrors.Wrapf(err, "failed to commit protocol parameters and versions in upgrade orchestrator for slot %d", slot) } roots := iotago.NewRoots( @@ -188,15 +219,14 @@ func (m *Manager) createCommitment(index iotago.SlotIndex) (success bool) { ) // calculate the new RMC - rmc, err := m.ledger.RMCManager().CommitSlot(index) + rmc, err := m.ledger.RMCManager().CommitSlot(slot) if err != nil { - m.errorHandler(ierrors.Wrapf(err, "failed to commit RMC for slot %d", index)) - return false + return nil, ierrors.Wrapf(err, "failed to commit RMC for slot %d", slot) } newCommitment := iotago.NewCommitment( apiForSlot.ProtocolParameters().Version(), - index, + slot, latestCommitment.ID(), roots.ID(), cumulativeWeight, @@ -205,22 +235,19 @@ func (m *Manager) createCommitment(index iotago.SlotIndex) (success bool) { newModelCommitment, err := model.CommitmentFromCommitment(newCommitment, apiForSlot, serix.WithValidation()) if err != nil { - return false + return nil, ierrors.Wrapf(err, "failed to create model commitment for commitment %s", newCommitment.MustID()) } - rootsStorage, err := m.storage.Roots(index) + rootsStorage, err := m.storage.Roots(slot) if err != nil { - m.errorHandler(ierrors.Wrapf(err, "failed get roots storage for commitment %s", newModelCommitment.ID())) - return false + return nil, ierrors.Wrapf(err, "failed get roots storage for commitment %s", newModelCommitment.ID()) } if err = rootsStorage.Store(newModelCommitment.ID(), roots); err != nil { - m.errorHandler(ierrors.Wrapf(err, "failed to store latest roots for commitment %s", newModelCommitment.ID())) - return false + return nil, ierrors.Wrapf(err, "failed to store latest roots for commitment %s", newModelCommitment.ID()) } if err = m.storage.Commitments().Store(newModelCommitment); err != nil { - m.errorHandler(ierrors.Wrapf(err, "failed to store latest commitment %s", newModelCommitment.ID())) - return false + return nil, ierrors.Wrapf(err, "failed to store latest commitment %s", newModelCommitment.ID()) } m.events.SlotCommitted.Trigger(¬arization.SlotCommittedDetails{ @@ -230,17 +257,20 @@ func (m *Manager) createCommitment(index iotago.SlotIndex) (success bool) { }) if err = m.storage.Settings().SetLatestCommitment(newModelCommitment); err != nil { - m.errorHandler(ierrors.Wrap(err, "failed to set latest commitment")) - return false + return nil, ierrors.Wrap(err, "failed to set latest commitment") } m.events.LatestCommitmentUpdated.Trigger(newModelCommitment) - if err = m.slotMutations.Evict(index); err != nil { - m.errorHandler(ierrors.Wrapf(err, "failed to evict slotMutations at index: %d", index)) + if err = m.slotMutations.Evict(slot); err != nil { + m.errorHandler(ierrors.Wrapf(err, "failed to evict slotMutations at slot: %d", slot)) } - return true + return newModelCommitment, nil +} + +func (m *Manager) AcceptedBlocksCount(index iotago.SlotIndex) int { + return m.slotMutations.AcceptedBlocksCount(index) } var _ notarization.Notarization = new(Manager) diff --git a/pkg/protocol/engine/notarization/slotnotarization/slotmutations.go b/pkg/protocol/engine/notarization/slotnotarization/slotmutations.go index cc667add0..1afd405af 100644 --- a/pkg/protocol/engine/notarization/slotnotarization/slotmutations.go +++ b/pkg/protocol/engine/notarization/slotnotarization/slotmutations.go @@ -83,6 +83,15 @@ func (m *SlotMutations) AcceptedBlocks(index iotago.SlotIndex, createIfMissing . return lo.Return1(m.acceptedBlocksBySlot.Get(index)) } +func (m *SlotMutations) AcceptedBlocksCount(index iotago.SlotIndex) int { + acceptedBlocks, exists := m.acceptedBlocksBySlot.Get(index) + if !exists { + return 0 + } + + return acceptedBlocks.Size() +} + // evictUntil removes all data for slots that are older than the given slot. func (m *SlotMutations) evictUntil(index iotago.SlotIndex) { for i := m.latestCommittedIndex + 1; i <= index; i++ { diff --git a/pkg/protocol/enginemanager/enginemanager.go b/pkg/protocol/enginemanager/enginemanager.go index 01e2efa45..a36b01175 100644 --- a/pkg/protocol/enginemanager/enginemanager.go +++ b/pkg/protocol/enginemanager/enginemanager.go @@ -139,6 +139,18 @@ func (e *EngineManager) LoadActiveEngine(snapshotPath string) (*engine.Engine, e if exists, isDirectory, err := ioutils.PathExists(e.directory.Path(info.Name)); err == nil && exists && isDirectory { // Load previous engine as active e.activeInstance = e.loadEngineInstanceFromSnapshot(info.Name, snapshotPath) + + // Clear the storage of the active instance to be consistent with the latest committed slot. + // Everything after the latest committed slot is pruned to ensure a consistent state (e.g. accepted blocks). + targetSlot := e.activeInstance.Storage.Settings().LatestCommitment().Slot() + if err := e.rollbackStorage(e.activeInstance.Storage, targetSlot); err != nil { + return nil, ierrors.Wrapf(err, "failed to rollback storage to slot %d", targetSlot) + } + + // Rollback attestations already on created engine instance, because this action modifies the in-memory storage. + if err := e.activeInstance.Attestations.Rollback(targetSlot); err != nil { + return nil, ierrors.Wrap(err, "error while rolling back attestations storage on candidate engine") + } } } @@ -247,10 +259,25 @@ func (e *EngineManager) ForkEngineAtSlot(index iotago.SlotIndex) (*engine.Engine return nil, ierrors.Wrapf(err, "failed to copy storage from active engine instance (%s) to new engine instance (%s)", e.activeInstance.Storage.Directory(), e.directory.Path(engineAlias)) } + if err := e.rollbackStorage(newStorage, index); err != nil { + return nil, ierrors.Wrapf(err, "failed to rollback storage to slot %d", index) + } + + candidateEngine := e.loadEngineInstanceWithStorage(engineAlias, newStorage) + + // Rollback attestations already on created engine instance, because this action modifies the in-memory storage. + if err := candidateEngine.Attestations.Rollback(index); err != nil { + return nil, ierrors.Wrap(err, "error while rolling back attestations storage on candidate engine") + } + + return candidateEngine, nil +} + +func (e *EngineManager) rollbackStorage(newStorage *storage.Storage, slot iotago.SlotIndex) error { // Remove commitments that after forking point. latestCommitment := newStorage.Settings().LatestCommitment() - if err := newStorage.Commitments().Rollback(index, latestCommitment.Slot()); err != nil { - return nil, ierrors.Wrap(err, "failed to rollback commitments") + if err := newStorage.Commitments().Rollback(slot, latestCommitment.Slot()); err != nil { + return ierrors.Wrap(err, "failed to rollback commitments") } // Create temporary components and rollback their permanent state, which will be reflected on disk. evictionState := eviction.NewState(newStorage.LatestNonEmptySlot(), newStorage.RootBlocks) @@ -260,38 +287,57 @@ func (e *EngineManager) ForkEngineAtSlot(index iotago.SlotIndex) (*engine.Engine accountsManager := accountsledger.New(newStorage.Settings().APIProvider(), blockCache.Block, newStorage.AccountDiffs, newStorage.Accounts()) accountsManager.SetLatestCommittedSlot(latestCommitment.Slot()) - if err := accountsManager.Rollback(index); err != nil { - return nil, ierrors.Wrap(err, "failed to rollback accounts manager") + if err := accountsManager.Rollback(slot); err != nil { + return ierrors.Wrap(err, "failed to rollback accounts manager") } - if err := evictionState.Rollback(newStorage.Settings().LatestFinalizedSlot(), index); err != nil { - return nil, ierrors.Wrap(err, "failed to rollback eviction state") + if err := evictionState.Rollback(newStorage.Settings().LatestFinalizedSlot(), slot); err != nil { + return ierrors.Wrap(err, "failed to rollback eviction state") } - if err := newStorage.Ledger().Rollback(index); err != nil { - return nil, err + if err := newStorage.Ledger().Rollback(slot); err != nil { + return ierrors.Wrapf(err, "failed to rollback ledger to slot %d", slot) } - targetCommitment, err := newStorage.Commitments().Load(index) + targetCommitment, err := newStorage.Commitments().Load(slot) if err != nil { - return nil, ierrors.Wrapf(err, "error while retrieving commitment for target index %d", index) + return ierrors.Wrapf(err, "error while retrieving commitment for target slot %d", slot) } if err := newStorage.Settings().Rollback(targetCommitment); err != nil { - return nil, err + return ierrors.Wrap(err, "failed to rollback settings") } - if err := newStorage.RollbackPrunable(index); err != nil { - return nil, err + if err := newStorage.RollbackPrunable(slot); err != nil { + return ierrors.Wrap(err, "failed to rollback prunable data") } - candidateEngine := e.loadEngineInstanceWithStorage(engineAlias, newStorage) + return nil +} + +func (e *EngineManager) RollbackEngine(slot iotago.SlotIndex) (*engine.Engine, error) { + engineAlias := e.activeInstance.Name() + errorHandler := func(err error) { + e.errorHandler(ierrors.Wrapf(err, "engine (%s)", engineAlias[0:8])) + } + + dir := e.activeInstance.Storage.Directory() + e.activeInstance.Shutdown() + + newStorage := storage.Create(dir, e.dbVersion, errorHandler, e.storageOptions...) + + if err := e.rollbackStorage(newStorage, slot); err != nil { + return nil, ierrors.Wrapf(err, "failed to rollback storage to slot %d", slot) + } + + newEngine := e.loadEngineInstanceWithStorage(engineAlias, newStorage) // Rollback attestations already on created engine instance, because this action modifies the in-memory storage. - if err := candidateEngine.Attestations.Rollback(index); err != nil { + if err := newEngine.Attestations.Rollback(slot); err != nil { return nil, ierrors.Wrap(err, "error while rolling back attestations storage on candidate engine") } - return candidateEngine, nil + return newEngine, nil + } func (e *EngineManager) OnEngineCreated(handler func(*engine.Engine)) (unsubscribe func()) { diff --git a/pkg/protocol/events.go b/pkg/protocol/events.go index eb50194c1..a39963221 100644 --- a/pkg/protocol/events.go +++ b/pkg/protocol/events.go @@ -10,6 +10,7 @@ import ( type Events struct { CandidateEngineActivated *event.Event1[*engine.Engine] MainEngineSwitched *event.Event1[*engine.Engine] + MainEngineRestarted *event.Event1[*engine.Engine] Error *event.Event1[error] Network *core.Events @@ -23,6 +24,7 @@ var NewEvents = event.CreateGroupConstructor(func() (newEvents *Events) { return &Events{ CandidateEngineActivated: event.New1[*engine.Engine](), MainEngineSwitched: event.New1[*engine.Engine](), + MainEngineRestarted: event.New1[*engine.Engine](), Error: event.New1[error](), Network: core.NewEvents(), diff --git a/pkg/protocol/sybilprotection/sybilprotectionv1/sybilprotection.go b/pkg/protocol/sybilprotection/sybilprotectionv1/sybilprotection.go index 3c9c1af8b..69f22f942 100644 --- a/pkg/protocol/sybilprotection/sybilprotectionv1/sybilprotection.go +++ b/pkg/protocol/sybilprotection/sybilprotectionv1/sybilprotection.go @@ -136,7 +136,6 @@ func (o *SybilProtection) CommitSlot(slot iotago.SlotIndex) (committeeRoot, rewa } committee.SetReused() - fmt.Println("reuse committee", currentEpoch, "stake", committee.TotalValidatorStake()) o.seatManager.SetCommittee(nextEpoch, committee) o.events.CommitteeSelected.Trigger(committee, nextEpoch) diff --git a/pkg/storage/permanent/settings.go b/pkg/storage/permanent/settings.go index ee6c44d16..3298beffe 100644 --- a/pkg/storage/permanent/settings.go +++ b/pkg/storage/permanent/settings.go @@ -25,6 +25,7 @@ const ( protocolVersionEpochMappingKey futureProtocolParametersKey protocolParametersKey + latestIssuedValidationBlock ) type Settings struct { @@ -33,6 +34,7 @@ type Settings struct { storeSnapshotImported *kvstore.TypedValue[bool] storeLatestCommitment *kvstore.TypedValue[*model.Commitment] storeLatestFinalizedSlot *kvstore.TypedValue[iotago.SlotIndex] + storeLatestIssuedValidationBlock *kvstore.TypedValue[*model.Block] storeProtocolVersionEpochMapping *kvstore.TypedStore[iotago.Version, iotago.EpochIndex] storeFutureProtocolParameters *kvstore.TypedStore[iotago.Version, *types.Tuple[iotago.EpochIndex, iotago.Identifier]] storeProtocolParameters *kvstore.TypedStore[iotago.Version, iotago.ProtocolParameters] @@ -81,6 +83,12 @@ func NewSettings(store kvstore.KVStore, opts ...options.Option[api.EpochBasedPro iotago.SlotIndex.Bytes, iotago.SlotIndexFromBytes, ), + storeLatestIssuedValidationBlock: kvstore.NewTypedValue( + store, + []byte{latestIssuedValidationBlock}, + (*model.Block).Bytes, + model.BlockFromBytesFunc(apiProvider), + ), storeProtocolVersionEpochMapping: kvstore.NewTypedStore( lo.PanicOnErr(store.WithExtendedRealm([]byte{protocolVersionEpochMappingKey})), @@ -299,6 +307,32 @@ func (s *Settings) latestFinalizedSlot() iotago.SlotIndex { return latestFinalizedSlot } +func (s *Settings) LatestIssuedValidationBlock() *model.Block { + s.mutex.RLock() + defer s.mutex.RUnlock() + + return s.latestIssuedValidationBlock() +} + +func (s *Settings) SetLatestIssuedValidationBlock(block *model.Block) (err error) { + s.mutex.Lock() + defer s.mutex.Unlock() + + return s.storeLatestIssuedValidationBlock.Set(block) +} + +func (s *Settings) latestIssuedValidationBlock() *model.Block { + block, err := s.storeLatestIssuedValidationBlock.Get() + if err != nil { + if ierrors.Is(err, kvstore.ErrKeyNotFound) { + return nil + } + panic(err) + } + + return block +} + func (s *Settings) Export(writer io.WriteSeeker, targetCommitment *iotago.Commitment) error { var commitmentBytes []byte var err error diff --git a/pkg/storage/prunable/prunable.go b/pkg/storage/prunable/prunable.go index 8ce6e166b..1fc392877 100644 --- a/pkg/storage/prunable/prunable.go +++ b/pkg/storage/prunable/prunable.go @@ -1,8 +1,6 @@ package prunable import ( - "fmt" - copydir "github.com/otiai10/copy" "github.com/iotaledger/hive.go/ierrors" @@ -168,7 +166,6 @@ func (p *Prunable) Rollback(targetSlot iotago.SlotIndex) error { return ierrors.Wrapf(err, "error while checking if committee for epoch %d should be rolled back", epoch) } - fmt.Println("rollback committee", shouldRollback, "epoch", epoch, "lastCommittedEpoch", lastCommittedEpoch, "targetSlotEpoch", targetSlotEpoch) if shouldRollback { if err := p.committee.DeleteEpoch(epoch); err != nil { return ierrors.Wrapf(err, "error while deleting committee for epoch %d", epoch) @@ -209,6 +206,10 @@ func (p *Prunable) shouldRollbackCommittee(epoch iotago.EpochIndex, targetSlot i return false, err } + if committee == nil { + return false, nil + } + return committee.IsReused(), nil } diff --git a/pkg/storage/prunable/prunable_slot.go b/pkg/storage/prunable/prunable_slot.go index 9bf7870a6..a7632ca3c 100644 --- a/pkg/storage/prunable/prunable_slot.go +++ b/pkg/storage/prunable/prunable_slot.go @@ -14,6 +14,7 @@ import ( const ( slotPrefixBlocks byte = iota slotPrefixRootBlocks + slotPrefixMutations slotPrefixAttestations slotPrefixAccountDiffs slotPrefixPerformanceFactors @@ -51,6 +52,10 @@ func (p *Prunable) RootBlocks(slot iotago.SlotIndex) (*slotstore.Store[iotago.Bl ), nil } +func (p *Prunable) Mutations(slot iotago.SlotIndex) (kvstore.KVStore, error) { + return p.getKVStoreFromSlot(slot, kvstore.Realm{slotPrefixMutations}) +} + func (p *Prunable) Attestations(slot iotago.SlotIndex) (kvstore.KVStore, error) { return p.getKVStoreFromSlot(slot, kvstore.Realm{slotPrefixAttestations}) } diff --git a/pkg/storage/storage_prunable.go b/pkg/storage/storage_prunable.go index cc1508f69..a65cd9406 100644 --- a/pkg/storage/storage_prunable.go +++ b/pkg/storage/storage_prunable.go @@ -37,6 +37,10 @@ func (s *Storage) RootBlocks(slot iotago.SlotIndex) (*slotstore.Store[iotago.Blo return s.prunable.RootBlocks(slot) } +func (s *Storage) Mutations(slot iotago.SlotIndex) (kvstore.KVStore, error) { + return s.prunable.Mutations(slot) +} + func (s *Storage) Attestations(slot iotago.SlotIndex) (kvstore.KVStore, error) { return s.prunable.Attestations(slot) } diff --git a/pkg/tests/loss_of_acceptance_test.go b/pkg/tests/loss_of_acceptance_test.go new file mode 100644 index 000000000..e5bce77bb --- /dev/null +++ b/pkg/tests/loss_of_acceptance_test.go @@ -0,0 +1,262 @@ +package tests + +import ( + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/iotaledger/iota-core/pkg/protocol" + "github.com/iotaledger/iota-core/pkg/testsuite" + "github.com/iotaledger/iota-core/pkg/testsuite/mock" + iotago "github.com/iotaledger/iota.go/v4" +) + +func TestLossOfAcceptanceFromGenesis(t *testing.T) { + ts := testsuite.NewTestSuite(t, + testsuite.WithLivenessThresholdLowerBound(10), + testsuite.WithLivenessThresholdUpperBound(10), + testsuite.WithMinCommittableAge(2), + testsuite.WithMaxCommittableAge(4), + testsuite.WithEpochNearingThreshold(2), + testsuite.WithSlotsPerEpochExponent(3), + testsuite.WithGenesisTimestampOffset(100*10), + ) + defer ts.Shutdown() + + ts.AddBasicBlockIssuer("default") + node0 := ts.AddValidatorNode("node0") + ts.AddValidatorNode("node1") + ts.AddNode("node2") + + ts.Run(true, nil) + + // Create snapshot to use later. + snapshotPath := ts.Directory.Path(fmt.Sprintf("%d_snapshot", time.Now().Unix())) + require.NoError(t, ts.Node("node0").Protocol.MainEngineInstance().WriteSnapshot(snapshotPath)) + + // Revive chain on node0. + { + block0 := ts.IssueValidationBlock("block0", node0, + mock.WithIssuingTime(ts.API.TimeProvider().SlotStartTime(50)), + ) + require.EqualValues(t, 48, ts.Block("block0").SlotCommitmentID().Slot()) + // Reviving the chain should select one parent from the last committed slot. + require.Len(t, block0.Parents(), 1) + require.Equal(t, block0.Parents()[0].Alias(), "Genesis") + ts.AssertBlocksExist(ts.Blocks("block0"), true, ts.Nodes("node0")...) + } + + // Need to issue to slot 52 so that all other nodes can warp sync up to slot 49 and then commit slot 50 themselves. + { + ts.IssueBlocksAtSlots("", []iotago.SlotIndex{51, 52}, 2, "block0", ts.Nodes("node0"), true, nil) + + ts.AssertEqualStoredCommitmentAtIndex(50, ts.Nodes()...) + ts.AssertLatestCommitmentSlotIndex(50, ts.Nodes()...) + ts.AssertBlocksExist(ts.Blocks("block0"), true, ts.Nodes()...) + } + + // Continue issuing on all nodes for a few slots. + { + ts.IssueBlocksAtSlots("", []iotago.SlotIndex{53, 54, 55, 56, 57}, 3, "52.1", ts.Nodes(), true, nil) + + ts.AssertEqualStoredCommitmentAtIndex(55, ts.Nodes()...) + ts.AssertLatestCommitmentSlotIndex(55, ts.Nodes()...) + ts.AssertBlocksInCacheAccepted(ts.BlocksWithPrefix("57.0"), true, ts.Nodes()...) + } + + // Start node3 from genesis snapshot. + { + node3 := ts.AddNode("node3") + node3.Initialize(true, + protocol.WithSnapshotPath(snapshotPath), + protocol.WithBaseDirectory(ts.Directory.PathWithCreate(node3.Name)), + ) + ts.Wait() + } + + // Continue issuing on all nodes for a few slots. + { + ts.IssueBlocksAtSlots("", []iotago.SlotIndex{58, 59}, 3, "57.2", ts.Nodes("node0", "node1", "node2"), true, nil) + + ts.AssertEqualStoredCommitmentAtIndex(57, ts.Nodes()...) + ts.AssertLatestCommitmentSlotIndex(57, ts.Nodes()...) + ts.AssertBlocksInCacheAccepted(ts.BlocksWithPrefix("59.0"), true, ts.Nodes()...) + } + + // Check that commitments from 1-49 are empty. + for slot := iotago.SlotIndex(1); slot <= 49; slot++ { + ts.AssertStorageCommitmentBlocks(slot, nil, ts.Nodes()...) + } +} + +func TestLossOfAcceptanceFromSnapshot(t *testing.T) { + ts := testsuite.NewTestSuite(t, + testsuite.WithLivenessThresholdLowerBound(10), + testsuite.WithLivenessThresholdUpperBound(10), + testsuite.WithMinCommittableAge(2), + testsuite.WithMaxCommittableAge(4), + testsuite.WithEpochNearingThreshold(2), + testsuite.WithSlotsPerEpochExponent(3), + testsuite.WithGenesisTimestampOffset(100*10), + ) + defer ts.Shutdown() + + ts.AddBasicBlockIssuer("default") + node0 := ts.AddValidatorNode("node0") + ts.AddValidatorNode("node1") + node2 := ts.AddNode("node2") + + ts.Run(true, nil) + + // Issue up to slot 10, committing slot 8. + { + ts.IssueBlocksAtSlots("", []iotago.SlotIndex{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 3, "Genesis", ts.Nodes(), true, nil) + + ts.AssertEqualStoredCommitmentAtIndex(8, ts.Nodes()...) + ts.AssertLatestCommitmentSlotIndex(8, ts.Nodes()...) + ts.AssertBlocksInCacheAccepted(ts.BlocksWithPrefix("10.0"), true, ts.Nodes()...) + } + + // Create snapshot and restart node0 from it. + var node0restarted *mock.Node + { + snapshotPath := ts.Directory.Path(fmt.Sprintf("%d_snapshot", time.Now().Unix())) + require.NoError(t, ts.Node("node0").Protocol.MainEngineInstance().WriteSnapshot(snapshotPath)) + + node0restarted = ts.AddNode("node0-restarted") + node0restarted.Validator = node0.Validator + node0restarted.Initialize(true, + protocol.WithSnapshotPath(snapshotPath), + protocol.WithBaseDirectory(ts.Directory.PathWithCreate(node0restarted.Name)), + ) + ts.Wait() + } + + for _, node := range ts.Nodes("node0", "node1") { + ts.RemoveNode(node.Name) + node.Shutdown() + } + + // Revive chain on node0-restarted. + { + block0 := ts.IssueValidationBlock("block0", node0restarted, + mock.WithIssuingTime(ts.API.TimeProvider().SlotStartTime(20)), + ) + require.EqualValues(t, 18, block0.SlotCommitmentID().Slot()) + // Reviving the chain should select one parent from the last committed slot. + require.Len(t, block0.Parents(), 1) + require.EqualValues(t, block0.Parents()[0].Slot(), 8) + ts.AssertBlocksExist(ts.Blocks("block0"), true, ts.Nodes("node0-restarted")...) + } + + // Need to issue to slot 22 so that all other nodes can warp sync up to slot 19 and then commit slot 20 themselves. + { + ts.IssueBlocksAtSlots("", []iotago.SlotIndex{21, 22}, 2, "block0", ts.Nodes("node0-restarted"), true, nil) + + ts.AssertEqualStoredCommitmentAtIndex(20, ts.Nodes()...) + ts.AssertLatestCommitmentSlotIndex(20, ts.Nodes()...) + } + + // Continue issuing on all online nodes for a few slots. + { + // Since issued blocks in slot 9 and 10 are be orphaned, we need to make sure that the already issued transactions in the testsuite + // are not used again. + ts.SetAutomaticTransactionIssuingCounters(node2.Partition, 24) + + ts.IssueBlocksAtSlots("", []iotago.SlotIndex{23, 24, 25}, 3, "22.1", ts.Nodes(), true, nil) + + ts.AssertEqualStoredCommitmentAtIndex(23, ts.Nodes()...) + ts.AssertLatestCommitmentSlotIndex(23, ts.Nodes()...) + ts.AssertBlocksInCacheAccepted(ts.BlocksWithPrefix("25.0"), true, ts.Nodes()...) + } + + // Check that commitments from 8-19 are empty -> all previously accepted blocks in 9,10 have been orphaned. + for _, slot := range []iotago.SlotIndex{9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19} { + ts.AssertStorageCommitmentBlocks(slot, nil, ts.Nodes()...) + } +} + +func TestLossOfAcceptanceWithRestartFromDisk(t *testing.T) { + ts := testsuite.NewTestSuite(t, + testsuite.WithLivenessThresholdLowerBound(10), + testsuite.WithLivenessThresholdUpperBound(10), + testsuite.WithMinCommittableAge(2), + testsuite.WithMaxCommittableAge(4), + testsuite.WithEpochNearingThreshold(2), + testsuite.WithSlotsPerEpochExponent(3), + testsuite.WithGenesisTimestampOffset(100*10), + ) + defer ts.Shutdown() + + ts.AddBasicBlockIssuer("default") + node0 := ts.AddValidatorNode("node0") + ts.AddValidatorNode("node1") + node2 := ts.AddNode("node2") + + ts.Run(true, nil) + + // Issue up to slot 10, committing slot 8. + { + ts.IssueBlocksAtSlots("", []iotago.SlotIndex{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 3, "Genesis", ts.Nodes(), true, nil) + + ts.AssertEqualStoredCommitmentAtIndex(8, ts.Nodes()...) + ts.AssertLatestCommitmentSlotIndex(8, ts.Nodes()...) + ts.AssertBlocksInCacheAccepted(ts.BlocksWithPrefix("10.0"), true, ts.Nodes()...) + } + + for _, node := range ts.Nodes("node0", "node1") { + ts.RemoveNode(node.Name) + node.Shutdown() + } + + // Create snapshot and restart node0 from it. + var node0restarted *mock.Node + { + node0restarted = ts.AddNode("node0-restarted") + node0restarted.Validator = node0.Validator + node0restarted.Initialize(true, + protocol.WithBaseDirectory(ts.Directory.PathWithCreate(node0.Name)), + ) + ts.Wait() + } + + // Revive chain on node0-restarted. + { + block0 := ts.IssueValidationBlock("block0", node0restarted, + mock.WithIssuingTime(ts.API.TimeProvider().SlotStartTime(20)), + ) + require.EqualValues(t, 18, block0.SlotCommitmentID().Slot()) + // Reviving the chain should select one parent from the last committed slot. + require.Len(t, block0.Parents(), 1) + require.EqualValues(t, block0.Parents()[0].Slot(), 8) + ts.AssertBlocksExist(ts.Blocks("block0"), true, ts.Nodes("node0-restarted")...) + } + + // Need to issue to slot 22 so that all other nodes can warp sync up to slot 19 and then commit slot 20 themselves. + { + ts.IssueBlocksAtSlots("", []iotago.SlotIndex{21, 22}, 2, "block0", ts.Nodes("node0-restarted"), true, nil) + + ts.AssertEqualStoredCommitmentAtIndex(20, ts.Nodes()...) + ts.AssertLatestCommitmentSlotIndex(20, ts.Nodes()...) + } + + // Continue issuing on all online nodes for a few slots. + { + // Since issued blocks in slot 9 and 10 are be orphaned, we need to make sure that the already issued transactions in the testsuite + // are not used again. + ts.SetAutomaticTransactionIssuingCounters(node2.Partition, 24) + + ts.IssueBlocksAtSlots("", []iotago.SlotIndex{23, 24, 25}, 3, "22.1", ts.Nodes(), true, nil) + + ts.AssertEqualStoredCommitmentAtIndex(23, ts.Nodes()...) + ts.AssertLatestCommitmentSlotIndex(23, ts.Nodes()...) + ts.AssertBlocksInCacheAccepted(ts.BlocksWithPrefix("25.0"), true, ts.Nodes()...) + } + + // Check that commitments from 8-19 are empty -> all previously accepted blocks in 9,10 have been orphaned. + for _, slot := range []iotago.SlotIndex{9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19} { + ts.AssertStorageCommitmentBlocks(slot, nil, ts.Nodes()...) + } +} diff --git a/pkg/tests/protocol_engine_rollback_test.go b/pkg/tests/protocol_engine_rollback_test.go index e0a8396f4..46d1f4abc 100644 --- a/pkg/tests/protocol_engine_rollback_test.go +++ b/pkg/tests/protocol_engine_rollback_test.go @@ -762,7 +762,7 @@ func TestProtocol_EngineRollbackNoFinalizationBeforePointOfNoReturn(t *testing.T require.Len(t, committeeEpoch.IDs(), 4) } - // Commmittee for the future epoch does not exist. + // Committee for the future epoch does not exist. committeeEpoch2, err := newEngine.Storage.Committee().Load(2) require.NoError(t, err) require.Nil(t, committeeEpoch2) diff --git a/pkg/tests/protocol_startup_test.go b/pkg/tests/protocol_startup_test.go index 20ffe89a3..260159175 100644 --- a/pkg/tests/protocol_startup_test.go +++ b/pkg/tests/protocol_startup_test.go @@ -293,8 +293,7 @@ func Test_StartNodeFromSnapshotAndDisk(t *testing.T) { ) ts.Wait() - // Everything that was accepted before shutting down should be available on disk (verifying that restoring the block cache from disk works). - ts.AssertBlocksExist(ts.BlocksWithPrefixes("8", "9", "11", "12", "13.0", "13.1", "13.2", "13.3"), true, ts.Nodes("nodeC-restarted")...) + // Everything that was accepted before shutting down should be available on disk. ts.AssertStorageRootBlocks(expectedStorageRootBlocksFrom0, ts.Nodes("nodeC-restarted")...) for _, slot := range []iotago.SlotIndex{8, 9, 11} { diff --git a/pkg/testsuite/mock/acceptance_loss.go b/pkg/testsuite/mock/acceptance_loss.go new file mode 100644 index 000000000..8e5909c7e --- /dev/null +++ b/pkg/testsuite/mock/acceptance_loss.go @@ -0,0 +1,46 @@ +package mock + +import ( + "time" + + "github.com/iotaledger/hive.go/ierrors" + iotago "github.com/iotaledger/iota.go/v4" +) + +func (i *BlockIssuer) reviveChain(issuingTime time.Time, node *Node) (*iotago.Commitment, iotago.BlockID, error) { + lastCommittedSlot := node.Protocol.MainEngineInstance().Storage.Settings().LatestCommitment().Slot() + apiForSlot := node.Protocol.APIForSlot(lastCommittedSlot) + + // Get a rootblock as recent as possible for the parent. + parentBlockID := iotago.EmptyBlockID + for rootBlock := range node.Protocol.MainEngineInstance().EvictionState.ActiveRootBlocks() { + if rootBlock.Slot() > parentBlockID.Slot() { + parentBlockID = rootBlock + } + + // Exit the loop if we found a rootblock in the last committed slot (which is the highest we can get). + if parentBlockID.Slot() == lastCommittedSlot { + break + } + } + + issuingSlot := apiForSlot.TimeProvider().SlotFromTime(issuingTime) + + // Force commitments until minCommittableAge relative to the block's issuing time. We basically "pretend" that + // this block was already accepted at the time of issuing so that we have a commitment to reference. + if issuingSlot < apiForSlot.ProtocolParameters().MinCommittableAge() { // Should never happen as we're beyond maxCommittableAge which is > minCommittableAge. + return nil, iotago.EmptyBlockID, ierrors.Errorf("issuing slot %d is smaller than min committable age %d", issuingSlot, apiForSlot.ProtocolParameters().MinCommittableAge()) + } + commitUntilSlot := issuingSlot - apiForSlot.ProtocolParameters().MinCommittableAge() + + if err := node.Protocol.MainEngineInstance().Notarization.ForceCommitUntil(commitUntilSlot); err != nil { + return nil, iotago.EmptyBlockID, ierrors.Wrapf(err, "failed to force commit until slot %d", commitUntilSlot) + } + + commitment, err := node.Protocol.MainEngineInstance().Storage.Commitments().Load(commitUntilSlot) + if err != nil { + return nil, iotago.EmptyBlockID, ierrors.Wrapf(err, "failed to commit until slot %d to revive chain", commitUntilSlot) + } + + return commitment.Commitment(), parentBlockID, nil +} diff --git a/pkg/testsuite/mock/blockissuer.go b/pkg/testsuite/mock/blockissuer.go index 4e284eb22..91d008f6f 100644 --- a/pkg/testsuite/mock/blockissuer.go +++ b/pkg/testsuite/mock/blockissuer.go @@ -30,6 +30,7 @@ var ( ErrBlockAttacherInvalidBlock = ierrors.New("invalid block") ErrBlockAttacherAttachingNotPossible = ierrors.New("attaching not possible") ErrBlockAttacherIncompleteBlockNotAllowed = ierrors.New("incomplete block is not allowed on this node") + ErrBlockTooRecent = ierrors.New("block is too recent compared to latest commitment") ) // TODO: make sure an honest validator does not issue blocks within the same slot ratification period in two conflicting chains. @@ -90,9 +91,31 @@ func (i *BlockIssuer) Shutdown() { i.workerPool.ShutdownComplete.Wait() } -func (i *BlockIssuer) CreateValidationBlock(ctx context.Context, alias string, issuerAccount Account, node *Node, opts ...options.Option[ValidatorBlockParams]) *blocks.Block { +func (i *BlockIssuer) CreateValidationBlock(ctx context.Context, alias string, issuerAccount Account, node *Node, opts ...options.Option[ValidatorBlockParams]) (*blocks.Block, error) { blockParams := options.Apply(&ValidatorBlockParams{}, opts) + if blockParams.BlockHeader.IssuingTime == nil { + issuingTime := time.Now().UTC() + blockParams.BlockHeader.IssuingTime = &issuingTime + } + + if blockParams.BlockHeader.SlotCommitment == nil { + var err error + blockParams.BlockHeader.SlotCommitment, err = i.getAddressableCommitment(node.Protocol.CurrentAPI().TimeProvider().SlotFromTime(*blockParams.BlockHeader.IssuingTime), node) + if err != nil && ierrors.Is(err, ErrBlockTooRecent) { + commitment, parentID, err := i.reviveChain(*blockParams.BlockHeader.IssuingTime, node) + if err != nil { + return nil, ierrors.Wrap(err, "failed to revive chain") + } + blockParams.BlockHeader.SlotCommitment = commitment + blockParams.BlockHeader.References = make(model.ParentReferences) + blockParams.BlockHeader.References[iotago.StrongParentType] = []iotago.BlockID{parentID} + + } else if err != nil { + return nil, ierrors.Wrap(err, "error getting commitment") + } + } + if blockParams.BlockHeader.References == nil { // TODO: change this to get references for validator block references, err := i.getReferences(ctx, nil, node, blockParams.BlockHeader.ParentsCount) @@ -154,11 +177,12 @@ func (i *BlockIssuer) CreateValidationBlock(ctx context.Context, alias string, i modelBlock.ID().RegisterAlias(alias) - return blocks.NewBlock(modelBlock) + return blocks.NewBlock(modelBlock), nil } func (i *BlockIssuer) IssueValidationBlock(ctx context.Context, alias string, node *Node, opts ...options.Option[ValidatorBlockParams]) *blocks.Block { - block := i.CreateValidationBlock(ctx, alias, NewEd25519Account(i.AccountID, i.privateKey), node, opts...) + block, err := i.CreateValidationBlock(ctx, alias, NewEd25519Account(i.AccountID, i.privateKey), node, opts...) + require.NoError(i.Testing, err) require.NoError(i.Testing, i.IssueBlock(block.ModelBlock(), node)) @@ -176,9 +200,22 @@ func (i *BlockIssuer) retrieveAPI(blockParams *BlockHeaderParams, node *Node) (i } // CreateBlock creates a new block with the options. -func (i *BlockIssuer) CreateBasicBlock(ctx context.Context, alias string, node *Node, opts ...options.Option[BasicBlockParams]) *blocks.Block { +func (i *BlockIssuer) CreateBasicBlock(ctx context.Context, alias string, node *Node, opts ...options.Option[BasicBlockParams]) (*blocks.Block, error) { blockParams := options.Apply(&BasicBlockParams{}, opts) + if blockParams.BlockHeader.IssuingTime == nil { + issuingTime := time.Now().UTC() + blockParams.BlockHeader.IssuingTime = &issuingTime + } + + if blockParams.BlockHeader.SlotCommitment == nil { + var err error + blockParams.BlockHeader.SlotCommitment, err = i.getAddressableCommitment(node.Protocol.CurrentAPI().TimeProvider().SlotFromTime(*blockParams.BlockHeader.IssuingTime), node) + if err != nil { + return nil, ierrors.Wrap(err, "error getting commitment") + } + } + if blockParams.BlockHeader.References == nil { references, err := i.getReferences(ctx, blockParams.Payload, node, blockParams.BlockHeader.ParentsCount) require.NoError(i.Testing, err) @@ -233,11 +270,12 @@ func (i *BlockIssuer) CreateBasicBlock(ctx context.Context, alias string, node * modelBlock.ID().RegisterAlias(alias) - return blocks.NewBlock(modelBlock) + return blocks.NewBlock(modelBlock), err } func (i *BlockIssuer) IssueBasicBlock(ctx context.Context, alias string, node *Node, opts ...options.Option[BasicBlockParams]) *blocks.Block { - block := i.CreateBasicBlock(ctx, alias, node, opts...) + block, err := i.CreateBasicBlock(ctx, alias, node, opts...) + require.NoError(i.Testing, err) require.NoErrorf(i.Testing, i.IssueBlock(block.ModelBlock(), node), "%s > failed to issue block with alias %s", i.Name, alias) @@ -445,7 +483,7 @@ func (i *BlockIssuer) setDefaultBlockParams(blockParams *BlockHeaderParams, node if blockParams.SlotCommitment == nil { var err error - blockParams.SlotCommitment, err = i.getCommitment(node.Protocol.CurrentAPI().TimeProvider().SlotFromTime(*blockParams.IssuingTime), node) + blockParams.SlotCommitment, err = i.getAddressableCommitment(node.Protocol.CurrentAPI().TimeProvider().SlotFromTime(*blockParams.IssuingTime), node) if err != nil { return ierrors.Wrap(err, "error getting commitment") } @@ -469,12 +507,12 @@ func (i *BlockIssuer) setDefaultBlockParams(blockParams *BlockHeaderParams, node return nil } -func (i *BlockIssuer) getCommitment(blockSlot iotago.SlotIndex, node *Node) (*iotago.Commitment, error) { +func (i *BlockIssuer) getAddressableCommitment(blockSlot iotago.SlotIndex, node *Node) (*iotago.Commitment, error) { protoParams := node.Protocol.CurrentAPI().ProtocolParameters() commitment := node.Protocol.MainEngineInstance().Storage.Settings().LatestCommitment().Commitment() if blockSlot > commitment.Slot+protoParams.MaxCommittableAge() { - return nil, ierrors.Errorf("can't issue block: block slot %d is too far in the future, latest commitment is %d", blockSlot, commitment.Slot) + return nil, ierrors.Wrapf(ErrBlockTooRecent, "can't issue block: block slot %d is too far in the future, latest commitment is %d", blockSlot, commitment.Slot) } if blockSlot < commitment.Slot+protoParams.MinCommittableAge() { @@ -526,6 +564,10 @@ func (i *BlockIssuer) IssueBlock(block *model.Block, node *Node) error { return err } + if _, isValidationBlock := block.ValidationBlock(); isValidationBlock { + _ = node.Protocol.MainEngineInstance().Storage.Settings().SetLatestIssuedValidationBlock(block) + } + i.events.BlockIssued.Trigger(block) return nil diff --git a/pkg/testsuite/mock/node.go b/pkg/testsuite/mock/node.go index 53d6e0c61..5c420dcb5 100644 --- a/pkg/testsuite/mock/node.go +++ b/pkg/testsuite/mock/node.go @@ -181,17 +181,25 @@ func (n *Node) hookLogging(failOnBlockFiltered bool) { fmt.Printf("%s > Network.AttestationsRequestReceived: from %s %s\n", n.Name, source, id) }) - //events.ChainManager.CommitmentBelowRoot.Hook(func(commitmentID iotago.CommitmentID) { + events.Network.WarpSyncResponseReceived.Hook(func(id iotago.CommitmentID, ds iotago.BlockIDs, m *merklehasher.Proof[iotago.Identifier], ds2 iotago.TransactionIDs, m2 *merklehasher.Proof[iotago.Identifier], id2 peer.ID) { + fmt.Printf("%s > Network.WarpSyncResponseReceived: from %s %s\n", n.Name, id2, id) + }) + + events.Network.WarpSyncRequestReceived.Hook(func(id iotago.CommitmentID, id2 peer.ID) { + fmt.Printf("%s > Network.WarpSyncRequestReceived: from %s %s\n", n.Name, id2, id) + }) + + // events.ChainManager.CommitmentBelowRoot.Hook(func(commitmentID iotago.CommitmentID) { // fmt.Printf("%s > ChainManager.CommitmentBelowRoot: %s\n", n.Name, commitmentID) - //}) + // }) events.ChainManager.ForkDetected.Hook(func(fork *chainmanager.Fork) { fmt.Printf("%s > ChainManager.ForkDetected: %s\n", n.Name, fork) }) - //events.Engine.TipManager.BlockAdded.Hook(func(tipMetadata tipmanager.TipMetadata) { + // events.Engine.TipManager.BlockAdded.Hook(func(tipMetadata tipmanager.TipMetadata) { // fmt.Printf("%s > TipManager.BlockAdded: %s in pool %d\n", n.Name, tipMetadata.ID(), tipMetadata.TipPool().Get()) - //}) + // }) events.CandidateEngineActivated.Hook(func(e *engine.Engine) { fmt.Printf("%s > CandidateEngineActivated: %s, ChainID:%s Slot:%s\n", n.Name, e.Name(), e.ChainID(), e.ChainID().Slot()) @@ -203,6 +211,12 @@ func (n *Node) hookLogging(failOnBlockFiltered bool) { fmt.Printf("%s > MainEngineSwitched: %s, ChainID:%s Slot:%s\n", n.Name, e.Name(), e.ChainID(), e.ChainID().Slot()) }) + events.MainEngineRestarted.Hook(func(e *engine.Engine) { + fmt.Printf("%s > MainEngineRestarted: %s, ChainID:%s Slot:%s\n", n.Name, e.Name(), e.ChainID(), e.ChainID().Slot()) + + n.attachEngineLogsWithName(failOnBlockFiltered, e, fmt.Sprintf("Main2 - %s", e.Name()[:8])) + }) + events.Network.Error.Hook(func(err error, id peer.ID) { fmt.Printf("%s > Network.Error: from %s %s\n", n.Name, id, err) }) @@ -212,8 +226,7 @@ func (n *Node) hookLogging(failOnBlockFiltered bool) { }) } -func (n *Node) attachEngineLogs(failOnBlockFiltered bool, instance *engine.Engine) { - engineName := fmt.Sprintf("%s - %s", lo.Cond(n.Protocol.MainEngineInstance() != instance, "Candidate", "Main"), instance.Name()[:8]) +func (n *Node) attachEngineLogsWithName(failOnBlockFiltered bool, instance *engine.Engine, engineName string) { events := instance.Events events.BlockDAG.BlockAttached.Hook(func(block *blocks.Block) { @@ -333,7 +346,7 @@ func (n *Node) attachEngineLogs(failOnBlockFiltered bool, instance *engine.Engin require.NoError(n.Testing, err) } - fmt.Printf("%s > [%s] NotarizationManager.SlotCommitted: %s %s %s %s %s\n", n.Name, engineName, details.Commitment.ID(), details.Commitment, acceptedBlocks, roots, attestationBlockIDs) + fmt.Printf("%s > [%s] NotarizationManager.SlotCommitted: %s %s Accepted Blocks: %s\n %s\n Attestations: %s\n", n.Name, engineName, details.Commitment.ID(), details.Commitment, acceptedBlocks, roots, attestationBlockIDs) }) events.Notarization.LatestCommitmentUpdated.Hook(func(commitment *model.Commitment) { @@ -428,6 +441,12 @@ func (n *Node) attachEngineLogs(failOnBlockFiltered bool, instance *engine.Engin }) } +func (n *Node) attachEngineLogs(failOnBlockFiltered bool, instance *engine.Engine) { + engineName := fmt.Sprintf("%s - %s", lo.Cond(n.Protocol.MainEngineInstance() != instance, "Candidate", "Main"), instance.Name()[:8]) + + n.attachEngineLogsWithName(failOnBlockFiltered, instance, engineName) +} + func (n *Node) Wait() { n.Workers.WaitChildren() } diff --git a/pkg/testsuite/storage_commitments.go b/pkg/testsuite/storage_commitments.go index c71f8f006..b8c8e22f4 100644 --- a/pkg/testsuite/storage_commitments.go +++ b/pkg/testsuite/storage_commitments.go @@ -57,3 +57,32 @@ func (t *TestSuite) AssertEqualStoredCommitmentAtIndex(index iotago.SlotIndex, n return nil }) } + +func (t *TestSuite) AssertStorageCommitmentBlocks(slot iotago.SlotIndex, expectedBlocks iotago.BlockIDs, nodes ...*mock.Node) { + mustNodes(nodes) + + t.Eventually(func() error { + for _, node := range nodes { + storedCommitment, err := node.Protocol.MainEngineInstance().Storage.Commitments().Load(slot) + if err != nil { + return ierrors.Wrapf(err, "AssertStorageCommitmentBlocks: %s: error loading commitment for slot: %d", node.Name, slot) + } + + committedSlot, err := node.Protocol.MainEngineInstance().CommittedSlot(storedCommitment.ID()) + if err != nil { + return ierrors.Wrapf(err, "AssertStorageCommitmentBlocks: %s: error getting committed slot for commitment: %s", node.Name, storedCommitment.ID()) + } + + committedBlocks, err := committedSlot.BlockIDs() + if err != nil { + return ierrors.Wrapf(err, "AssertStorageCommitmentBlocks: %s: error getting committed blocks for slot: %d", node.Name, slot) + } + + if !cmp.Equal(committedBlocks, expectedBlocks) { + return ierrors.Errorf("AssertStorageCommitmentBlocks: %s: expected %s, got %s", node.Name, expectedBlocks, committedBlocks) + } + } + + return nil + }) +} diff --git a/pkg/testsuite/testsuite.go b/pkg/testsuite/testsuite.go index 5e50bef92..534c8710c 100644 --- a/pkg/testsuite/testsuite.go +++ b/pkg/testsuite/testsuite.go @@ -555,3 +555,7 @@ func (t *TestSuite) SplitIntoPartitions(partitions map[string][]*mock.Node) { func (t *TestSuite) MergePartitionsToMain(partitions ...string) { t.network.MergePartitionsToMain(partitions...) } + +func (t *TestSuite) SetAutomaticTransactionIssuingCounters(partition string, newValue int) { + t.automaticTransactionIssuingCounters.Set(partition, newValue) +} diff --git a/pkg/testsuite/testsuite_issue_blocks.go b/pkg/testsuite/testsuite_issue_blocks.go index 08c727d5a..4d47c232e 100644 --- a/pkg/testsuite/testsuite_issue_blocks.go +++ b/pkg/testsuite/testsuite_issue_blocks.go @@ -65,7 +65,8 @@ func (t *TestSuite) CreateBasicBlock(alias string, blockIssuer *mock.BlockIssuer t.mutex.Lock() defer t.mutex.Unlock() - block := blockIssuer.CreateBasicBlock(context.Background(), alias, node, blockOpts...) + block, err := blockIssuer.CreateBasicBlock(context.Background(), alias, node, blockOpts...) + require.NoError(t.Testing, err) t.registerBlock(alias, block) } diff --git a/tools/docker-network/docker-compose.yml b/tools/docker-network/docker-compose.yml index ebc264856..e547cb9c1 100644 --- a/tools/docker-network/docker-compose.yml +++ b/tools/docker-network/docker-compose.yml @@ -11,7 +11,6 @@ services: ${COMMON_CONFIG} ${MANUALPEERING_CONFIG} --p2p.identityPrivateKey=08735375679f3d8031353e94282ed1d65119e5c288fe56d6639d9184a3f978fee8febfedff11cc376daea0f59c395ae2e9a870a25ac4e36093000fbf4d0e8f18 - --blockIssuer.rateSetterEnabled=true --validator.enabled=true --validator.ignoreBootstrapped=true --validator.account=0x907c02e9302e0f0571f10f885594e56d8c54ff0708ab7a39bc1b74d396b93b12 @@ -45,7 +44,6 @@ services: ${COMMON_CONFIG} ${MANUALPEERING_CONFIG} --p2p.identityPrivateKey=ba771419c52132a0dfb2521ed18667813f398da159010a55a0a482af939affb92d3338789ad4a07a7631b91791deb11f82ed5dc612822f24275e9f7a313b691f - --blockIssuer.rateSetterEnabled=true --validator.enabled=true --validator.account=0x375358f92cc94750669598b0aaa55a6ff73310b90710e1fad524c0f911be0fea --validator.privateKey=3a5d39f8b60367a17fd54dac2a32c172c8e1fd6cf74ce65f1e13edba565f281705c1de274451db8de8182d64c6ee0dca3ae0c9077e0b4330c976976171d79064 @@ -70,7 +68,6 @@ services: ${COMMON_CONFIG} ${MANUALPEERING_CONFIG} --p2p.identityPrivateKey=a6261ac049755675ff1437654ca9f83b305055f01ff08c4f039209ef5a4a7d96d06fb61df77a8815209a8f4d204226dee593e50d0ec897ec440a2c1fbde77656 - --blockIssuer.rateSetterEnabled=true --validator.enabled=true --validator.account=0x6aee704f25558e8aa7630fed0121da53074188abc423b3c5810f80be4936eb6e --validator.privateKey=db39d2fde6301d313b108dc9db1ee724d0f405f6fde966bd776365bc5f4a5fb31e4b21eb51dcddf65c20db1065e1f1514658b23a3ddbf48d30c0efc926a9a648 diff --git a/tools/evil-spammer/go.mod b/tools/evil-spammer/go.mod index 6a1e0c8e3..2faecf860 100644 --- a/tools/evil-spammer/go.mod +++ b/tools/evil-spammer/go.mod @@ -8,13 +8,13 @@ replace github.com/iotaledger/iota-core/tools/genesis-snapshot => ../genesis-sna require ( github.com/AlecAivazis/survey/v2 v2.3.7 - github.com/iotaledger/hive.go/app v0.0.0-20231010083704-a2a91fb9e0ab - github.com/iotaledger/hive.go/crypto v0.0.0-20231010083704-a2a91fb9e0ab - github.com/iotaledger/hive.go/ds v0.0.0-20231010083704-a2a91fb9e0ab - github.com/iotaledger/hive.go/ierrors v0.0.0-20231010083704-a2a91fb9e0ab - github.com/iotaledger/hive.go/lo v0.0.0-20231010083704-a2a91fb9e0ab - github.com/iotaledger/hive.go/logger v0.0.0-20231010083704-a2a91fb9e0ab - github.com/iotaledger/hive.go/runtime v0.0.0-20231010083704-a2a91fb9e0ab + github.com/iotaledger/hive.go/app v0.0.0-20231010121205-23c1f3fcddd5 + github.com/iotaledger/hive.go/crypto v0.0.0-20231010121205-23c1f3fcddd5 + github.com/iotaledger/hive.go/ds v0.0.0-20231010121205-23c1f3fcddd5 + github.com/iotaledger/hive.go/ierrors v0.0.0-20231010121205-23c1f3fcddd5 + github.com/iotaledger/hive.go/lo v0.0.0-20231010121205-23c1f3fcddd5 + github.com/iotaledger/hive.go/logger v0.0.0-20231010121205-23c1f3fcddd5 + github.com/iotaledger/hive.go/runtime v0.0.0-20231010121205-23c1f3fcddd5 github.com/iotaledger/iota-core v0.0.0-00010101000000-000000000000 github.com/iotaledger/iota-core/tools/genesis-snapshot v0.0.0-00010101000000-000000000000 github.com/iotaledger/iota.go/v4 v4.0.0-20231005184534-62e6761a7b7c @@ -37,12 +37,12 @@ require ( github.com/holiman/uint256 v1.2.3 // indirect github.com/iancoleman/orderedmap v0.3.0 // indirect github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7 // indirect - github.com/iotaledger/hive.go/ads v0.0.0-20231010083704-a2a91fb9e0ab // indirect - github.com/iotaledger/hive.go/constraints v0.0.0-20231010083704-a2a91fb9e0ab // indirect - github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231010083704-a2a91fb9e0ab // indirect - github.com/iotaledger/hive.go/kvstore v0.0.0-20231010083704-a2a91fb9e0ab // indirect - github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231010083704-a2a91fb9e0ab // indirect - github.com/iotaledger/hive.go/stringify v0.0.0-20231010083704-a2a91fb9e0ab // indirect + github.com/iotaledger/hive.go/ads v0.0.0-20231010121205-23c1f3fcddd5 // indirect + github.com/iotaledger/hive.go/constraints v0.0.0-20231010121205-23c1f3fcddd5 // indirect + github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231010121205-23c1f3fcddd5 // indirect + github.com/iotaledger/hive.go/kvstore v0.0.0-20231010121205-23c1f3fcddd5 // indirect + github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231010121205-23c1f3fcddd5 // indirect + github.com/iotaledger/hive.go/stringify v0.0.0-20231010121205-23c1f3fcddd5 // indirect github.com/ipfs/go-cid v0.4.1 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect diff --git a/tools/evil-spammer/go.sum b/tools/evil-spammer/go.sum index ceb658824..d8913b4b2 100644 --- a/tools/evil-spammer/go.sum +++ b/tools/evil-spammer/go.sum @@ -169,32 +169,32 @@ github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJ github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7 h1:dTrD7X2PTNgli6EbS4tV9qu3QAm/kBU3XaYZV2xdzys= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7/go.mod h1:ZRdPu684P0fQ1z8sXz4dj9H5LWHhz4a9oCtvjunkSrw= -github.com/iotaledger/hive.go/ads v0.0.0-20231010083704-a2a91fb9e0ab h1:3U1ADSPHU9GahpWujnkwOlGxA+NMl9l0j3ch4ISH1S0= -github.com/iotaledger/hive.go/ads v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:IAWZ/5It5P8B41mWyJXJVcG0vuikVRaTFKQnr2D2q+c= -github.com/iotaledger/hive.go/app v0.0.0-20231010083704-a2a91fb9e0ab h1:ePgcMl1XOXoF4kNfwMGn63GXZSihv+Z2t1GWLKqkGOw= -github.com/iotaledger/hive.go/app v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:eiZgbcwTDZ7d9hEait2EAwAhixWhceW4MXmuVk2EcEw= -github.com/iotaledger/hive.go/constraints v0.0.0-20231010083704-a2a91fb9e0ab h1:u7wN87wrmlPbUpNZhHfNKN6HLLQdY/zICIiJgVCSBvM= -github.com/iotaledger/hive.go/constraints v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:dOBOM2s4se3HcWefPe8sQLUalGXJ8yVXw58oK8jke3s= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231010083704-a2a91fb9e0ab h1:u4h5GdvR1MTNZHYkLx8USCadbkMkISZyYXudH5qEhZQ= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231010083704-a2a91fb9e0ab/go.mod h1:jn3TNmiNRIiQm/rS4VD+7wFHI2+UXABHvCA3PbQxBqI= -github.com/iotaledger/hive.go/crypto v0.0.0-20231010083704-a2a91fb9e0ab h1:0wGm1JPCBNK7h9WYtFZhSxmbhPmiBEzeeonUfJmVASE= -github.com/iotaledger/hive.go/crypto v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:jP68na941d9uq7RtnA8aQ/FtIGRGz/51cU4uXrInQFU= -github.com/iotaledger/hive.go/ds v0.0.0-20231010083704-a2a91fb9e0ab h1:Wlv4bCfT6IZ3LD3jzHCgT00MdL0LR7kzhlW3zQohKKA= -github.com/iotaledger/hive.go/ds v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:ZrqsjIJS2QCgGp7Ki+l4hWJQgzfBObUCemb5Upwlx18= -github.com/iotaledger/hive.go/ierrors v0.0.0-20231010083704-a2a91fb9e0ab h1:RMKlQlTpWSSfauU3KgNyv/8WGzTTx3G4KzFuLf1EhrM= -github.com/iotaledger/hive.go/ierrors v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:HcE8B5lP96enc/OALTb2/rIIi+yOLouRoHOKRclKmC8= -github.com/iotaledger/hive.go/kvstore v0.0.0-20231010083704-a2a91fb9e0ab h1:50bOe5+XlIj0pPqcMNkOl+nf4BEITIhWuSr1j3fieLI= -github.com/iotaledger/hive.go/kvstore v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:DeP4JF4N995LteD0+/o7NsW1bO5IXURIJ27A69Ca5+Y= -github.com/iotaledger/hive.go/lo v0.0.0-20231010083704-a2a91fb9e0ab h1:o9JT//S67PRsrEir1GSdlNz2hLYfG+6SDayxmJm+jKQ= -github.com/iotaledger/hive.go/lo v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:4oKCdMEhHMLCudBz79kuvJmgSY/DhfVePNIyJhew/80= -github.com/iotaledger/hive.go/logger v0.0.0-20231010083704-a2a91fb9e0ab h1:AWU4PRv5No/ghKH02JY3/N9Xpv2he+byVLRd7DPHJGg= -github.com/iotaledger/hive.go/logger v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:sxqWRdZ1OOxwkxVczuGcW034Mpt2vFh5ebJHO++ZYeI= -github.com/iotaledger/hive.go/runtime v0.0.0-20231010083704-a2a91fb9e0ab h1:ZE7MENkq9l8B+uIt1ulFfflDPnYX4Xxk6UYbzAPr8/U= -github.com/iotaledger/hive.go/runtime v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:fXVyQ1MAwxe/EmjAnG8WcQqbzGk9EW/FsJ/n16H/f/w= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231010083704-a2a91fb9e0ab h1:ebO3VhgcS2Dd8zBe+8Lizd8RoV00TUAm06tY0HGZmIE= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231010083704-a2a91fb9e0ab/go.mod h1:IJgaaxbgKCsNat18jlJJEAxCY2oVYR3F30B+M4vJ89I= -github.com/iotaledger/hive.go/stringify v0.0.0-20231010083704-a2a91fb9e0ab h1:nDsUCc2/EPFspZcKk3T4HklhUeI+B5ed3W+HoTciYzA= -github.com/iotaledger/hive.go/stringify v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:FTo/UWzNYgnQ082GI9QVM9HFDERqf9rw9RivNpqrnTs= +github.com/iotaledger/hive.go/ads v0.0.0-20231010121205-23c1f3fcddd5 h1:lbrYweNe+DB/kh7OYlogJMQfEDRpriyIgA+MmHmb01k= +github.com/iotaledger/hive.go/ads v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:IAWZ/5It5P8B41mWyJXJVcG0vuikVRaTFKQnr2D2q+c= +github.com/iotaledger/hive.go/app v0.0.0-20231010121205-23c1f3fcddd5 h1:P2BE6RylO7mlEtJSbNyuRz6OcJTNs0MZ0qFIN5lI3Kg= +github.com/iotaledger/hive.go/app v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:q2H/9TE6hvg6TKZI0YoxtbCdrf8O1sYBVjNT2Y3yAIU= +github.com/iotaledger/hive.go/constraints v0.0.0-20231010121205-23c1f3fcddd5 h1:AsgWEa8dpgF0JX3pc+nwaoQLlc5rKEoWAgrYdN1AIRE= +github.com/iotaledger/hive.go/constraints v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:dOBOM2s4se3HcWefPe8sQLUalGXJ8yVXw58oK8jke3s= +github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231010121205-23c1f3fcddd5 h1:T/vnm1B2SFYWz7QiQsQq0AuoJCXKwW3hBCITudvh//c= +github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231010121205-23c1f3fcddd5/go.mod h1:jn3TNmiNRIiQm/rS4VD+7wFHI2+UXABHvCA3PbQxBqI= +github.com/iotaledger/hive.go/crypto v0.0.0-20231010121205-23c1f3fcddd5 h1:8TgnDcl6gIqXqgTnx4wA3LZt8vwAsVfekcKlPK5WRNY= +github.com/iotaledger/hive.go/crypto v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:jP68na941d9uq7RtnA8aQ/FtIGRGz/51cU4uXrInQFU= +github.com/iotaledger/hive.go/ds v0.0.0-20231010121205-23c1f3fcddd5 h1:xyVUPe/tZtS3nkg8fU1GB1ZyUo7D32YfrY0XNA8GZhM= +github.com/iotaledger/hive.go/ds v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:ZrqsjIJS2QCgGp7Ki+l4hWJQgzfBObUCemb5Upwlx18= +github.com/iotaledger/hive.go/ierrors v0.0.0-20231010121205-23c1f3fcddd5 h1:Q8giZLqmIJRlcUS5x1TxVlS5X+iHgZ8zOq4GsK3YWnQ= +github.com/iotaledger/hive.go/ierrors v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:HcE8B5lP96enc/OALTb2/rIIi+yOLouRoHOKRclKmC8= +github.com/iotaledger/hive.go/kvstore v0.0.0-20231010121205-23c1f3fcddd5 h1:nI6LJ32saPs1c3HwA7V3T2jo3bh4+UuhBg1EsBK9N7k= +github.com/iotaledger/hive.go/kvstore v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:DeP4JF4N995LteD0+/o7NsW1bO5IXURIJ27A69Ca5+Y= +github.com/iotaledger/hive.go/lo v0.0.0-20231010121205-23c1f3fcddd5 h1:XVKwwjBJGOUuNzormIYJMWgqyscgusR5WtxA3rxql5g= +github.com/iotaledger/hive.go/lo v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:4oKCdMEhHMLCudBz79kuvJmgSY/DhfVePNIyJhew/80= +github.com/iotaledger/hive.go/logger v0.0.0-20231010121205-23c1f3fcddd5 h1:hp+Bstpyxgeci0eDnEp0CZDbavgwnhDHkBTCf/fBQv4= +github.com/iotaledger/hive.go/logger v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:sxqWRdZ1OOxwkxVczuGcW034Mpt2vFh5ebJHO++ZYeI= +github.com/iotaledger/hive.go/runtime v0.0.0-20231010121205-23c1f3fcddd5 h1:2TxlF+6APu1ZeImwny5EAwfe/n/JVlq+1NpzBybJnCU= +github.com/iotaledger/hive.go/runtime v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:fXVyQ1MAwxe/EmjAnG8WcQqbzGk9EW/FsJ/n16H/f/w= +github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231010121205-23c1f3fcddd5 h1:SV8s8rZIHj2yhGRj8vX2AyA3MOKOWPk4rKiz0mJ/4mw= +github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231010121205-23c1f3fcddd5/go.mod h1:IJgaaxbgKCsNat18jlJJEAxCY2oVYR3F30B+M4vJ89I= +github.com/iotaledger/hive.go/stringify v0.0.0-20231010121205-23c1f3fcddd5 h1:zD60l2AnJmYLfjwhytAILQLZbkgmOPeg7IvFSY6Uv1E= +github.com/iotaledger/hive.go/stringify v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:FTo/UWzNYgnQ082GI9QVM9HFDERqf9rw9RivNpqrnTs= github.com/iotaledger/iota.go/v4 v4.0.0-20231005184534-62e6761a7b7c h1:3gWmDG+XNtTcT4FxcID6hijCUVs3kRGhAfhdv3FiWJs= github.com/iotaledger/iota.go/v4 v4.0.0-20231005184534-62e6761a7b7c/go.mod h1:l5yEhEf90+V0sv8kgWINTsM/O6W75bgEDHxWrIlC4AY= github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= diff --git a/tools/gendoc/go.mod b/tools/gendoc/go.mod index 8ec310193..965ecacb8 100644 --- a/tools/gendoc/go.mod +++ b/tools/gendoc/go.mod @@ -5,7 +5,7 @@ go 1.21 replace github.com/iotaledger/iota-core => ../../ require ( - github.com/iotaledger/hive.go/app v0.0.0-20231010083704-a2a91fb9e0ab + github.com/iotaledger/hive.go/app v0.0.0-20231010121205-23c1f3fcddd5 github.com/iotaledger/hive.go/apputils v0.0.0-20230829152614-7afc7a4d89b3 github.com/iotaledger/iota-core v0.0.0-00010101000000-000000000000 ) @@ -58,18 +58,18 @@ require ( github.com/huin/goupnp v1.3.0 // indirect github.com/iancoleman/orderedmap v0.3.0 // indirect github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7 // indirect - github.com/iotaledger/hive.go/ads v0.0.0-20231010083704-a2a91fb9e0ab // indirect - github.com/iotaledger/hive.go/constraints v0.0.0-20231010083704-a2a91fb9e0ab // indirect - github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231010083704-a2a91fb9e0ab // indirect - github.com/iotaledger/hive.go/crypto v0.0.0-20231010083704-a2a91fb9e0ab // indirect - github.com/iotaledger/hive.go/ds v0.0.0-20231010083704-a2a91fb9e0ab // indirect - github.com/iotaledger/hive.go/ierrors v0.0.0-20231010083704-a2a91fb9e0ab // indirect - github.com/iotaledger/hive.go/kvstore v0.0.0-20231010083704-a2a91fb9e0ab // indirect - github.com/iotaledger/hive.go/lo v0.0.0-20231010083704-a2a91fb9e0ab // indirect - github.com/iotaledger/hive.go/logger v0.0.0-20231010083704-a2a91fb9e0ab // indirect - github.com/iotaledger/hive.go/runtime v0.0.0-20231010083704-a2a91fb9e0ab // indirect - github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231010083704-a2a91fb9e0ab // indirect - github.com/iotaledger/hive.go/stringify v0.0.0-20231010083704-a2a91fb9e0ab // indirect + github.com/iotaledger/hive.go/ads v0.0.0-20231010121205-23c1f3fcddd5 // indirect + github.com/iotaledger/hive.go/constraints v0.0.0-20231010121205-23c1f3fcddd5 // indirect + github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231010121205-23c1f3fcddd5 // indirect + github.com/iotaledger/hive.go/crypto v0.0.0-20231010121205-23c1f3fcddd5 // indirect + github.com/iotaledger/hive.go/ds v0.0.0-20231010121205-23c1f3fcddd5 // indirect + github.com/iotaledger/hive.go/ierrors v0.0.0-20231010121205-23c1f3fcddd5 // indirect + github.com/iotaledger/hive.go/kvstore v0.0.0-20231010121205-23c1f3fcddd5 // indirect + github.com/iotaledger/hive.go/lo v0.0.0-20231010121205-23c1f3fcddd5 // indirect + github.com/iotaledger/hive.go/logger v0.0.0-20231010121205-23c1f3fcddd5 // indirect + github.com/iotaledger/hive.go/runtime v0.0.0-20231010121205-23c1f3fcddd5 // indirect + github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231010121205-23c1f3fcddd5 // indirect + github.com/iotaledger/hive.go/stringify v0.0.0-20231010121205-23c1f3fcddd5 // indirect github.com/iotaledger/inx-app v1.0.0-rc.3.0.20231005192108-08a985c2e217 // indirect github.com/iotaledger/inx/go v1.0.0-rc.2.0.20231005191759-16a3636128c4 // indirect github.com/iotaledger/iota.go/v4 v4.0.0-20231005184534-62e6761a7b7c // indirect diff --git a/tools/gendoc/go.sum b/tools/gendoc/go.sum index b5b81ca0c..5089d6d4b 100644 --- a/tools/gendoc/go.sum +++ b/tools/gendoc/go.sum @@ -279,34 +279,34 @@ github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJ github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7 h1:dTrD7X2PTNgli6EbS4tV9qu3QAm/kBU3XaYZV2xdzys= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7/go.mod h1:ZRdPu684P0fQ1z8sXz4dj9H5LWHhz4a9oCtvjunkSrw= -github.com/iotaledger/hive.go/ads v0.0.0-20231010083704-a2a91fb9e0ab h1:3U1ADSPHU9GahpWujnkwOlGxA+NMl9l0j3ch4ISH1S0= -github.com/iotaledger/hive.go/ads v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:IAWZ/5It5P8B41mWyJXJVcG0vuikVRaTFKQnr2D2q+c= -github.com/iotaledger/hive.go/app v0.0.0-20231010083704-a2a91fb9e0ab h1:ePgcMl1XOXoF4kNfwMGn63GXZSihv+Z2t1GWLKqkGOw= -github.com/iotaledger/hive.go/app v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:eiZgbcwTDZ7d9hEait2EAwAhixWhceW4MXmuVk2EcEw= +github.com/iotaledger/hive.go/ads v0.0.0-20231010121205-23c1f3fcddd5 h1:lbrYweNe+DB/kh7OYlogJMQfEDRpriyIgA+MmHmb01k= +github.com/iotaledger/hive.go/ads v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:IAWZ/5It5P8B41mWyJXJVcG0vuikVRaTFKQnr2D2q+c= +github.com/iotaledger/hive.go/app v0.0.0-20231010121205-23c1f3fcddd5 h1:P2BE6RylO7mlEtJSbNyuRz6OcJTNs0MZ0qFIN5lI3Kg= +github.com/iotaledger/hive.go/app v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:q2H/9TE6hvg6TKZI0YoxtbCdrf8O1sYBVjNT2Y3yAIU= github.com/iotaledger/hive.go/apputils v0.0.0-20230829152614-7afc7a4d89b3 h1:4aVJTc0KS77uEw0Tny4r0n1ORwcbAQDECaCclgf/6lE= github.com/iotaledger/hive.go/apputils v0.0.0-20230829152614-7afc7a4d89b3/go.mod h1:TZeAqieDu+xDOZp2e9+S+8pZp1PrfgcwLUnxmd8IgLU= -github.com/iotaledger/hive.go/constraints v0.0.0-20231010083704-a2a91fb9e0ab h1:u7wN87wrmlPbUpNZhHfNKN6HLLQdY/zICIiJgVCSBvM= -github.com/iotaledger/hive.go/constraints v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:dOBOM2s4se3HcWefPe8sQLUalGXJ8yVXw58oK8jke3s= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231010083704-a2a91fb9e0ab h1:u4h5GdvR1MTNZHYkLx8USCadbkMkISZyYXudH5qEhZQ= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231010083704-a2a91fb9e0ab/go.mod h1:jn3TNmiNRIiQm/rS4VD+7wFHI2+UXABHvCA3PbQxBqI= -github.com/iotaledger/hive.go/crypto v0.0.0-20231010083704-a2a91fb9e0ab h1:0wGm1JPCBNK7h9WYtFZhSxmbhPmiBEzeeonUfJmVASE= -github.com/iotaledger/hive.go/crypto v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:jP68na941d9uq7RtnA8aQ/FtIGRGz/51cU4uXrInQFU= -github.com/iotaledger/hive.go/ds v0.0.0-20231010083704-a2a91fb9e0ab h1:Wlv4bCfT6IZ3LD3jzHCgT00MdL0LR7kzhlW3zQohKKA= -github.com/iotaledger/hive.go/ds v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:ZrqsjIJS2QCgGp7Ki+l4hWJQgzfBObUCemb5Upwlx18= -github.com/iotaledger/hive.go/ierrors v0.0.0-20231010083704-a2a91fb9e0ab h1:RMKlQlTpWSSfauU3KgNyv/8WGzTTx3G4KzFuLf1EhrM= -github.com/iotaledger/hive.go/ierrors v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:HcE8B5lP96enc/OALTb2/rIIi+yOLouRoHOKRclKmC8= -github.com/iotaledger/hive.go/kvstore v0.0.0-20231010083704-a2a91fb9e0ab h1:50bOe5+XlIj0pPqcMNkOl+nf4BEITIhWuSr1j3fieLI= -github.com/iotaledger/hive.go/kvstore v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:DeP4JF4N995LteD0+/o7NsW1bO5IXURIJ27A69Ca5+Y= -github.com/iotaledger/hive.go/lo v0.0.0-20231010083704-a2a91fb9e0ab h1:o9JT//S67PRsrEir1GSdlNz2hLYfG+6SDayxmJm+jKQ= -github.com/iotaledger/hive.go/lo v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:4oKCdMEhHMLCudBz79kuvJmgSY/DhfVePNIyJhew/80= -github.com/iotaledger/hive.go/logger v0.0.0-20231010083704-a2a91fb9e0ab h1:AWU4PRv5No/ghKH02JY3/N9Xpv2he+byVLRd7DPHJGg= -github.com/iotaledger/hive.go/logger v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:sxqWRdZ1OOxwkxVczuGcW034Mpt2vFh5ebJHO++ZYeI= -github.com/iotaledger/hive.go/runtime v0.0.0-20231010083704-a2a91fb9e0ab h1:ZE7MENkq9l8B+uIt1ulFfflDPnYX4Xxk6UYbzAPr8/U= -github.com/iotaledger/hive.go/runtime v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:fXVyQ1MAwxe/EmjAnG8WcQqbzGk9EW/FsJ/n16H/f/w= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231010083704-a2a91fb9e0ab h1:ebO3VhgcS2Dd8zBe+8Lizd8RoV00TUAm06tY0HGZmIE= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231010083704-a2a91fb9e0ab/go.mod h1:IJgaaxbgKCsNat18jlJJEAxCY2oVYR3F30B+M4vJ89I= -github.com/iotaledger/hive.go/stringify v0.0.0-20231010083704-a2a91fb9e0ab h1:nDsUCc2/EPFspZcKk3T4HklhUeI+B5ed3W+HoTciYzA= -github.com/iotaledger/hive.go/stringify v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:FTo/UWzNYgnQ082GI9QVM9HFDERqf9rw9RivNpqrnTs= +github.com/iotaledger/hive.go/constraints v0.0.0-20231010121205-23c1f3fcddd5 h1:AsgWEa8dpgF0JX3pc+nwaoQLlc5rKEoWAgrYdN1AIRE= +github.com/iotaledger/hive.go/constraints v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:dOBOM2s4se3HcWefPe8sQLUalGXJ8yVXw58oK8jke3s= +github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231010121205-23c1f3fcddd5 h1:T/vnm1B2SFYWz7QiQsQq0AuoJCXKwW3hBCITudvh//c= +github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231010121205-23c1f3fcddd5/go.mod h1:jn3TNmiNRIiQm/rS4VD+7wFHI2+UXABHvCA3PbQxBqI= +github.com/iotaledger/hive.go/crypto v0.0.0-20231010121205-23c1f3fcddd5 h1:8TgnDcl6gIqXqgTnx4wA3LZt8vwAsVfekcKlPK5WRNY= +github.com/iotaledger/hive.go/crypto v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:jP68na941d9uq7RtnA8aQ/FtIGRGz/51cU4uXrInQFU= +github.com/iotaledger/hive.go/ds v0.0.0-20231010121205-23c1f3fcddd5 h1:xyVUPe/tZtS3nkg8fU1GB1ZyUo7D32YfrY0XNA8GZhM= +github.com/iotaledger/hive.go/ds v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:ZrqsjIJS2QCgGp7Ki+l4hWJQgzfBObUCemb5Upwlx18= +github.com/iotaledger/hive.go/ierrors v0.0.0-20231010121205-23c1f3fcddd5 h1:Q8giZLqmIJRlcUS5x1TxVlS5X+iHgZ8zOq4GsK3YWnQ= +github.com/iotaledger/hive.go/ierrors v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:HcE8B5lP96enc/OALTb2/rIIi+yOLouRoHOKRclKmC8= +github.com/iotaledger/hive.go/kvstore v0.0.0-20231010121205-23c1f3fcddd5 h1:nI6LJ32saPs1c3HwA7V3T2jo3bh4+UuhBg1EsBK9N7k= +github.com/iotaledger/hive.go/kvstore v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:DeP4JF4N995LteD0+/o7NsW1bO5IXURIJ27A69Ca5+Y= +github.com/iotaledger/hive.go/lo v0.0.0-20231010121205-23c1f3fcddd5 h1:XVKwwjBJGOUuNzormIYJMWgqyscgusR5WtxA3rxql5g= +github.com/iotaledger/hive.go/lo v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:4oKCdMEhHMLCudBz79kuvJmgSY/DhfVePNIyJhew/80= +github.com/iotaledger/hive.go/logger v0.0.0-20231010121205-23c1f3fcddd5 h1:hp+Bstpyxgeci0eDnEp0CZDbavgwnhDHkBTCf/fBQv4= +github.com/iotaledger/hive.go/logger v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:sxqWRdZ1OOxwkxVczuGcW034Mpt2vFh5ebJHO++ZYeI= +github.com/iotaledger/hive.go/runtime v0.0.0-20231010121205-23c1f3fcddd5 h1:2TxlF+6APu1ZeImwny5EAwfe/n/JVlq+1NpzBybJnCU= +github.com/iotaledger/hive.go/runtime v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:fXVyQ1MAwxe/EmjAnG8WcQqbzGk9EW/FsJ/n16H/f/w= +github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231010121205-23c1f3fcddd5 h1:SV8s8rZIHj2yhGRj8vX2AyA3MOKOWPk4rKiz0mJ/4mw= +github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231010121205-23c1f3fcddd5/go.mod h1:IJgaaxbgKCsNat18jlJJEAxCY2oVYR3F30B+M4vJ89I= +github.com/iotaledger/hive.go/stringify v0.0.0-20231010121205-23c1f3fcddd5 h1:zD60l2AnJmYLfjwhytAILQLZbkgmOPeg7IvFSY6Uv1E= +github.com/iotaledger/hive.go/stringify v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:FTo/UWzNYgnQ082GI9QVM9HFDERqf9rw9RivNpqrnTs= github.com/iotaledger/inx-app v1.0.0-rc.3.0.20231005192108-08a985c2e217 h1:DdrsW+J04ne2j6bjU1yVu+4C4CjpjGFYDEVMtmg0zyA= github.com/iotaledger/inx-app v1.0.0-rc.3.0.20231005192108-08a985c2e217/go.mod h1:OWZGwG4q2Ypd/D6LJicgdPXtw9yYkhaZEvJFhkIm2Ck= github.com/iotaledger/inx/go v1.0.0-rc.2.0.20231005191759-16a3636128c4 h1:uI+sZeZnGzAkM34JDbZBfyRIXrkkd1L0w8qVJ5rGy3E= diff --git a/tools/genesis-snapshot/go.mod b/tools/genesis-snapshot/go.mod index d3f6d0384..573a5889e 100644 --- a/tools/genesis-snapshot/go.mod +++ b/tools/genesis-snapshot/go.mod @@ -5,10 +5,10 @@ go 1.21 replace github.com/iotaledger/iota-core => ../../ require ( - github.com/iotaledger/hive.go/crypto v0.0.0-20231010083704-a2a91fb9e0ab - github.com/iotaledger/hive.go/ierrors v0.0.0-20231010083704-a2a91fb9e0ab - github.com/iotaledger/hive.go/lo v0.0.0-20231010083704-a2a91fb9e0ab - github.com/iotaledger/hive.go/runtime v0.0.0-20231010083704-a2a91fb9e0ab + github.com/iotaledger/hive.go/crypto v0.0.0-20231010121205-23c1f3fcddd5 + github.com/iotaledger/hive.go/ierrors v0.0.0-20231010121205-23c1f3fcddd5 + github.com/iotaledger/hive.go/lo v0.0.0-20231010121205-23c1f3fcddd5 + github.com/iotaledger/hive.go/runtime v0.0.0-20231010121205-23c1f3fcddd5 github.com/iotaledger/iota-core v0.0.0-00010101000000-000000000000 github.com/iotaledger/iota.go/v4 v4.0.0-20231005184534-62e6761a7b7c github.com/mr-tron/base58 v1.2.0 @@ -27,13 +27,13 @@ require ( github.com/holiman/uint256 v1.2.3 // indirect github.com/iancoleman/orderedmap v0.3.0 // indirect github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7 // indirect - github.com/iotaledger/hive.go/ads v0.0.0-20231010083704-a2a91fb9e0ab // indirect - github.com/iotaledger/hive.go/constraints v0.0.0-20231010083704-a2a91fb9e0ab // indirect - github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231010083704-a2a91fb9e0ab // indirect - github.com/iotaledger/hive.go/ds v0.0.0-20231010083704-a2a91fb9e0ab // indirect - github.com/iotaledger/hive.go/kvstore v0.0.0-20231010083704-a2a91fb9e0ab // indirect - github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231010083704-a2a91fb9e0ab // indirect - github.com/iotaledger/hive.go/stringify v0.0.0-20231010083704-a2a91fb9e0ab // indirect + github.com/iotaledger/hive.go/ads v0.0.0-20231010121205-23c1f3fcddd5 // indirect + github.com/iotaledger/hive.go/constraints v0.0.0-20231010121205-23c1f3fcddd5 // indirect + github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231010121205-23c1f3fcddd5 // indirect + github.com/iotaledger/hive.go/ds v0.0.0-20231010121205-23c1f3fcddd5 // indirect + github.com/iotaledger/hive.go/kvstore v0.0.0-20231010121205-23c1f3fcddd5 // indirect + github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231010121205-23c1f3fcddd5 // indirect + github.com/iotaledger/hive.go/stringify v0.0.0-20231010121205-23c1f3fcddd5 // indirect github.com/ipfs/go-cid v0.4.1 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/kr/text v0.2.0 // indirect diff --git a/tools/genesis-snapshot/go.sum b/tools/genesis-snapshot/go.sum index eb342e3f0..bce198553 100644 --- a/tools/genesis-snapshot/go.sum +++ b/tools/genesis-snapshot/go.sum @@ -28,28 +28,28 @@ github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJ github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7 h1:dTrD7X2PTNgli6EbS4tV9qu3QAm/kBU3XaYZV2xdzys= github.com/iotaledger/grocksdb v1.7.5-0.20230220105546-5162e18885c7/go.mod h1:ZRdPu684P0fQ1z8sXz4dj9H5LWHhz4a9oCtvjunkSrw= -github.com/iotaledger/hive.go/ads v0.0.0-20231010083704-a2a91fb9e0ab h1:3U1ADSPHU9GahpWujnkwOlGxA+NMl9l0j3ch4ISH1S0= -github.com/iotaledger/hive.go/ads v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:IAWZ/5It5P8B41mWyJXJVcG0vuikVRaTFKQnr2D2q+c= -github.com/iotaledger/hive.go/constraints v0.0.0-20231010083704-a2a91fb9e0ab h1:u7wN87wrmlPbUpNZhHfNKN6HLLQdY/zICIiJgVCSBvM= -github.com/iotaledger/hive.go/constraints v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:dOBOM2s4se3HcWefPe8sQLUalGXJ8yVXw58oK8jke3s= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231010083704-a2a91fb9e0ab h1:u4h5GdvR1MTNZHYkLx8USCadbkMkISZyYXudH5qEhZQ= -github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231010083704-a2a91fb9e0ab/go.mod h1:jn3TNmiNRIiQm/rS4VD+7wFHI2+UXABHvCA3PbQxBqI= -github.com/iotaledger/hive.go/crypto v0.0.0-20231010083704-a2a91fb9e0ab h1:0wGm1JPCBNK7h9WYtFZhSxmbhPmiBEzeeonUfJmVASE= -github.com/iotaledger/hive.go/crypto v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:jP68na941d9uq7RtnA8aQ/FtIGRGz/51cU4uXrInQFU= -github.com/iotaledger/hive.go/ds v0.0.0-20231010083704-a2a91fb9e0ab h1:Wlv4bCfT6IZ3LD3jzHCgT00MdL0LR7kzhlW3zQohKKA= -github.com/iotaledger/hive.go/ds v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:ZrqsjIJS2QCgGp7Ki+l4hWJQgzfBObUCemb5Upwlx18= -github.com/iotaledger/hive.go/ierrors v0.0.0-20231010083704-a2a91fb9e0ab h1:RMKlQlTpWSSfauU3KgNyv/8WGzTTx3G4KzFuLf1EhrM= -github.com/iotaledger/hive.go/ierrors v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:HcE8B5lP96enc/OALTb2/rIIi+yOLouRoHOKRclKmC8= -github.com/iotaledger/hive.go/kvstore v0.0.0-20231010083704-a2a91fb9e0ab h1:50bOe5+XlIj0pPqcMNkOl+nf4BEITIhWuSr1j3fieLI= -github.com/iotaledger/hive.go/kvstore v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:DeP4JF4N995LteD0+/o7NsW1bO5IXURIJ27A69Ca5+Y= -github.com/iotaledger/hive.go/lo v0.0.0-20231010083704-a2a91fb9e0ab h1:o9JT//S67PRsrEir1GSdlNz2hLYfG+6SDayxmJm+jKQ= -github.com/iotaledger/hive.go/lo v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:4oKCdMEhHMLCudBz79kuvJmgSY/DhfVePNIyJhew/80= -github.com/iotaledger/hive.go/runtime v0.0.0-20231010083704-a2a91fb9e0ab h1:ZE7MENkq9l8B+uIt1ulFfflDPnYX4Xxk6UYbzAPr8/U= -github.com/iotaledger/hive.go/runtime v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:fXVyQ1MAwxe/EmjAnG8WcQqbzGk9EW/FsJ/n16H/f/w= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231010083704-a2a91fb9e0ab h1:ebO3VhgcS2Dd8zBe+8Lizd8RoV00TUAm06tY0HGZmIE= -github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231010083704-a2a91fb9e0ab/go.mod h1:IJgaaxbgKCsNat18jlJJEAxCY2oVYR3F30B+M4vJ89I= -github.com/iotaledger/hive.go/stringify v0.0.0-20231010083704-a2a91fb9e0ab h1:nDsUCc2/EPFspZcKk3T4HklhUeI+B5ed3W+HoTciYzA= -github.com/iotaledger/hive.go/stringify v0.0.0-20231010083704-a2a91fb9e0ab/go.mod h1:FTo/UWzNYgnQ082GI9QVM9HFDERqf9rw9RivNpqrnTs= +github.com/iotaledger/hive.go/ads v0.0.0-20231010121205-23c1f3fcddd5 h1:lbrYweNe+DB/kh7OYlogJMQfEDRpriyIgA+MmHmb01k= +github.com/iotaledger/hive.go/ads v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:IAWZ/5It5P8B41mWyJXJVcG0vuikVRaTFKQnr2D2q+c= +github.com/iotaledger/hive.go/constraints v0.0.0-20231010121205-23c1f3fcddd5 h1:AsgWEa8dpgF0JX3pc+nwaoQLlc5rKEoWAgrYdN1AIRE= +github.com/iotaledger/hive.go/constraints v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:dOBOM2s4se3HcWefPe8sQLUalGXJ8yVXw58oK8jke3s= +github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231010121205-23c1f3fcddd5 h1:T/vnm1B2SFYWz7QiQsQq0AuoJCXKwW3hBCITudvh//c= +github.com/iotaledger/hive.go/core v1.0.0-rc.3.0.20231010121205-23c1f3fcddd5/go.mod h1:jn3TNmiNRIiQm/rS4VD+7wFHI2+UXABHvCA3PbQxBqI= +github.com/iotaledger/hive.go/crypto v0.0.0-20231010121205-23c1f3fcddd5 h1:8TgnDcl6gIqXqgTnx4wA3LZt8vwAsVfekcKlPK5WRNY= +github.com/iotaledger/hive.go/crypto v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:jP68na941d9uq7RtnA8aQ/FtIGRGz/51cU4uXrInQFU= +github.com/iotaledger/hive.go/ds v0.0.0-20231010121205-23c1f3fcddd5 h1:xyVUPe/tZtS3nkg8fU1GB1ZyUo7D32YfrY0XNA8GZhM= +github.com/iotaledger/hive.go/ds v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:ZrqsjIJS2QCgGp7Ki+l4hWJQgzfBObUCemb5Upwlx18= +github.com/iotaledger/hive.go/ierrors v0.0.0-20231010121205-23c1f3fcddd5 h1:Q8giZLqmIJRlcUS5x1TxVlS5X+iHgZ8zOq4GsK3YWnQ= +github.com/iotaledger/hive.go/ierrors v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:HcE8B5lP96enc/OALTb2/rIIi+yOLouRoHOKRclKmC8= +github.com/iotaledger/hive.go/kvstore v0.0.0-20231010121205-23c1f3fcddd5 h1:nI6LJ32saPs1c3HwA7V3T2jo3bh4+UuhBg1EsBK9N7k= +github.com/iotaledger/hive.go/kvstore v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:DeP4JF4N995LteD0+/o7NsW1bO5IXURIJ27A69Ca5+Y= +github.com/iotaledger/hive.go/lo v0.0.0-20231010121205-23c1f3fcddd5 h1:XVKwwjBJGOUuNzormIYJMWgqyscgusR5WtxA3rxql5g= +github.com/iotaledger/hive.go/lo v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:4oKCdMEhHMLCudBz79kuvJmgSY/DhfVePNIyJhew/80= +github.com/iotaledger/hive.go/runtime v0.0.0-20231010121205-23c1f3fcddd5 h1:2TxlF+6APu1ZeImwny5EAwfe/n/JVlq+1NpzBybJnCU= +github.com/iotaledger/hive.go/runtime v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:fXVyQ1MAwxe/EmjAnG8WcQqbzGk9EW/FsJ/n16H/f/w= +github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231010121205-23c1f3fcddd5 h1:SV8s8rZIHj2yhGRj8vX2AyA3MOKOWPk4rKiz0mJ/4mw= +github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20231010121205-23c1f3fcddd5/go.mod h1:IJgaaxbgKCsNat18jlJJEAxCY2oVYR3F30B+M4vJ89I= +github.com/iotaledger/hive.go/stringify v0.0.0-20231010121205-23c1f3fcddd5 h1:zD60l2AnJmYLfjwhytAILQLZbkgmOPeg7IvFSY6Uv1E= +github.com/iotaledger/hive.go/stringify v0.0.0-20231010121205-23c1f3fcddd5/go.mod h1:FTo/UWzNYgnQ082GI9QVM9HFDERqf9rw9RivNpqrnTs= github.com/iotaledger/iota.go/v4 v4.0.0-20231005184534-62e6761a7b7c h1:3gWmDG+XNtTcT4FxcID6hijCUVs3kRGhAfhdv3FiWJs= github.com/iotaledger/iota.go/v4 v4.0.0-20231005184534-62e6761a7b7c/go.mod h1:l5yEhEf90+V0sv8kgWINTsM/O6W75bgEDHxWrIlC4AY= github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= diff --git a/tools/genesis-snapshot/presets/presets.go b/tools/genesis-snapshot/presets/presets.go index ab7b68bb2..d6627393d 100644 --- a/tools/genesis-snapshot/presets/presets.go +++ b/tools/genesis-snapshot/presets/presets.go @@ -22,7 +22,7 @@ var Base = []options.Option[snapshotcreator.Options]{ iotago.NewV3ProtocolParameters( iotago.WithNetworkOptions("default", "rms"), iotago.WithSupplyOptions(10_000_000_000, 100, 1, 10, 100, 100, 100), - iotago.WithTimeProviderOptions(time.Now().Unix(), 10, 13), + iotago.WithTimeProviderOptions(1696841745, 10, 13), iotago.WithLivenessOptions(30, 30, 7, 14, 30), // increase/decrease threshold = fraction * slotDurationInSeconds * schedulerRate iotago.WithCongestionControlOptions(500, 500, 500, 800000, 500000, 100000, 1000, 100),