Skip to content

Commit

Permalink
Implemented the rolldown of cross prime etxs through regions
Browse files Browse the repository at this point in the history
  • Loading branch information
gameofpointers committed Jun 10, 2024
1 parent f7a29b4 commit 3d6dcd0
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 73 deletions.
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() {
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
48 changes: 14 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 @@ -762,22 +761,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 @@ -857,25 +840,22 @@ 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 {
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 {
// 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 {
if tx.ConfirmationCtx(nodeLocation) == ctx {
filteredList = append(filteredList, tx)
// 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.To().Location().SubIndex(nodeCtx) == slice.SubIndex(nodeCtx) {
filteredList = append(filteredList, tx)
}
case common.REGION_CTX:
if tx.To().Location().Equal(slice) {
filteredList = append(filteredList, tx)
}
}
}
return filteredList
Expand Down
10 changes: 9 additions & 1 deletion core/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -1364,7 +1364,15 @@ 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
filteredEtxsRollup := types.Transactions{}
for _, etx := range etxRollup {
to := etx.To().Location()
if to.Region() != w.hc.NodeLocation().Region() {
filteredEtxsRollup = append(filteredEtxsRollup, etx)
}
}
etxRollupHash := types.DeriveSha(filteredEtxsRollup, trie.NewStackTrie(nil))
wo.Header().SetEtxRollupHash(etxRollupHash)
}
}
Expand Down

0 comments on commit 3d6dcd0

Please sign in to comment.