forked from OffchainLabs/nitro
-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Full node with espresso finality (#237)
* wip * wip * Add espresso finality node test * fix ci * fix ci * pin to before marketplace version * fix tests * add comments * address comments * address comments * address comments * cleanup * fix test * fmt * rerun ci * ci skip tests
- Loading branch information
Showing
6 changed files
with
255 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
package gethexec | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
espressoClient "github.com/EspressoSystems/espresso-sequencer-go/client" | ||
"github.com/ethereum/go-ethereum/arbitrum_types" | ||
"github.com/ethereum/go-ethereum/core/types" | ||
"github.com/ethereum/go-ethereum/log" | ||
"github.com/offchainlabs/nitro/arbos" | ||
"time" | ||
|
||
"github.com/offchainlabs/nitro/arbos/arbostypes" | ||
"github.com/offchainlabs/nitro/arbos/l1pricing" | ||
"github.com/offchainlabs/nitro/util/stopwaiter" | ||
) | ||
|
||
var ( | ||
retryTime = time.Second * 1 | ||
) | ||
|
||
/* | ||
Espresso Finality Node creates blocks with finalized hotshot transactions | ||
*/ | ||
type EspressoFinalityNode struct { | ||
stopwaiter.StopWaiter | ||
|
||
config SequencerConfigFetcher | ||
execEngine *ExecutionEngine | ||
namespace uint64 | ||
|
||
espressoClient *espressoClient.Client | ||
nextSeqBlockNum uint64 | ||
} | ||
|
||
func NewEspressoFinalityNode(execEngine *ExecutionEngine, configFetcher SequencerConfigFetcher) *EspressoFinalityNode { | ||
config := configFetcher() | ||
if err := config.Validate(); err != nil { | ||
panic(err) | ||
} | ||
return &EspressoFinalityNode{ | ||
execEngine: execEngine, | ||
config: configFetcher, | ||
namespace: config.EspressoFinalityNodeConfig.Namespace, | ||
espressoClient: espressoClient.NewClient(config.EspressoFinalityNodeConfig.HotShotUrl), | ||
nextSeqBlockNum: config.EspressoFinalityNodeConfig.StartBlock, | ||
} | ||
} | ||
|
||
func (n *EspressoFinalityNode) createBlock(ctx context.Context) (returnValue bool) { | ||
if n.nextSeqBlockNum == 0 { | ||
latestBlock, err := n.espressoClient.FetchLatestBlockHeight(ctx) | ||
if err != nil && latestBlock == 0 { | ||
log.Warn("unable to fetch latest hotshot block", "err", err) | ||
return false | ||
} | ||
log.Info("Started espresso finality node at the latest hotshot block", "block number", latestBlock) | ||
n.nextSeqBlockNum = latestBlock | ||
} | ||
|
||
nextSeqBlockNum := n.nextSeqBlockNum | ||
header, err := n.espressoClient.FetchHeaderByHeight(ctx, nextSeqBlockNum) | ||
if err != nil { | ||
arbos.LogFailedToFetchHeader(nextSeqBlockNum) | ||
return false | ||
} | ||
|
||
arbTxns, err := n.espressoClient.FetchTransactionsInBlock(ctx, header.Height, n.namespace) | ||
if err != nil { | ||
arbos.LogFailedToFetchTransactions(header.Height, err) | ||
return false | ||
} | ||
arbHeader := &arbostypes.L1IncomingMessageHeader{ | ||
Kind: arbostypes.L1MessageType_L2Message, | ||
Poster: l1pricing.BatchPosterAddress, | ||
BlockNumber: header.L1Head, | ||
Timestamp: header.Timestamp, | ||
RequestId: nil, | ||
L1BaseFee: nil, | ||
} | ||
|
||
// Deserialize the transactions and remove the signature from the transactions. | ||
// Ignore the malformed transactions | ||
txes := types.Transactions{} | ||
for _, tx := range arbTxns.Transactions { | ||
var out types.Transaction | ||
// signature from the data poster is the first 65 bytes of a transaction | ||
tx = tx[65:] | ||
if err := out.UnmarshalBinary(tx); err != nil { | ||
log.Warn("malformed tx found") | ||
continue | ||
} | ||
txes = append(txes, &out) | ||
} | ||
|
||
hooks := arbos.NoopSequencingHooks() | ||
_, err = n.execEngine.SequenceTransactions(arbHeader, txes, hooks, false) | ||
if err != nil { | ||
log.Error("espresso finality node: failed to sequence transactions", "err", err) | ||
return false | ||
} | ||
|
||
return true | ||
} | ||
|
||
func (n *EspressoFinalityNode) Start(ctx context.Context) error { | ||
n.StopWaiter.Start(ctx, n) | ||
err := n.CallIterativelySafe(func(ctx context.Context) time.Duration { | ||
madeBlock := n.createBlock(ctx) | ||
if madeBlock { | ||
n.nextSeqBlockNum += 1 | ||
return 0 | ||
} | ||
return retryTime | ||
}) | ||
if err != nil { | ||
return fmt.Errorf("failed to start espresso finality node: %w", err) | ||
} | ||
return nil | ||
} | ||
|
||
func (n *EspressoFinalityNode) PublishTransaction(ctx context.Context, tx *types.Transaction, options *arbitrum_types.ConditionalOptions) error { | ||
return nil | ||
} | ||
|
||
func (n *EspressoFinalityNode) CheckHealth(ctx context.Context) error { | ||
return nil | ||
} | ||
|
||
func (n *EspressoFinalityNode) Initialize(ctx context.Context) error { | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
package arbtest | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"testing" | ||
"time" | ||
) | ||
|
||
func createEspressoFinalityNode(t *testing.T, builder *NodeBuilder) (*TestClient, func()) { | ||
nodeConfig := builder.nodeConfig | ||
execConfig := builder.execConfig | ||
// poster config | ||
nodeConfig.BatchPoster.Enable = true | ||
nodeConfig.BatchPoster.ErrorDelay = 5 * time.Second | ||
nodeConfig.BatchPoster.MaxSize = 41 | ||
nodeConfig.BatchPoster.PollInterval = 10 * time.Second | ||
nodeConfig.BatchPoster.MaxDelay = -1000 * time.Hour | ||
nodeConfig.BatchPoster.LightClientAddress = lightClientAddress | ||
nodeConfig.BatchPoster.HotShotUrl = hotShotUrl | ||
|
||
nodeConfig.BlockValidator.Enable = true | ||
nodeConfig.BlockValidator.ValidationPoll = 2 * time.Second | ||
nodeConfig.BlockValidator.ValidationServer.URL = fmt.Sprintf("ws://127.0.0.1:%d", 54327) | ||
nodeConfig.BlockValidator.LightClientAddress = lightClientAddress | ||
nodeConfig.BlockValidator.Espresso = true | ||
nodeConfig.DelayedSequencer.Enable = true | ||
nodeConfig.DelayedSequencer.FinalizeDistance = 1 | ||
nodeConfig.Sequencer = true | ||
nodeConfig.Dangerous.NoSequencerCoordinator = true | ||
execConfig.Sequencer.Enable = true | ||
execConfig.Sequencer.EnableEspressoFinalityNode = true | ||
execConfig.Sequencer.EspressoFinalityNodeConfig.Namespace = builder.chainConfig.ChainID.Uint64() | ||
execConfig.Sequencer.EspressoFinalityNodeConfig.StartBlock = 1 | ||
execConfig.Sequencer.EspressoFinalityNodeConfig.HotShotUrl = hotShotUrl | ||
|
||
// disable sovereign sequencer | ||
execConfig.Sequencer.EnableEspressoSovereign = false | ||
builder.nodeConfig.TransactionStreamer.SovereignSequencerEnabled = false | ||
|
||
return builder.Build2ndNode(t, &SecondNodeParams{ | ||
nodeConfig: nodeConfig, | ||
execConfig: execConfig, | ||
}) | ||
} | ||
|
||
func TestEspressoFinalityNode(t *testing.T) { | ||
ctx, cancel := context.WithCancel(context.Background()) | ||
defer cancel() | ||
|
||
valNodeCleanup := createValidationNode(ctx, t, true) | ||
defer valNodeCleanup() | ||
|
||
builder, cleanup := createL1AndL2Node(ctx, t) | ||
defer cleanup() | ||
|
||
err := waitForL1Node(t, ctx) | ||
Require(t, err) | ||
|
||
cleanEspresso := runEspresso(t, ctx) | ||
defer cleanEspresso() | ||
|
||
// wait for the builder | ||
err = waitForEspressoNode(t, ctx) | ||
Require(t, err) | ||
|
||
err = checkTransferTxOnL2(t, ctx, builder.L2, "User14", builder.L2Info) | ||
Require(t, err) | ||
|
||
msgCnt, err := builder.L2.ConsensusNode.TxStreamer.GetMessageCount() | ||
Require(t, err) | ||
|
||
err = waitForWith(t, ctx, 6*time.Minute, 60*time.Second, func() bool { | ||
validatedCnt := builder.L2.ConsensusNode.BlockValidator.Validated(t) | ||
return validatedCnt == msgCnt | ||
}) | ||
Require(t, err) | ||
|
||
// start the finality node | ||
builderEspressoFinalityNode, cleanupEspressoFinalityNode := createEspressoFinalityNode(t, builder) | ||
defer cleanupEspressoFinalityNode() | ||
|
||
err = waitForWith(t, ctx, 6*time.Minute, 60*time.Second, func() bool { | ||
msgCntFinalityNode, err := builderEspressoFinalityNode.ConsensusNode.TxStreamer.GetMessageCount() | ||
Require(t, err) | ||
return msgCntFinalityNode == msgCnt | ||
}) | ||
Require(t, err) | ||
} |