Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented the rolldown of cross prime etxs through regions #1846

Merged
merged 1 commit into from
Jun 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions common/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
9 changes: 4 additions & 5 deletions core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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()))
}
}
}
Expand Down
7 changes: 0 additions & 7 deletions core/headerchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
}
}
Expand Down
72 changes: 48 additions & 24 deletions core/slice.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand All @@ -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))
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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{}
Expand All @@ -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
Expand Down Expand Up @@ -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 {
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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")
}
Expand All @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
61 changes: 27 additions & 34 deletions core/types/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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())
}
11 changes: 10 additions & 1 deletion core/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Expand Down
Loading