diff --git a/common/types.go b/common/types.go index 31b10c0425..617e720331 100644 --- a/common/types.go +++ b/common/types.go @@ -406,8 +406,8 @@ func (loc Location) DomIndex(nodeLocation Location) int { } // SubIndex returns the index of the subordinate chain for a given location -func (loc Location) SubIndex(nodeLocation Location) int { - switch nodeLocation.Context() { +func (loc Location) SubIndex(nodeCtx int) int { + switch nodeCtx { case PRIME_CTX: return loc.Region() case REGION_CTX: diff --git a/core/core.go b/core/core.go index 4327f3c50b..f6d7da0e29 100644 --- a/core/core.go +++ b/core/core.go @@ -114,7 +114,6 @@ func NewCore(db ethdb.Database, config *Config, isLocalBlock func(block *types.W // the number of blocks which were successfully consumed (either appended, or // cached), and an error. func (c *Core) InsertChain(blocks types.WorkObjects) (int, error) { - nodeLocation := c.NodeLocation() nodeCtx := c.NodeCtx() for idx, block := range blocks { // Only attempt to append a block, if it is not coincident with our dominant @@ -180,8 +179,8 @@ func (c *Core) InsertChain(blocks types.WorkObjects) (int, error) { } if err.Error() == ErrSubNotSyncedToDom.Error() || err.Error() == ErrPendingEtxNotFound.Error() { - if nodeCtx != common.ZONE_CTX && c.sl.subInterface[block.Location().SubIndex(nodeLocation)] != nil { - c.sl.subInterface[block.Location().SubIndex(nodeLocation)].DownloadBlocksInManifest(block.Hash(), block.Manifest(), block.ParentEntropy(nodeCtx)) + if nodeCtx != common.ZONE_CTX && c.sl.subInterface[block.Location().SubIndex(c.NodeCtx())] != nil { + c.sl.subInterface[block.Location().SubIndex(c.NodeCtx())].DownloadBlocksInManifest(block.Hash(), block.Manifest(), block.ParentEntropy(nodeCtx)) } } return idx, ErrPendingBlock @@ -640,8 +639,8 @@ func (c *Core) DownloadBlocksInManifest(blockHash common.Hash, manifest types.Bl block := c.GetBlockOrCandidateByHash(blockHash) if block != nil { // If a prime block comes in - if c.sl.subInterface[block.Location().SubIndex(c.NodeLocation())] != nil { - c.sl.subInterface[block.Location().SubIndex(c.NodeLocation())].DownloadBlocksInManifest(block.Hash(), block.Manifest(), block.ParentEntropy(c.NodeCtx())) + if c.sl.subInterface[block.Location().SubIndex(c.NodeCtx())] != nil { + c.sl.subInterface[block.Location().SubIndex(c.NodeCtx())].DownloadBlocksInManifest(block.Hash(), block.Manifest(), block.ParentEntropy(c.NodeCtx())) } } } diff --git a/core/headerchain.go b/core/headerchain.go index 79c37f35e3..275ded2a57 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -174,13 +174,6 @@ func (hc *HeaderChain) CollectSubRollup(b *types.WorkObject) (types.Transactions return nil, ErrPendingEtxNotFound } subRollup = append(subRollup, pendingEtxs.Etxs...) - - } - } - // Rolluphash is specifically for zone rollup, which can only be validated by region - if nodeCtx == common.REGION_CTX { - if subRollupHash := types.DeriveSha(subRollup, trie.NewStackTrie(nil)); subRollupHash != b.EtxRollupHash() { - return nil, errors.New("sub rollup does not match sub rollup hash") } } } diff --git a/core/slice.go b/core/slice.go index 32eec3e50f..eda899be43 100644 --- a/core/slice.go +++ b/core/slice.go @@ -243,11 +243,19 @@ func (sl *Slice) Append(header *types.WorkObject, domPendingHeader *types.WorkOb if err != nil { return nil, false, false, err } - } else if nodeCtx == common.ZONE_CTX && order < nodeCtx { + } + + if order < nodeCtx { // Store the inbound etxs for all dom blocks and use // it in the future if dom switch happens // This should be pruned at the re-org tolerance depth rawdb.WriteInboundEtxs(sl.sliceDb, block.Hash(), newInboundEtxs) + + // After writing the inbound etxs, we need to just send the inbounds for + // the given block location down in the region + if nodeCtx == common.REGION_CTX { + newInboundEtxs = newInboundEtxs.FilterToSub(block.Location(), nodeCtx) + } } // If this was a coincident block, our dom will be passing us a set of newly @@ -289,8 +297,8 @@ func (sl *Slice) Append(header *types.WorkObject, domPendingHeader *types.WorkOb // Call my sub to append the block, and collect the rolled up ETXs from that sub if nodeCtx != common.ZONE_CTX { // How to get the sub pending etxs if not running the full node?. - if sl.subInterface[location.SubIndex(sl.NodeLocation())] != nil { - subPendingEtxs, subReorg, setHead, err = sl.subInterface[location.SubIndex(sl.NodeLocation())].Append(header, block.Manifest(), pendingHeaderWithTermini.WorkObject(), domTerminus, true, newInboundEtxs) + if sl.subInterface[location.SubIndex(sl.NodeCtx())] != nil { + subPendingEtxs, subReorg, setHead, err = sl.subInterface[location.SubIndex(sl.NodeCtx())].Append(header, block.Manifest(), pendingHeaderWithTermini.WorkObject(), domTerminus, true, newInboundEtxs) if err != nil { return nil, false, false, err } @@ -302,12 +310,25 @@ func (sl *Slice) Append(header *types.WorkObject, domPendingHeader *types.WorkOb sl.AddPendingEtxs(pEtxs) // Only region has the rollup hashes for pendingEtxs if nodeCtx == common.REGION_CTX { + crossPrimeRollup := types.Transactions{} subRollup, err := sl.hc.CollectSubRollup(block) if err != nil { return nil, false, false, err } + for _, etx := range subRollup { + to := etx.To().Location() + if to.Region() != sl.NodeLocation().Region() || etx.IsTxAConversionTx(sl.NodeLocation()) { + crossPrimeRollup = append(crossPrimeRollup, etx) + } + } + // Rolluphash is specifically for zone rollup, which can only be validated by region + if nodeCtx == common.REGION_CTX { + if etxRollupHash := types.DeriveSha(crossPrimeRollup, trie.NewStackTrie(nil)); etxRollupHash != block.EtxRollupHash() { + return nil, false, false, errors.New("sub rollup does not match sub rollup hash") + } + } // We also need to store the pendingEtxRollup to the dom - pEtxRollup := types.PendingEtxsRollup{header.ConvertToPEtxView(), subRollup} + pEtxRollup := types.PendingEtxsRollup{header.ConvertToPEtxView(), crossPrimeRollup} sl.AddPendingEtxsRollup(pEtxRollup) } time6_3 = common.PrettyDuration(time.Since(start)) @@ -525,9 +546,9 @@ func (sl *Slice) UpdateDom(oldTerminus common.Hash, pendingHeader types.PendingH nodeCtx := sl.NodeLocation().Context() sl.phCacheMu.Lock() defer sl.phCacheMu.Unlock() - newDomTermini := sl.hc.GetTerminiByHash(pendingHeader.Termini().DomTerminiAtIndex(location.SubIndex(nodeLocation))) + newDomTermini := sl.hc.GetTerminiByHash(pendingHeader.Termini().DomTerminiAtIndex(location.SubIndex(nodeCtx))) if newDomTermini == nil { - sl.logger.WithField("hash", pendingHeader.Termini().DomTerminiAtIndex(location.SubIndex(nodeLocation))).Warn("New Dom Termini doesn't exists in the database") + sl.logger.WithField("hash", pendingHeader.Termini().DomTerminiAtIndex(location.SubIndex(nodeCtx))).Warn("New Dom Termini doesn't exists in the database") return } newDomTerminus := newDomTermini.DomTerminus(nodeLocation) @@ -726,7 +747,6 @@ func (sl *Slice) generateSlicePendingHeader(block *types.WorkObject, newTermini // CollectNewlyConfirmedEtxs collects all newly confirmed ETXs since the last coincident with the given location func (sl *Slice) CollectNewlyConfirmedEtxs(block *types.WorkObject) (types.Transactions, types.Transactions, error) { nodeCtx := sl.NodeCtx() - nodeLocation := sl.NodeLocation() blockLocation := block.Location() // Collect rollup of ETXs from the subordinate node's manifest subRollup := types.Transactions{} @@ -747,11 +767,9 @@ func (sl *Slice) CollectNewlyConfirmedEtxs(block *types.WorkObject) (types.Trans sl.hc.subRollupCache.Add(block.Hash(), subRollup) } } - // Filter for ETXs destined to this slice - newInboundEtxs := subRollup.FilterToSlice(blockLocation) - // Filter this list to exclude any ETX for which we are not the crossing - // context node. This check prevents cross-Region ETXs from going through Prime. - newlyConfirmedEtxs := newInboundEtxs.FilterConfirmationCtx(nodeCtx, nodeLocation) + // Filter for ETXs destined to this sub + newInboundEtxs := subRollup.FilterToSub(blockLocation, sl.NodeCtx()) + newlyConfirmedEtxs := newInboundEtxs for { ancHash := block.ParentHash(nodeCtx) ancNum := block.NumberU64(nodeCtx) - 1 @@ -780,12 +798,11 @@ func (sl *Slice) CollectNewlyConfirmedEtxs(block *types.WorkObject) (types.Trans } // Terminate the search when we find a block produced by the same sub - if parent.Location().Equal(blockLocation) && order == nodeCtx { + if parent.Location().SubIndex(sl.NodeCtx()) == blockLocation.SubIndex(sl.NodeCtx()) && order == nodeCtx { break } subRollup := types.Transactions{} - if nodeCtx < common.ZONE_CTX { rollup, exists := sl.hc.subRollupCache.Get(parent.Hash()) if exists && rollup != nil { @@ -802,9 +819,16 @@ func (sl *Slice) CollectNewlyConfirmedEtxs(block *types.WorkObject) (types.Trans sl.hc.subRollupCache.Add(parent.Hash(), subRollup) } } - ancInboundEtxs := subRollup.FilterToSlice(blockLocation) - newlyConfirmedAncEtxs := ancInboundEtxs.FilterConfirmationCtx(nodeCtx, nodeLocation) - newlyConfirmedEtxs = append(newlyConfirmedEtxs, newlyConfirmedAncEtxs...) + // change for the rolldown feature is that in the region when we go + // back, if we find a prime block, we have to take the transactions that + // are going towards the given zone + if nodeCtx == common.REGION_CTX && order < nodeCtx && !blockLocation.Equal(parent.Location()) { + inboundEtxs := rawdb.ReadInboundEtxs(sl.sliceDb, parent.Hash()) + subRollup = append(subRollup, inboundEtxs...) + } + + ancInboundEtxs := subRollup.FilterToSub(blockLocation, sl.NodeCtx()) + newlyConfirmedEtxs = append(newlyConfirmedEtxs, ancInboundEtxs...) block = parent } return newlyConfirmedEtxs, subRollup, nil @@ -830,7 +854,7 @@ func (sl *Slice) pcrc(batch ethdb.Batch, header *types.WorkObject, domTerminus c newTermini := types.CopyTermini(*termini) // Set the subtermini if nodeCtx != common.ZONE_CTX { - newTermini.SetSubTerminiAtIndex(header.Hash(), location.SubIndex(nodeLocation)) + newTermini.SetSubTerminiAtIndex(header.Hash(), location.SubIndex(nodeCtx)) } // Set the terminus @@ -860,7 +884,7 @@ func (sl *Slice) pcrc(batch ethdb.Batch, header *types.WorkObject, domTerminus c return common.Hash{}, newTermini, nil } - return termini.SubTerminiAtIndex(location.SubIndex(nodeLocation)), newTermini, nil + return termini.SubTerminiAtIndex(location.SubIndex(nodeCtx)), newTermini, nil } // POEM compares externS to the currentHead S and returns true if externS is greater @@ -895,7 +919,7 @@ func (sl *Slice) GetManifest(blockHash common.Hash) (types.BlockManifest, error) // GetSubManifest gets the block manifest from the subordinate node which // produced this block func (sl *Slice) GetSubManifest(slice common.Location, blockHash common.Hash) (types.BlockManifest, error) { - subIdx := slice.SubIndex(sl.NodeLocation()) + subIdx := slice.SubIndex(sl.NodeCtx()) if sl.subInterface[subIdx] == nil { return nil, errors.New("missing requested subordinate node") } @@ -919,8 +943,8 @@ func (sl *Slice) GetPEtxRollupAfterRetryThreshold(blockHash common.Hash, hash co func (sl *Slice) GetPendingEtxsRollupFromSub(hash common.Hash, location common.Location) (types.PendingEtxsRollup, error) { nodeCtx := sl.NodeLocation().Context() if nodeCtx == common.PRIME_CTX { - if sl.subInterface[location.SubIndex(sl.NodeLocation())] != nil { - pEtxRollup, err := sl.subInterface[location.SubIndex(sl.NodeLocation())].GetPendingEtxsRollupFromSub(hash, location) + if sl.subInterface[location.SubIndex(sl.NodeCtx())] != nil { + pEtxRollup, err := sl.subInterface[location.SubIndex(sl.NodeCtx())].GetPendingEtxsRollupFromSub(hash, location) if err != nil { return types.PendingEtxsRollup{}, err } else { @@ -953,8 +977,8 @@ func (sl *Slice) GetPEtxAfterRetryThreshold(blockHash common.Hash, hash common.H func (sl *Slice) GetPendingEtxsFromSub(hash common.Hash, location common.Location) (types.PendingEtxs, error) { nodeCtx := sl.NodeLocation().Context() if nodeCtx != common.ZONE_CTX { - if sl.subInterface[location.SubIndex(sl.NodeLocation())] != nil { - pEtx, err := sl.subInterface[location.SubIndex(sl.NodeLocation())].GetPendingEtxsFromSub(hash, location) + if sl.subInterface[location.SubIndex(sl.NodeCtx())] != nil { + pEtx, err := sl.subInterface[location.SubIndex(sl.NodeCtx())].GetPendingEtxsFromSub(hash, location) if err != nil { return types.PendingEtxs{}, err } else { diff --git a/core/types/transaction.go b/core/types/transaction.go index ee6efb119d..491d26a852 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -28,7 +28,6 @@ import ( "github.com/btcsuite/btcd/btcec/v2/schnorr" "github.com/dominant-strategies/go-quai/common" "github.com/dominant-strategies/go-quai/common/math" - "github.com/dominant-strategies/go-quai/params" "google.golang.org/protobuf/proto" "github.com/dominant-strategies/go-quai/crypto" @@ -761,22 +760,6 @@ func (tx *Transaction) FromChain(nodeLocation common.Location) common.Location { return loc } -// ConfirmationCtx indicates the chain context at which this ETX becomes -// confirmed and referencable to the destination chain -func (tx *Transaction) ConfirmationCtx(nodeLocation common.Location) int { - if ctx := tx.confirmCtx.Load(); ctx != nil { - return ctx.(int) - } - if tx.ETXSender().Location().Equal(*tx.To().Location()) { - // If the ETX sender and the destination chain are the same, the ETX is a conversion tx - return params.ConversionConfirmationContext - } - - ctx := tx.To().Location().CommonDom(tx.FromChain(nodeLocation)).Context() - tx.confirmCtx.Store(ctx) - return ctx -} - // Size returns the true RLP encoded storage size of the transaction, either by // encoding and returning it, or returning a previously cached value. func (tx *Transaction) Size() common.StorageSize { @@ -856,25 +839,27 @@ func (s Transactions) FilterToLocation(l common.Location) Transactions { } // FilterToSlice returns the subset of transactions with a 'to' address which -// belongs to the given slice location, at or above the given minimum context -func (s Transactions) FilterToSlice(slice common.Location) Transactions { +// belongs to the given sub location, at or above the given minimum context +func (s Transactions) FilterToSub(slice common.Location, nodeCtx int) Transactions { filteredList := Transactions{} for _, tx := range s { - toChain := tx.To().Location() - if toChain.Equal(slice) { - filteredList = append(filteredList, tx) - } - } - return filteredList -} - -// FilterConfirmationCtx returns the subset of transactions who can be confirmed -// at the given context -func (s Transactions) FilterConfirmationCtx(ctx int, nodeLocation common.Location) Transactions { - filteredList := Transactions{} - for _, tx := range s { - if tx.ConfirmationCtx(nodeLocation) == ctx { - filteredList = append(filteredList, tx) + // check if the tx is a conversion type and filter all the conversion types + // that are going into the region in the case of Prime and all the ones + // going to the zone in the case of region + // In the case of Prime, we will filter all the etxs that are going into + // any zone in the given location, but in the case of Region we only send + // the relevant etxs down + switch nodeCtx { + case common.PRIME_CTX: + if tx.IsTxAConversionTx(slice) && tx.To().Location().Region() == slice.Region() || + tx.To().Location().Region() == slice.Region() { + filteredList = append(filteredList, tx) + } + case common.REGION_CTX: + if tx.IsTxAConversionTx(slice) && tx.To().Location().Equal(slice) || + tx.To().Location().Equal(slice) { + filteredList = append(filteredList, tx) + } } } return filteredList @@ -1247,3 +1232,11 @@ func (al *AccessList) ProtoDecode(protoAccessList *ProtoAccessList, location com func GetInnerForTesting(tx *Transaction) TxData { return tx.inner } + +// It checks if an tx is a conversion type +func (tx *Transaction) IsTxAConversionTx(nodeLocation common.Location) bool { + if tx.Type() != ExternalTxType { + return false + } + return tx.ETXSender().Location().Equal(*tx.To().Location()) +} diff --git a/core/worker.go b/core/worker.go index 3953204375..a38b6caad8 100644 --- a/core/worker.go +++ b/core/worker.go @@ -1373,7 +1373,16 @@ func (w *worker) FinalizeAssemble(chain consensus.ChainHeaderReader, newWo *type } etxRollup = append(etxRollup, parent.ExtTransactions()...) } - etxRollupHash := types.DeriveSha(etxRollup, trie.NewStackTrie(nil)) + // Only include the etxs that are going cross Prime in the rollup and the + // conversion type + filteredEtxsRollup := types.Transactions{} + for _, etx := range etxRollup { + to := etx.To().Location() + if to.Region() != w.hc.NodeLocation().Region() || etx.IsTxAConversionTx(w.hc.NodeLocation()) { + filteredEtxsRollup = append(filteredEtxsRollup, etx) + } + } + etxRollupHash := types.DeriveSha(filteredEtxsRollup, trie.NewStackTrie(nil)) wo.Header().SetEtxRollupHash(etxRollupHash) } }