-
Notifications
You must be signed in to change notification settings - Fork 0
feat: txgossip package
#26
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
base: main
Are you sure you want to change the base?
Changes from 53 commits
171cd6f
0b59c0e
5eca897
1fd6ecf
bd0a0c3
f3ca5e7
d04027f
f442e76
d74bccc
90a49eb
0245a1b
fc2f6fc
0183d17
e7772ce
03aa639
3023e06
4e46f0b
60c8273
472f9fb
433494f
d56614d
f7bcfb1
476e73c
18ee4b5
6feac2a
eb40cdd
9e314c4
a4b244c
dcccec9
5b5304c
e042828
894b190
88d0728
f55b3b9
00e5417
c1f12b8
4fe05c1
0fafec4
e64de9c
beed9ca
c6d1ef2
48342cd
caf3242
2f17888
0789c80
15ea194
97b474a
0e3a778
06256a9
f41f1a7
eb39d06
3d9b416
7ed61a4
ef30b88
4ae521b
46199a2
7358fcb
9c79186
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,108 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||
| // Copyright (C) 2025, Ava Labs, Inc. All rights reserved. | ||||||||||||||||||||||||||||||||||||||||||||||||
| // See the file LICENSE for licensing terms. | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| package txgossip | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| import ( | ||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/ava-labs/libevm/common" | ||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/ava-labs/libevm/core" | ||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/ava-labs/libevm/core/state" | ||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/ava-labs/libevm/core/txpool" | ||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/ava-labs/libevm/core/txpool/legacypool" | ||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/ava-labs/libevm/core/types" | ||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/ava-labs/libevm/event" | ||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/ava-labs/libevm/params" | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/ava-labs/strevm/blocks" | ||||||||||||||||||||||||||||||||||||||||||||||||
| "github.com/ava-labs/strevm/saexec" | ||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // A BlockChain is the union of [txpool.BlockChain] and [legacypool.BlockChain]. | ||||||||||||||||||||||||||||||||||||||||||||||||
| type BlockChain interface { | ||||||||||||||||||||||||||||||||||||||||||||||||
| txpool.BlockChain | ||||||||||||||||||||||||||||||||||||||||||||||||
| legacypool.BlockChain | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // NewBlockChain wraps an [saexec.Executor] to be compatible with a | ||||||||||||||||||||||||||||||||||||||||||||||||
| // non-blob-transaction mempool. | ||||||||||||||||||||||||||||||||||||||||||||||||
| func NewBlockChain(exec *saexec.Executor, blocks blocks.Source) BlockChain { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return &blockchain{ | ||||||||||||||||||||||||||||||||||||||||||||||||
| exec: exec, | ||||||||||||||||||||||||||||||||||||||||||||||||
| blocks: blocks, | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| type blockchain struct { | ||||||||||||||||||||||||||||||||||||||||||||||||
| exec *saexec.Executor | ||||||||||||||||||||||||||||||||||||||||||||||||
| blocks blocks.Source | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| func (bc *blockchain) Config() *params.ChainConfig { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return bc.exec.ChainConfig() | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| func (bc *blockchain) CurrentBlock() *types.Header { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return bc.exec.LastEnqueued().Header() | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| func (bc *blockchain) GetBlock(hash common.Hash, number uint64) *types.Block { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return bc.blocks.EthBlock(hash, number) | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| func (bc *blockchain) StateAt(root common.Hash) (*state.StateDB, error) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return state.New(root, bc.exec.StateCache(), nil) | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // SubscribeChainHeadEvent subscribes to block enqueueing, NOT to regular head | ||||||||||||||||||||||||||||||||||||||||||||||||
| // events as these only occur after execution. Enqueuing is equivalent to block | ||||||||||||||||||||||||||||||||||||||||||||||||
| // acceptance, which is when a transaction SHOULD be removed from the mempool. | ||||||||||||||||||||||||||||||||||||||||||||||||
| func (bc *blockchain) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription { | ||||||||||||||||||||||||||||||||||||||||||||||||
| bCh := make(chan *types.Block) | ||||||||||||||||||||||||||||||||||||||||||||||||
| sub := bc.exec.SubscribeBlockEnqueueEvent(bCh) | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| p := &pipe{ | ||||||||||||||||||||||||||||||||||||||||||||||||
| in: bCh, | ||||||||||||||||||||||||||||||||||||||||||||||||
| out: ch, | ||||||||||||||||||||||||||||||||||||||||||||||||
| sub: sub, | ||||||||||||||||||||||||||||||||||||||||||||||||
| quit: make(chan struct{}), | ||||||||||||||||||||||||||||||||||||||||||||||||
| done: make(chan struct{}), | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| go p.loop() | ||||||||||||||||||||||||||||||||||||||||||||||||
| return p | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| // A pipe is an [event.Subscription] that converts from a [types.Block] channel | ||||||||||||||||||||||||||||||||||||||||||||||||
| // to a [core.ChainHeadEvent] one. | ||||||||||||||||||||||||||||||||||||||||||||||||
| type pipe struct { | ||||||||||||||||||||||||||||||||||||||||||||||||
| in <-chan *types.Block | ||||||||||||||||||||||||||||||||||||||||||||||||
| out chan<- core.ChainHeadEvent | ||||||||||||||||||||||||||||||||||||||||||||||||
| sub event.Subscription | ||||||||||||||||||||||||||||||||||||||||||||||||
| quit, done chan struct{} | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| func (p *pipe) loop() { | ||||||||||||||||||||||||||||||||||||||||||||||||
| defer close(p.done) | ||||||||||||||||||||||||||||||||||||||||||||||||
| for { | ||||||||||||||||||||||||||||||||||||||||||||||||
| select { | ||||||||||||||||||||||||||||||||||||||||||||||||
| case b := <-p.in: | ||||||||||||||||||||||||||||||||||||||||||||||||
| select { | ||||||||||||||||||||||||||||||||||||||||||||||||
| case p.out <- core.ChainHeadEvent{Block: b}: | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| case <-p.quit: | ||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| case <-p.quit: | ||||||||||||||||||||||||||||||||||||||||||||||||
| return | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+86
to
+96
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I feel like this makes the "receive" followed by "send" logic a bit more clear
Suggested change
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have a strong dislike of the "variable look-back" pattern as it causes a non-linearity in reading the code. I'm being hyperbolic here, but this is the cognitive pattern I'm trying to avoid:
vs
Granted in this particular solution it's not all that significant because the "distraction" code is minimal, but I still feel like it's an anti-pattern. All that said, I'm open to having my mind changed if you think there are sufficient reasons to change. |
||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| func (p *pipe) Err() <-chan error { | ||||||||||||||||||||||||||||||||||||||||||||||||
| return p.sub.Err() | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| func (p *pipe) Unsubscribe() { | ||||||||||||||||||||||||||||||||||||||||||||||||
| p.sub.Unsubscribe() | ||||||||||||||||||||||||||||||||||||||||||||||||
| close(p.quit) | ||||||||||||||||||||||||||||||||||||||||||||||||
| <-p.done | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| // Copyright (C) 2025, Ava Labs, Inc. All rights reserved. | ||
| // See the file LICENSE for licensing terms. | ||
|
|
||
| package txgossip | ||
|
|
||
| import ( | ||
| "slices" | ||
|
|
||
| "github.com/ava-labs/libevm/common" | ||
| "github.com/ava-labs/libevm/core/txpool" | ||
| ) | ||
|
|
||
| // A LazyTransaction couples a [txpool.LazyTransaction] with its sender. | ||
| type LazyTransaction struct { | ||
| *txpool.LazyTransaction | ||
| Sender common.Address | ||
| } | ||
|
|
||
| // TransactionsByPriority calls [txpool.TxPool.Pending] with the given filter, | ||
| // collapses the results into a slice, and sorts said slice by decreasing gas | ||
| // tip then chronologically. Transactions from the same sender are merely sorted | ||
| // by increasing nonce. | ||
| func (s *Set) TransactionsByPriority(filter txpool.PendingFilter) []*LazyTransaction { | ||
| pending := s.Pool.Pending(filter) | ||
| var n int | ||
| for _, txs := range pending { | ||
| n += len(txs) | ||
| } | ||
|
|
||
| all := make([]*LazyTransaction, n) | ||
| var i int | ||
| for from, txs := range pending { | ||
| for _, tx := range txs { | ||
| all[i] = &LazyTransaction{ | ||
| LazyTransaction: tx, | ||
| Sender: from, | ||
| } | ||
| i++ | ||
| } | ||
| } | ||
|
|
||
| slices.SortStableFunc(all, func(a, b *LazyTransaction) int { | ||
| if a.Sender == b.Sender { | ||
| // [txpool.TxPool.Pending] already returns each slice in nonce order | ||
| // and we're performing a stable sort. | ||
| return 0 | ||
| } | ||
| if tip := a.GasTipCap.Cmp(b.GasTipCap); tip != 0 { | ||
| return -tip // Higher tips first | ||
| } | ||
| return a.Time.Compare(b.Time) | ||
| }) | ||
| return all | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, with this PR the mempool's validity checking is based on the settled state right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Intent is currently undefined. Sorry, I should have been clearer in what I meant in the PR description (i.e. I'm kicking that can down the road entirely):
The p2p architecture and gossip was all so new to me that I found this PR relatively difficult, so wanted to focus on learning it and getting the wiring correct. I'll concentrate on things like validity when we have the
VMto orchestrate all the moving parts.