diff --git a/grpc/execution/server.go b/grpc/execution/server.go index 1bb17cb1e..2cf3dd7c9 100644 --- a/grpc/execution/server.go +++ b/grpc/execution/server.go @@ -172,7 +172,7 @@ func (s *ExecutionServiceServerV1) ExecuteBlock(ctx context.Context, req *astria addressPrefix := s.Bc().Config().AstriaSequencerAddressPrefix - txsToProcess := shared.UnbundleRollupDataTransactions(req.Transactions, height, s.BridgeAddresses(), s.BridgeAllowedAssets(), prevHeadHash.Bytes(), s.AuctioneerAddress(), addressPrefix) + txsToProcess := shared.UnbundleRollupDataTransactions(req.Transactions, height, s.BridgeAddresses(), s.BridgeAllowedAssets(), prevHeadHash.Bytes(), s.AuctioneerAddress(), s.AuctioneerStartHeight(), addressPrefix) // This set of ordered TXs on the TxPool is has been configured to be used by // the Miner when building a payload. @@ -448,3 +448,7 @@ func (s *ExecutionServiceServerV1) AuctioneerAddress() string { func (s *ExecutionServiceServerV1) SetAuctioneerAddress(auctioneerAddress string) { s.sharedServiceContainer.SetAuctioneerAddress(auctioneerAddress) } + +func (s *ExecutionServiceServerV1) AuctioneerStartHeight() uint64 { + return s.sharedServiceContainer.AuctioneerStartHeight() +} diff --git a/grpc/optimistic/server.go b/grpc/optimistic/server.go index 52cf8116c..e2959b0c4 100644 --- a/grpc/optimistic/server.go +++ b/grpc/optimistic/server.go @@ -193,7 +193,7 @@ func (o *OptimisticServiceV1Alpha1) ExecuteOptimisticBlock(ctx context.Context, addressPrefix := o.Bc().Config().AstriaSequencerAddressPrefix - txsToProcess := shared.UnbundleRollupDataTransactions(req.Transactions, height, o.BridgeAddresses(), o.BridgeAllowedAssets(), softBlock.Hash().Bytes(), o.AuctioneerAddress(), addressPrefix) + txsToProcess := shared.UnbundleRollupDataTransactions(req.Transactions, height, o.BridgeAddresses(), o.BridgeAllowedAssets(), softBlock.Hash().Bytes(), o.AuctioneerAddress(), o.AuctioneerStartHeight(), addressPrefix) // Build a payload to add to the chain payloadAttributes := &miner.BuildPayloadArgs{ @@ -286,18 +286,22 @@ func (o *OptimisticServiceV1Alpha1) SetNextFeeRecipient(feeRecipient common.Addr o.sharedServiceContainer.SetNextFeeRecipient(feeRecipient) } -func (s *OptimisticServiceV1Alpha1) BridgeAddresses() map[string]*params.AstriaBridgeAddressConfig { - return s.sharedServiceContainer.BridgeAddresses() +func (o *OptimisticServiceV1Alpha1) BridgeAddresses() map[string]*params.AstriaBridgeAddressConfig { + return o.sharedServiceContainer.BridgeAddresses() } -func (s *OptimisticServiceV1Alpha1) BridgeAllowedAssets() map[string]struct{} { - return s.sharedServiceContainer.BridgeAllowedAssets() +func (o *OptimisticServiceV1Alpha1) BridgeAllowedAssets() map[string]struct{} { + return o.sharedServiceContainer.BridgeAllowedAssets() } -func (s *OptimisticServiceV1Alpha1) SyncMethodsCalled() bool { - return s.sharedServiceContainer.SyncMethodsCalled() +func (o *OptimisticServiceV1Alpha1) SyncMethodsCalled() bool { + return o.sharedServiceContainer.SyncMethodsCalled() } -func (s *OptimisticServiceV1Alpha1) AuctioneerAddress() string { - return s.sharedServiceContainer.AuctioneerAddress() +func (o *OptimisticServiceV1Alpha1) AuctioneerAddress() string { + return o.sharedServiceContainer.AuctioneerAddress() +} + +func (o *OptimisticServiceV1Alpha1) AuctioneerStartHeight() uint64 { + return o.sharedServiceContainer.AuctioneerStartHeight() } diff --git a/grpc/shared/container.go b/grpc/shared/container.go index 263971c5e..8972729d1 100644 --- a/grpc/shared/container.go +++ b/grpc/shared/container.go @@ -27,6 +27,9 @@ type SharedServiceContainer struct { // auctioneer address is a bech32m address auctioneerAddress atomic.Pointer[string] + // this is set to the height at which the first auctioneer address is activated. + // before `auctioneerStartHeight` any incoming `Allocations` will be ignored + auctioneerStartHeight uint64 nextFeeRecipient atomic.Pointer[common.Address] // Fee recipient for the next block } @@ -109,6 +112,7 @@ func NewSharedServiceContainer(eth *eth.Ethereum) (*SharedServiceContainer, erro for height, address := range auctioneerAddressesBlockMap { if height <= nextBlock && height > maxHeightCollectorMatch { maxHeightCollectorMatch = height + if err := ValidateBech32mAddress(address, bc.Config().AstriaSequencerAddressPrefix); err != nil { return nil, errors.Wrapf(err, "auctioneer address %s at height %d is invalid", address, height) } @@ -117,11 +121,20 @@ func NewSharedServiceContainer(eth *eth.Ethereum) (*SharedServiceContainer, erro } } + // the height at which the first auctioneer address is activated + auctioneerStartHeight := ^uint64(0) + for height := range auctioneerAddressesBlockMap { + if uint64(height) < auctioneerStartHeight { + auctioneerStartHeight = uint64(height) + } + } + sharedServiceContainer := &SharedServiceContainer{ - eth: eth, - bc: bc, - bridgeAddresses: bridgeAddresses, - bridgeAllowedAssets: bridgeAllowedAssets, + eth: eth, + bc: bc, + bridgeAddresses: bridgeAddresses, + bridgeAllowedAssets: bridgeAllowedAssets, + auctioneerStartHeight: auctioneerStartHeight, } sharedServiceContainer.SetAuctioneerAddress(auctioneerAddress) @@ -183,6 +196,10 @@ func (s *SharedServiceContainer) BridgeAllowedAssets() map[string]struct{} { return s.bridgeAllowedAssets } +func (s *SharedServiceContainer) AuctioneerStartHeight() uint64 { + return s.auctioneerStartHeight +} + func (s *SharedServiceContainer) AuctioneerAddress() string { return *s.auctioneerAddress.Load() } diff --git a/grpc/shared/validation.go b/grpc/shared/validation.go index 90ae13619..f29e47181 100644 --- a/grpc/shared/validation.go +++ b/grpc/shared/validation.go @@ -193,15 +193,17 @@ func unmarshallAllocationTxs(allocation *auctionv1alpha1.Allocation, prevBlockHa } -// `UnbundleRollupDataTransactions` takes in a list of rollup data transactions and returns a list of Ethereum transactions. -// TODO - this function has become too big. we should start breaking it down +// `UnbundleRollupDataTransactions` takes in a list of rollup data transactions and returns the corresponding +// list of Ethereum transactions. It also returns an allocation transaction if it finds one in the rollup data. +// The allocation transaction is placed at the beginning of the returned list of transactions. func UnbundleRollupDataTransactions(txs []*sequencerblockv1.RollupData, height uint64, bridgeAddresses map[string]*params.AstriaBridgeAddressConfig, - bridgeAllowedAssets map[string]struct{}, prevBlockHash []byte, auctioneerBech32Address string, addressPrefix string) types.Transactions { + bridgeAllowedAssets map[string]struct{}, prevBlockHash []byte, auctioneerBech32Address string, auctioneerStartHeight uint64, addressPrefix string) types.Transactions { processedTxs := types.Transactions{} allocationTxs := types.Transactions{} - // we just return the allocation here and do not unmarshall the transactions in the bid if we find it + var allocation *auctionv1alpha1.Allocation + for _, tx := range txs { if deposit := tx.GetDeposit(); deposit != nil { depositTx, err := validateAndUnmarshalDepositTx(deposit, height, bridgeAddresses, bridgeAllowedAssets) @@ -213,27 +215,29 @@ func UnbundleRollupDataTransactions(txs []*sequencerblockv1.RollupData, height u processedTxs = append(processedTxs, depositTx) } else { sequenceData := tx.GetSequencedData() - // check if sequence data is of type Allocation - if allocation == nil { - // TODO - check if we can avoid a temp value - tempAllocation := &auctionv1alpha1.Allocation{} - err := proto.Unmarshal(sequenceData, tempAllocation) - if err == nil { + // check if sequence data is of type Allocation. + // we should expect only one valid allocation per block. duplicate allocations should be ignored. + + tempAllocation := &auctionv1alpha1.Allocation{} + err := proto.Unmarshal(sequenceData, tempAllocation) + if err == nil { + if allocation != nil { + log.Debug("ignoring allocation tx as it is a duplicate", "height", height) + } else { + if height < auctioneerStartHeight { + log.Debug("ignoring allocation tx as it is before the auctioneer start height", "height", height, "auctioneerStartHeight", auctioneerStartHeight) + continue + } + unmarshalledAllocationTxs, err := unmarshallAllocationTxs(tempAllocation, prevBlockHash, auctioneerBech32Address, addressPrefix) if err != nil { log.Error("failed to unmarshall allocation transactions", "error", err) continue } + // we found the valid allocation, we should ignore all future allocations in this block allocation = tempAllocation allocationTxs = unmarshalledAllocationTxs - } else { - ethtx, err := validateAndUnmarshallSequenceAction(tx) - if err != nil { - log.Error("failed to unmarshall sequence action", "error", err) - continue - } - processedTxs = append(processedTxs, ethtx) } } else { ethtx, err := validateAndUnmarshallSequenceAction(tx) diff --git a/grpc/shared/validation_test.go b/grpc/shared/validation_test.go index 3292d5334..1dd77aaf6 100644 --- a/grpc/shared/validation_test.go +++ b/grpc/shared/validation_test.go @@ -482,7 +482,7 @@ func TestUnbundleRollupData(t *testing.T) { finalTxs := []*sequencerblockv1.RollupData{seqData1, seqData2, allocationSequenceData, depositTx} - txsToProcess := UnbundleRollupDataTransactions(finalTxs, 2, serviceV1Alpha1.BridgeAddresses(), serviceV1Alpha1.BridgeAllowedAssets(), prevRollupBlockHash, serviceV1Alpha1.AuctioneerAddress(), addressPrefix) + txsToProcess := UnbundleRollupDataTransactions(finalTxs, 2, serviceV1Alpha1.BridgeAddresses(), serviceV1Alpha1.BridgeAllowedAssets(), prevRollupBlockHash, serviceV1Alpha1.AuctioneerAddress(), serviceV1Alpha1.AuctioneerStartHeight(), addressPrefix) require.Equal(t, txsToProcess.Len(), 6, "expected 6 txs to process") @@ -589,7 +589,7 @@ func TestUnbundleRollupDataWithDuplicateAllocations(t *testing.T) { finalTxs := []*sequencerblockv1.RollupData{seqData1, seqData2, allocationSequenceData, allocationSequenceData2, depositTx} - txsToProcess := UnbundleRollupDataTransactions(finalTxs, 2, serviceV1Alpha1.BridgeAddresses(), serviceV1Alpha1.BridgeAllowedAssets(), prevRollupBlockHash, serviceV1Alpha1.AuctioneerAddress(), addressPrefix) + txsToProcess := UnbundleRollupDataTransactions(finalTxs, 2, serviceV1Alpha1.BridgeAddresses(), serviceV1Alpha1.BridgeAllowedAssets(), prevRollupBlockHash, serviceV1Alpha1.AuctioneerAddress(), serviceV1Alpha1.AuctioneerStartHeight(), addressPrefix) require.Equal(t, txsToProcess.Len(), 6, "expected 6 txs to process") @@ -734,7 +734,7 @@ func TestUnbundleRollupDataWithDuplicateInvalidAllocations(t *testing.T) { finalTxs := []*sequencerblockv1.RollupData{seqData1, seqData2, allocationSequenceData, invalidAllocationSequenceData, depositTx} - txsToProcess := UnbundleRollupDataTransactions(finalTxs, 2, serviceV1Alpha1.BridgeAddresses(), serviceV1Alpha1.BridgeAllowedAssets(), prevRollupBlockHash, serviceV1Alpha1.AuctioneerAddress(), addressPrefix) + txsToProcess := UnbundleRollupDataTransactions(finalTxs, 2, serviceV1Alpha1.BridgeAddresses(), serviceV1Alpha1.BridgeAllowedAssets(), prevRollupBlockHash, serviceV1Alpha1.AuctioneerAddress(), serviceV1Alpha1.AuctioneerStartHeight(), addressPrefix) require.Equal(t, txsToProcess.Len(), 6, "expected 6 txs to process")