Skip to content

Commit

Permalink
EVM-746 Fix txpool_content and txpool_inspect for dynamic fee txs (#1741
Browse files Browse the repository at this point in the history
)

* EVM-746 Fix txpool_content and txpool_inspect for dynamic fee txs

* just a small change
  • Loading branch information
igorcrevar authored Jul 24, 2023
1 parent 7d15e3a commit 13e82b6
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 127 deletions.
132 changes: 33 additions & 99 deletions jsonrpc/txpool_endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ type txPoolStore interface {

// GetCapacity returns the current and max capacity of the pool in slots
GetCapacity() (uint64, uint64)

// GetBaseFee returns current base fee
GetBaseFee() uint64
}

// TxPool is the txpool jsonrpc endpoint
Expand All @@ -22,8 +25,8 @@ type TxPool struct {
}

type ContentResponse struct {
Pending map[types.Address]map[uint64]*txpoolTransaction `json:"pending"`
Queued map[types.Address]map[uint64]*txpoolTransaction `json:"queued"`
Pending map[types.Address]map[uint64]*transaction `json:"pending"`
Queued map[types.Address]map[uint64]*transaction `json:"queued"`
}

type InspectResponse struct {
Expand All @@ -38,86 +41,27 @@ type StatusResponse struct {
Queued uint64 `json:"queued"`
}

type txpoolTransaction struct {
Nonce argUint64 `json:"nonce"`
GasPrice argBig `json:"gasPrice"`
GasFeeCap *argBig `json:"gasFeeCap,omitempty"`
GasTipCap *argBig `json:"gasTipCap,omitempty"`
Gas argUint64 `json:"gas"`
To *types.Address `json:"to"`
Value argBig `json:"value"`
Input argBytes `json:"input"`
Hash types.Hash `json:"hash"`
From types.Address `json:"from"`
BlockHash types.Hash `json:"blockHash"`
BlockNumber interface{} `json:"blockNumber"`
TxIndex interface{} `json:"transactionIndex"`
}

func toTxPoolTransaction(t *types.Transaction) *txpoolTransaction {
var gasTipCap, gasFeeCap *argBig

if t.GasTipCap != nil {
gasTipCapVal := argBig(*t.GasTipCap)
gasTipCap = &gasTipCapVal
}

if t.GasFeeCap != nil {
gasFeeCapVal := argBig(*t.GasFeeCap)
gasFeeCap = &gasFeeCapVal
}

return &txpoolTransaction{
Nonce: argUint64(t.Nonce),
GasPrice: argBig(*t.GasPrice),
GasFeeCap: gasFeeCap,
GasTipCap: gasTipCap,
Gas: argUint64(t.Gas),
To: t.To,
Value: argBig(*t.Value),
Input: t.Input,
Hash: t.Hash,
From: t.From,
BlockHash: types.ZeroHash,
BlockNumber: nil,
TxIndex: nil,
}
}

// Create response for txpool_content request.
// See https://geth.ethereum.org/docs/rpc/ns-txpool#txpool_content.
func (t *TxPool) Content() (interface{}, error) {
pendingTxs, queuedTxs := t.store.GetTxs(true)

// collect pending
pendingRPCTxs := make(map[types.Address]map[uint64]*txpoolTransaction)
for addr, txs := range pendingTxs {
pendingRPCTxs[addr] = make(map[uint64]*txpoolTransaction, len(txs))
convertTxMap := func(txMap map[types.Address][]*types.Transaction) map[types.Address]map[uint64]*transaction {
result := make(map[types.Address]map[uint64]*transaction, len(txMap))

for _, tx := range txs {
nonce := tx.Nonce
rpcTx := toTxPoolTransaction(tx)
for addr, txs := range txMap {
result[addr] = make(map[uint64]*transaction, len(txs))

pendingRPCTxs[addr][nonce] = rpcTx
for _, tx := range txs {
result[addr][tx.Nonce] = toTransaction(tx, nil, &types.ZeroHash, nil)
}
}
}

// collect enqueued
queuedRPCTxs := make(map[types.Address]map[uint64]*txpoolTransaction)
for addr, txs := range queuedTxs {
queuedRPCTxs[addr] = make(map[uint64]*txpoolTransaction, len(txs))

for _, tx := range txs {
nonce := tx.Nonce
rpcTx := toTxPoolTransaction(tx)

queuedRPCTxs[addr][nonce] = rpcTx
}
return result
}

pendingTxs, queuedTxs := t.store.GetTxs(true)
resp := ContentResponse{
Pending: pendingRPCTxs,
Queued: queuedRPCTxs,
Pending: convertTxMap(pendingTxs),
Queued: convertTxMap(queuedTxs),
}

return resp, nil
Expand All @@ -126,40 +70,30 @@ func (t *TxPool) Content() (interface{}, error) {
// Create response for txpool_inspect request.
// See https://geth.ethereum.org/docs/rpc/ns-txpool#txpool_inspect.
func (t *TxPool) Inspect() (interface{}, error) {
pendingTxs, queuedTxs := t.store.GetTxs(true)

// collect pending
pendingRPCTxs := make(map[string]map[string]string)
for addr, txs := range pendingTxs {
pendingRPCTxs[addr.String()] = make(map[string]string, len(txs))

for _, tx := range txs {
nonceStr := strconv.FormatUint(tx.Nonce, 10)
pendingRPCTxs[addr.String()][nonceStr] = fmt.Sprintf(
"%d wei + %d gas x %d wei", tx.Value, tx.Gas, tx.GasPrice,
)
baseFee := t.store.GetBaseFee()
convertTxMap := func(txMap map[types.Address][]*types.Transaction) map[string]map[string]string {
result := make(map[string]map[string]string, len(txMap))

for addr, txs := range txMap {
result[addr.String()] = make(map[string]string, len(txs))

for _, tx := range txs {
nonceStr := strconv.FormatUint(tx.Nonce, 10)
result[addr.String()][nonceStr] = fmt.Sprintf(
"%d wei + %d gas x %d wei", tx.Value, tx.Gas, tx.GetGasPrice(baseFee),
)
}
}
}

// collect enqueued
queuedRPCTxs := make(map[string]map[string]string)
for addr, txs := range queuedTxs {
queuedRPCTxs[addr.String()] = make(map[string]string, len(txs))

for _, tx := range txs {
nonceStr := strconv.FormatUint(tx.Nonce, 10)
queuedRPCTxs[addr.String()][nonceStr] = fmt.Sprintf(
"%d wei + %d gas x %d wei", tx.Value, tx.Gas, tx.GasPrice,
)
}
return result
}

// get capacity of the TxPool
current, max := t.store.GetCapacity()

pendingTxs, queuedTxs := t.store.GetTxs(true)
resp := InspectResponse{
Pending: pendingRPCTxs,
Queued: queuedRPCTxs,
Pending: convertTxMap(pendingTxs),
Queued: convertTxMap(queuedTxs),
CurrentCapacity: current,
MaxCapacity: max,
}
Expand Down
117 changes: 90 additions & 27 deletions jsonrpc/txpool_endpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@ func TestContentEndpoint(t *testing.T) {
assert.Equal(t, 0, len(response.Queued))
})

//nolint:dupl
t.Run("returns correct data for pending transaction", func(t *testing.T) {
t.Parallel()

mockStore := newMockTxPoolStore()
address1 := types.Address{0x1}
testTx := newTestTransaction(2, address1)
mockStore.pending[address1] = []*types.Transaction{testTx}
testTx1 := newTestTransaction(2, address1)
testTx2 := newTestDynamicFeeTransaction(3, address1)
mockStore.pending[address1] = []*types.Transaction{testTx1, testTx2}
txPoolEndpoint := &TxPool{mockStore}

result, _ := txPoolEndpoint.Content()
Expand All @@ -44,48 +44,84 @@ func TestContentEndpoint(t *testing.T) {

assert.Equal(t, 1, len(response.Pending))
assert.Equal(t, 0, len(response.Queued))
assert.Equal(t, 1, len(response.Pending[address1]))
assert.Equal(t, 2, len(response.Pending[address1]))

txData := response.Pending[address1][testTx.Nonce]
txData := response.Pending[address1][testTx1.Nonce]
assert.NotNil(t, txData)
assert.Equal(t, testTx1.Gas, uint64(txData.Gas))
assert.Equal(t, *testTx1.GasPrice, big.Int(*txData.GasPrice))
assert.Equal(t, (*argBig)(nil), txData.GasFeeCap)
assert.Equal(t, (*argBig)(nil), txData.GasTipCap)
assert.Equal(t, testTx1.To, txData.To)
assert.Equal(t, testTx1.From, txData.From)
assert.Equal(t, *testTx1.Value, big.Int(txData.Value))
assert.Equal(t, testTx1.Input, []byte(txData.Input))
assert.Equal(t, (*argUint64)(nil), txData.BlockNumber)
assert.Equal(t, (*argUint64)(nil), txData.TxIndex)

txData = response.Pending[address1][testTx2.Nonce]
assert.NotNil(t, txData)
assert.Equal(t, testTx.Gas, uint64(txData.Gas))
assert.Equal(t, *testTx.GasPrice, big.Int(txData.GasPrice))
assert.Equal(t, testTx.To, txData.To)
assert.Equal(t, testTx.From, txData.From)
assert.Equal(t, *testTx.Value, big.Int(txData.Value))
assert.Equal(t, testTx.Input, []byte(txData.Input))
assert.Equal(t, nil, txData.BlockNumber)
assert.Equal(t, nil, txData.TxIndex)
assert.Equal(t, (argUint64)(types.DynamicFeeTx), txData.Type)
assert.Equal(t, testTx2.Gas, uint64(txData.Gas))
assert.Equal(t, (*argBig)(nil), txData.GasPrice)
assert.Equal(t, *testTx2.GasFeeCap, big.Int(*txData.GasFeeCap))
assert.Equal(t, *testTx2.GasTipCap, big.Int(*txData.GasTipCap))
assert.Equal(t, testTx2.To, txData.To)
assert.Equal(t, testTx2.From, txData.From)
assert.Equal(t, *testTx2.ChainID, big.Int(*txData.ChainID))
assert.Equal(t, *testTx2.Value, big.Int(txData.Value))
assert.Equal(t, testTx2.Input, []byte(txData.Input))
assert.Equal(t, (*argUint64)(nil), txData.BlockNumber)
assert.Equal(t, (*argUint64)(nil), txData.TxIndex)
})

//nolint:dupl
t.Run("returns correct data for queued transaction", func(t *testing.T) {
t.Parallel()

mockStore := newMockTxPoolStore()
address1 := types.Address{0x1}
testTx := newTestTransaction(2, address1)
mockStore.queued[address1] = []*types.Transaction{testTx}
address1, address2 := types.Address{0x1}, types.Address{0x2}
testTx1 := newTestTransaction(2, address1)
testTx2 := newTestDynamicFeeTransaction(1, address2)
mockStore.queued[address1] = []*types.Transaction{testTx1}
mockStore.queued[address2] = []*types.Transaction{testTx2}
txPoolEndpoint := &TxPool{mockStore}

result, _ := txPoolEndpoint.Content()
//nolint:forcetypeassert
response := result.(ContentResponse)

assert.Equal(t, 0, len(response.Pending))
assert.Equal(t, 1, len(response.Queued))
assert.Equal(t, 2, len(response.Queued))
assert.Equal(t, 1, len(response.Queued[address1]))
assert.Equal(t, 1, len(response.Queued[address2]))

txData := response.Queued[address1][testTx.Nonce]
txData := response.Queued[address1][testTx1.Nonce]
assert.NotNil(t, txData)
assert.Equal(t, testTx.Gas, uint64(txData.Gas))
assert.Equal(t, *testTx.GasPrice, big.Int(txData.GasPrice))
assert.Equal(t, testTx.To, txData.To)
assert.Equal(t, testTx.From, txData.From)
assert.Equal(t, *testTx.Value, big.Int(txData.Value))
assert.Equal(t, testTx.Input, []byte(txData.Input))
assert.Equal(t, nil, txData.BlockNumber)
assert.Equal(t, nil, txData.TxIndex)
assert.Equal(t, testTx1.Gas, uint64(txData.Gas))
assert.Equal(t, *testTx1.GasPrice, big.Int(*txData.GasPrice))
assert.Equal(t, (*argBig)(nil), txData.GasFeeCap)
assert.Equal(t, (*argBig)(nil), txData.GasTipCap)
assert.Equal(t, testTx1.To, txData.To)
assert.Equal(t, testTx1.From, txData.From)
assert.Equal(t, *testTx1.Value, big.Int(txData.Value))
assert.Equal(t, testTx1.Input, []byte(txData.Input))
assert.Equal(t, (*argUint64)(nil), txData.BlockNumber)
assert.Equal(t, (*argUint64)(nil), txData.TxIndex)

txData = response.Queued[address2][testTx2.Nonce]
assert.NotNil(t, txData)
assert.Equal(t, (argUint64)(types.DynamicFeeTx), txData.Type)
assert.Equal(t, testTx2.Gas, uint64(txData.Gas))
assert.Equal(t, (*argBig)(nil), txData.GasPrice)
assert.Equal(t, *testTx2.GasFeeCap, big.Int(*txData.GasFeeCap))
assert.Equal(t, *testTx2.GasTipCap, big.Int(*txData.GasTipCap))
assert.Equal(t, testTx2.To, txData.To)
assert.Equal(t, testTx2.From, txData.From)
assert.Equal(t, *testTx2.ChainID, big.Int(*txData.ChainID))
assert.Equal(t, *testTx2.Value, big.Int(txData.Value))
assert.Equal(t, testTx2.Input, []byte(txData.Input))
assert.Equal(t, (*argUint64)(nil), txData.BlockNumber)
assert.Equal(t, (*argUint64)(nil), txData.TxIndex)
})

t.Run("returns correct ContentResponse data for multiple transactions", func(t *testing.T) {
Expand Down Expand Up @@ -233,6 +269,7 @@ type mockTxPoolStore struct {
queued map[types.Address][]*types.Transaction
capacity uint64
maxSlots uint64
baseFee uint64
includeQueued bool
}

Expand All @@ -253,6 +290,10 @@ func (s *mockTxPoolStore) GetCapacity() (uint64, uint64) {
return s.capacity, s.maxSlots
}

func (s *mockTxPoolStore) GetBaseFee() uint64 {
return s.baseFee
}

func newTestTransaction(nonce uint64, from types.Address) *types.Transaction {
txn := &types.Transaction{
Nonce: nonce,
Expand All @@ -271,3 +312,25 @@ func newTestTransaction(nonce uint64, from types.Address) *types.Transaction {

return txn
}

func newTestDynamicFeeTransaction(nonce uint64, from types.Address) *types.Transaction {
txn := &types.Transaction{
Type: types.DynamicFeeTx,
Nonce: nonce,
GasTipCap: big.NewInt(2),
GasFeeCap: big.NewInt(4),
ChainID: big.NewInt(100),
Gas: nonce * 100,
Value: big.NewInt(200),
Input: []byte{0xff},
From: from,
To: &addr1,
V: big.NewInt(1),
R: big.NewInt(1),
S: big.NewInt(1),
}

txn.ComputeHash(1)

return txn
}
5 changes: 4 additions & 1 deletion txpool/txpool.go
Original file line number Diff line number Diff line change
Expand Up @@ -755,7 +755,10 @@ func (p *TxPool) addTx(origin txOrigin, tx *types.Transaction) error {
return err
}

tx.ChainID = p.chainID // add chainID to the tx
// add chainID to the tx - only dynamic fee tx
if tx.Type == types.DynamicFeeTx {
tx.ChainID = p.chainID
}

// calculate tx hash
tx.ComputeHash(p.store.Header().Number)
Expand Down

0 comments on commit 13e82b6

Please sign in to comment.