core/txpool/blobpool: add BlobTxForPool type#34882
Conversation
|
If it is at all possible, we should move this type into |
|
|
||
| // GetRLP returns a RLP-encoded transaction for network if it is contained in the pool. | ||
| // GetRLP returns a RLP-encoded transaction for p2p messages, converted from | ||
| // the pool's internal type, if it is contained in the pool. |
There was a problem hiding this comment.
We have to make a small decision here. GetRLP is supposed to be an optimization that makes the pool return objects which are directly sendable in a p2p message. With the new format, this is not easily possible anymore. So the question now becomes, if we have to transform the format anyway, should we transform it here or in the p2p protocol implementation?
I think it's fine to have knowledge of the p2p encoding in the blobpool. But we should document the format returned by this method, i.e. say
// This method returns the RLP format used by the eth protocol:
// txType || [..., version, blobs, commitments, proofs]
There was a problem hiding this comment.
In the eth/72 protocol, the blobs in this encoding is always empty !! 😭
But @healthykim confirmed that we will later change this method to pass in the protocol version for which it should be encoded.
There was a problem hiding this comment.
I think we can leave it like this here
I looked a bit more into where the GET operations are actually used.
Getis only used by the resolver, at least in blobpool. (legacypool also uses it at the P2P level for broadcasting. It is a bit tricky since they share the same interface.)- GetRLP is only used for P2P serving.
So I think the possible approach for sparse blobpool would be:
- keep GetRLP as the P2P path and take a version number,
- make Get return everything except sidecars,
- split sidecar/cell queries into separate functions
| txRLP := txBytes[1:] | ||
|
|
||
| // 2. Find the version of sidecar. | ||
| version, _, err := rlp.SplitString(elems[1]) |
There was a problem hiding this comment.
Can it use rlp.SplitUint64? The version is an integer.
| } | ||
|
|
||
| // recheck verifies the pool's content for a specific account and drops anything | ||
| // that does not |
There was a problem hiding this comment.
this comment was duplicated accidentally
| } | ||
| var tx types.Transaction | ||
| if err = rlp.DecodeBytes(data, &tx); err != nil { | ||
| ptx := new(blobTxForPool) |
There was a problem hiding this comment.
Style thing, why use new(T) instead of var ptx T
| tx := new(types.Transaction) | ||
| if err := rlp.DecodeBytes(blob, tx); err != nil { | ||
| return false, err | ||
| } |
There was a problem hiding this comment.
Do we really have to do this? It is quite an expensive way to check. We could also just check if blob[0] == 3 or something.
| if p.lookup.exists(meta.hash) { | ||
| return false, errors.New("duplicate blob entry") | ||
| } |
There was a problem hiding this comment.
This is duplicated in trackTransaction
| // blobTxForPool is the storage representation of a blob transaction in the | ||
| // blobpool. | ||
| type blobTxForPool struct { | ||
| Tx *types.Transaction // tx without sidecar |
There was a problem hiding this comment.
We could embed this field. Then you can just call ptx.Hash().
| TxHash common.Hash // Owner transaction's hash to support resurrecting reorged txs | ||
| Block uint64 // Block in which the blob transaction was included | ||
| Tx *types.Transaction | ||
| Ptx *blobTxForPool |
…n and trackTransaction
| } | ||
|
|
||
| // WithSidecar copies the sidecar's fields into the flat fields. | ||
| func (ptx *blobTxForPool) WithSidecar(sc *types.BlobTxSidecar) { |
There was a problem hiding this comment.
We typically use the WithX naming for methods that copy the receiver and return it modified. This method just modifies the receiver so it should be called ApplySidecar.
| var fails []uint64 | ||
| var ( | ||
| fails []uint64 | ||
| convertTxs []*types.Transaction |
There was a problem hiding this comment.
Just a note here, if the blobpool is at full capacity, this slice could potentially hold multiple gigabytes worth of transactions. I don't think it will occur in practice, but might be better to store the ids, then pull up the transactions from billy one-by-one for the conversion.
| // Index all transactions on disk and delete anything unprocessable | ||
| var fails []uint64 | ||
| var ( | ||
| fails []uint64 |
There was a problem hiding this comment.
Also fails here is a bit strange name. The idea is that it contains the ids that will be deleted. So perhaps it should be called toDelete.
| var ptx blobTxForPool | ||
| if err := rlp.DecodeBytes(blob, &ptx); err != nil { | ||
| kind, _, _, splitErr := rlp.Split(blob) | ||
| if splitErr == nil && kind == rlp.String { |
There was a problem hiding this comment.
you could still check for the first byte being 3 here, like this
func isLegacyBlobTx(b []byte) bool {
k, content, _, err := rlp.Split(b)
return err == nil && k == rlp.String && len(content) > 1 && content[0] == 3
}It's basically free to check this.
| return true, nil | ||
| } | ||
| return false, err |
There was a problem hiding this comment.
Actually, it's a bit strange to return a boolean. You could also return a special error errLegacyTx and then match on it like err == errLegacyTx in the caller.
|
#34960. May you check this? |
This PR introduces a separate transaction pool type for sparse blobpool.
In sparse blobpool, PooledTransactions message delivers transactions without
blobs, partial or full cells are downloaded by Cells message. Blobpool no longer
stores transactions with complete sidecars, and it stores transactions without
blobs, along with the corresponding cells. Because of this, a dedicated type
distinct from types.Transaction is required.
This PR introduces a type called
BlobTxForPooland stores each sidecar fieldindependently, in order to bypass the assumption that a sidecar always exists as
a complete unit.
Reintroducing the conversion queue was considered, but was ultimately omitted
because type conversion should be sufficiently fast. With sparse blobpool, blob
-> cell computation would take about ~13ms per blob. Not sure whether this is
fast enough, but otherwise we can add the conversion queue later on the sparse
blobpool branch.