This is an automated email from the ASF dual-hosted git repository.

toulmean pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-tuweni.git


The following commit(s) were added to refs/heads/master by this push:
     new c28cfe2  eth65 (#152)
c28cfe2 is described below

commit c28cfe2dc702986d64259cb3547659ca5e92a434
Author: Antoine Toulme <[email protected]>
AuthorDate: Fri Oct 9 23:28:31 2020 -0700

    eth65 (#152)
    
    * Support eth65. Add an integration test to connect to a node and send 
transactions once per second
    
    * Fix eth65 behavior, support besu dev genesis format
    
    * ignore besu-dev.json file
---
 build.gradle                                       |   2 +
 .../tuweni/devp2p/eth/ConnectToAnotherNodeTest.kt  |  10 +-
 .../devp2p/eth/SendPendingTransactionsTest.kt      | 120 +++++++++++++++++++++
 .../org/apache/tuweni/devp2p/eth/EthClient.kt      |  19 +++-
 .../org/apache/tuweni/devp2p/eth/EthController.kt  |  18 ++++
 .../org/apache/tuweni/devp2p/eth/EthHandler.kt     |  64 +++++++++++
 .../apache/tuweni/devp2p/eth/EthRequestsManager.kt |   7 ++
 .../org/apache/tuweni/devp2p/eth/EthSubprotocol.kt |  14 ++-
 .../org/apache/tuweni/devp2p/eth/Messages.kt       |  64 ++++++++++-
 .../org/apache/tuweni/devp2p/eth/EthHandlerTest.kt |   3 +-
 .../apache/tuweni/devp2p/eth/EthSubprotocolTest.kt |  14 ++-
 .../org/apache/tuweni/devp2p/eth/MessagesTest.kt   |  33 ++++++
 devp2p-eth/src/test/resources/besu-dev.json        |  41 +++++++
 .../org/apache/tuweni/ethclient/EthereumClient.kt  |   4 +-
 .../tuweni/eth/repository/TransactionPool.kt       |  41 +++++++
 .../tuweni/eth/repository/TransactionPoolTest.kt   |  54 ++++++++++
 .../org/apache/tuweni/eth/genesis/GenesisFile.java |  38 +++++--
 .../apache/tuweni/eth/genesis/GenesisFileTest.java |  39 +++++++
 eth/src/test/resources/besu-dev.json               |  41 +++++++
 .../kotlin/org/apache/tuweni/les/LESSubprotocol.kt |   6 +-
 .../apache/tuweni/les/LESSubProtocolHandlerTest.kt |  19 ++--
 .../org/apache/tuweni/les/LESSubprotocolTest.kt    |  10 +-
 22 files changed, 627 insertions(+), 34 deletions(-)

diff --git a/build.gradle b/build.gradle
index fcadc66..96f5d67 100644
--- a/build.gradle
+++ b/build.gradle
@@ -113,12 +113,14 @@ if (file('.git').exists()) {
         'package-lock.json',
         '.github/pull_request_template.md',
         'devp2p-eth/src/test/resources/mainnet.json',
+        'devp2p-eth/src/test/resources/besu-dev.json',
         'eth/src/test/resources/mainnet.json',
         'eth-client/src/main/resources/mainnet.json',
         'eth-client/src/main/resources/default.json',
         'eth/src/test/resources/missing-difficulty.json',
         'eth/src/test/resources/missing-nonce.json',
         'eth/src/test/resources/valid-genesis.json',
+        'eth/src/test/resources/besu-dev.json',
         'eth-client-app/src/main/resources/tuweni.txt',
         'evm/src/test/resources/VMTests/**'
       ])
diff --git 
a/devp2p-eth/src/integrationTest/kotlin/org/apache/tuweni/devp2p/eth/ConnectToAnotherNodeTest.kt
 
b/devp2p-eth/src/integrationTest/kotlin/org/apache/tuweni/devp2p/eth/ConnectToAnotherNodeTest.kt
index 6516936..0073e29 100644
--- 
a/devp2p-eth/src/integrationTest/kotlin/org/apache/tuweni/devp2p/eth/ConnectToAnotherNodeTest.kt
+++ 
b/devp2p-eth/src/integrationTest/kotlin/org/apache/tuweni/devp2p/eth/ConnectToAnotherNodeTest.kt
@@ -25,6 +25,7 @@ import org.apache.tuweni.crypto.SECP256K1
 import org.apache.tuweni.eth.genesis.GenesisFile
 import org.apache.tuweni.eth.repository.BlockchainIndex
 import org.apache.tuweni.eth.repository.BlockchainRepository
+import org.apache.tuweni.eth.repository.MemoryTransactionPool
 import org.apache.tuweni.junit.BouncyCastleExtension
 import org.apache.tuweni.junit.LuceneIndexWriter
 import org.apache.tuweni.junit.LuceneIndexWriterExtension
@@ -75,7 +76,8 @@ class ConnectToAnotherNodeTest {
           blockchainInfo = SimpleBlockchainInformation(
             UInt256.valueOf(genesisFile.chainId.toLong()), 
genesisBlock.header.difficulty,
             genesisBlock.header.hash, UInt256.valueOf(42L), 
genesisBlock.header.hash, genesisFile.forks
-          )
+          ),
+          pendingTransactionsPool = MemoryTransactionPool()
         )
       ),
       "Tuweni Experiment 0.1"
@@ -134,7 +136,8 @@ class ConnectToAnotherNodeTest {
             genesisBlock.header.hash, UInt256.valueOf(42L),
             genesisBlock.header.hash,
             emptyList()
-          )
+          ),
+          pendingTransactionsPool = MemoryTransactionPool()
         )
       ),
       "Tuweni Experiment 0.1"
@@ -166,7 +169,8 @@ class ConnectToAnotherNodeTest {
             genesisBlock.header.hash, UInt256.valueOf(42L),
             genesisBlock.header.hash,
             emptyList()
-          )
+          ),
+          pendingTransactionsPool = MemoryTransactionPool()
         )
       ),
       "Tuweni Experiment 0.1"
diff --git 
a/devp2p-eth/src/integrationTest/kotlin/org/apache/tuweni/devp2p/eth/SendPendingTransactionsTest.kt
 
b/devp2p-eth/src/integrationTest/kotlin/org/apache/tuweni/devp2p/eth/SendPendingTransactionsTest.kt
new file mode 100644
index 0000000..be5ab25
--- /dev/null
+++ 
b/devp2p-eth/src/integrationTest/kotlin/org/apache/tuweni/devp2p/eth/SendPendingTransactionsTest.kt
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tuweni.devp2p.eth
+
+import io.vertx.core.Vertx
+import kotlinx.coroutines.async
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.runBlocking
+import org.apache.lucene.index.IndexWriter
+import org.apache.tuweni.bytes.Bytes
+import org.apache.tuweni.concurrent.coroutines.await
+import org.apache.tuweni.crypto.SECP256K1
+import org.apache.tuweni.eth.Address
+import org.apache.tuweni.eth.Transaction
+import org.apache.tuweni.eth.genesis.GenesisFile
+import org.apache.tuweni.eth.repository.BlockchainIndex
+import org.apache.tuweni.eth.repository.BlockchainRepository
+import org.apache.tuweni.eth.repository.MemoryTransactionPool
+import org.apache.tuweni.junit.BouncyCastleExtension
+import org.apache.tuweni.junit.LuceneIndexWriter
+import org.apache.tuweni.junit.LuceneIndexWriterExtension
+import org.apache.tuweni.junit.VertxExtension
+import org.apache.tuweni.junit.VertxInstance
+import org.apache.tuweni.kv.MapKeyValueStore
+import org.apache.tuweni.rlpx.vertx.VertxRLPxService
+import org.apache.tuweni.units.bigints.UInt256
+import org.apache.tuweni.units.ethereum.Gas
+import org.apache.tuweni.units.ethereum.Wei
+import org.junit.jupiter.api.Disabled
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.extension.ExtendWith
+import java.net.InetSocketAddress
+
+/**
+ * This test sends continuously new transactions to all the peers of the 
service for 10 minutes.
+ *
+ * The test will connect to a live instance located at port 30303 on localhost.
+ */
+@Disabled
+@ExtendWith(LuceneIndexWriterExtension::class, VertxExtension::class, 
BouncyCastleExtension::class)
+class SendPendingTransactionsTest {
+
+  private val peerId = 
"b1c9e33ebfd9446151688f0abaf171dac6df31ea5205a200f2cbaf5f8be" +
+    "d241c9f93732f25109e16badea1aa657a6078240657688cbbddb91a50aa8c7c34a9cc"
+
+  @Test
+  fun testSendPendingTransactions(@LuceneIndexWriter writer: IndexWriter, 
@VertxInstance vertx: Vertx) = runBlocking {
+    val contents = 
ConnectToAnotherNodeTest::class.java.getResourceAsStream("/besu-dev.json").readAllBytes()
+    val genesisFile = GenesisFile.read(contents)
+    val genesisBlock = genesisFile.toBlock()
+
+    val repository = BlockchainRepository.init(
+      MapKeyValueStore(),
+      MapKeyValueStore(),
+      MapKeyValueStore(),
+      MapKeyValueStore(),
+      MapKeyValueStore(),
+      MapKeyValueStore(),
+      BlockchainIndex(writer),
+      genesisBlock
+    )
+    val service = VertxRLPxService(
+      vertx,
+      30304,
+      "127.0.0.1",
+      30304,
+      SECP256K1.KeyPair.random(),
+      listOf(
+        EthSubprotocol(
+          repository = repository,
+          blockchainInfo = SimpleBlockchainInformation(
+            UInt256.valueOf(genesisFile.chainId.toLong()), 
genesisBlock.header.difficulty,
+            genesisBlock.header.hash, UInt256.valueOf(42L), 
genesisBlock.header.hash, genesisFile.forks
+          ),
+          pendingTransactionsPool = MemoryTransactionPool()
+        )
+      ),
+      "Tuweni Experiment 0.1"
+    )
+    service.start().await()
+    service.connectTo(
+      SECP256K1.PublicKey.fromHexString(peerId),
+      InetSocketAddress("127.0.0.1", 30303)
+    ).await()
+
+    var loop = true
+    async {
+      delay(10 * 60 * 1000)
+      loop = false
+    }
+    val client = service.getClient(EthSubprotocol.ETH65) as EthClient
+    while (loop) {
+      val tx = Transaction(
+        UInt256.valueOf(1),
+        Wei.valueOf(2),
+        Gas.valueOf(2),
+        Address.fromBytes(Bytes.random(20)),
+        Wei.valueOf(2),
+        Bytes.random(12),
+        SECP256K1.KeyPair.random()
+      )
+      client.submitPooledTransaction(tx)
+      delay(100)
+    }
+  }
+}
diff --git 
a/devp2p-eth/src/main/kotlin/org/apache/tuweni/devp2p/eth/EthClient.kt 
b/devp2p-eth/src/main/kotlin/org/apache/tuweni/devp2p/eth/EthClient.kt
index c2c1e7e..8f60d5d 100644
--- a/devp2p-eth/src/main/kotlin/org/apache/tuweni/devp2p/eth/EthClient.kt
+++ b/devp2p-eth/src/main/kotlin/org/apache/tuweni/devp2p/eth/EthClient.kt
@@ -23,7 +23,9 @@ import org.apache.tuweni.concurrent.CompletableAsyncCompletion
 import org.apache.tuweni.eth.BlockBody
 import org.apache.tuweni.eth.BlockHeader
 import org.apache.tuweni.eth.Hash
+import org.apache.tuweni.eth.Transaction
 import org.apache.tuweni.eth.TransactionReceipt
+import org.apache.tuweni.eth.repository.TransactionPool
 import org.apache.tuweni.rlpx.RLPxService
 import org.apache.tuweni.rlpx.wire.SubProtocolClient
 import org.apache.tuweni.rlpx.wire.WireConnection
@@ -32,7 +34,8 @@ import org.apache.tuweni.units.bigints.UInt256
 /**
  * Client of the ETH subprotocol, allowing to request block and node data
  */
-class EthClient(private val service: RLPxService) : EthRequestsManager, 
SubProtocolClient {
+class EthClient(private val service: RLPxService, private val 
pendingTransactionsPool: TransactionPool) :
+  EthRequestsManager, SubProtocolClient {
 
   private val headerRequests = HashMap<Bytes32, Request>()
   private val bodiesRequests = HashMap<String, Request>()
@@ -159,4 +162,18 @@ class EthClient(private val service: RLPxService) : 
EthRequestsManager, SubProto
     connection: WireConnection,
     transactionReceipts: List<List<TransactionReceipt>>
   ): Request? = transactionReceiptRequests[connection.uri()]
+
+  override suspend fun submitPooledTransaction(vararg tx: Transaction) {
+    for (t in tx) { pendingTransactionsPool.add(t) }
+    val hashes = tx.map { it.hash }
+    val conns = service.repository().asIterable(EthSubprotocol.ETH65)
+    conns.forEach { conn ->
+      service.send(
+        EthSubprotocol.ETH65,
+        MessageType.NewPooledTransactionHashes.code,
+        conn,
+        GetBlockBodies(hashes).toBytes()
+      )
+    }
+  }
 }
diff --git 
a/devp2p-eth/src/main/kotlin/org/apache/tuweni/devp2p/eth/EthController.kt 
b/devp2p-eth/src/main/kotlin/org/apache/tuweni/devp2p/eth/EthController.kt
index 5c4f9fa..0efd68e 100644
--- a/devp2p-eth/src/main/kotlin/org/apache/tuweni/devp2p/eth/EthController.kt
+++ b/devp2p-eth/src/main/kotlin/org/apache/tuweni/devp2p/eth/EthController.kt
@@ -24,6 +24,7 @@ import org.apache.tuweni.eth.Hash
 import org.apache.tuweni.eth.Transaction
 import org.apache.tuweni.eth.TransactionReceipt
 import org.apache.tuweni.eth.repository.BlockchainRepository
+import org.apache.tuweni.eth.repository.TransactionPool
 import org.apache.tuweni.rlpx.wire.WireConnection
 
 /**
@@ -31,6 +32,7 @@ import org.apache.tuweni.rlpx.wire.WireConnection
  */
 class EthController(
   val repository: BlockchainRepository,
+  val pendingTransactionsPool: TransactionPool,
   val requestsManager: EthRequestsManager,
   val connectionsListener: (WireConnection, Status) -> Unit = { _, _ -> }
 ) {
@@ -178,4 +180,20 @@ class EthController(
       repository.storeTransaction(it)
     }
   }
+
+  suspend fun addNewPooledTransactions(transactions: List<Transaction>) {
+    for (tx in transactions) {
+      pendingTransactionsPool.add(tx)
+    }
+  }
+
+  suspend fun findPooledTransactions(hashes: List<Hash>): List<Transaction> {
+    val result = ArrayList<Transaction>()
+    for (hash in hashes) {
+      pendingTransactionsPool.get(hash)?.let {
+        result.add(it)
+      }
+    }
+    return result
+  }
 }
diff --git 
a/devp2p-eth/src/main/kotlin/org/apache/tuweni/devp2p/eth/EthHandler.kt 
b/devp2p-eth/src/main/kotlin/org/apache/tuweni/devp2p/eth/EthHandler.kt
index 938553b..53d840b 100644
--- a/devp2p-eth/src/main/kotlin/org/apache/tuweni/devp2p/eth/EthHandler.kt
+++ b/devp2p-eth/src/main/kotlin/org/apache/tuweni/devp2p/eth/EthHandler.kt
@@ -22,12 +22,15 @@ import org.apache.tuweni.bytes.Bytes
 import org.apache.tuweni.concurrent.AsyncCompletion
 import org.apache.tuweni.concurrent.CompletableAsyncCompletion
 import org.apache.tuweni.concurrent.coroutines.asyncCompletion
+import org.apache.tuweni.eth.Hash
 import org.apache.tuweni.rlpx.RLPxService
 import org.apache.tuweni.rlpx.wire.DisconnectReason
 import org.apache.tuweni.rlpx.wire.SubProtocolHandler
 import org.apache.tuweni.rlpx.wire.WireConnection
 import org.slf4j.LoggerFactory
 import java.util.WeakHashMap
+import kotlin.collections.ArrayList
+import kotlin.collections.set
 import kotlin.coroutines.CoroutineContext
 
 internal class EthHandler(
@@ -41,6 +44,8 @@ internal class EthHandler(
 
   companion object {
     val logger = LoggerFactory.getLogger(EthHandler::class.java)!!
+    val MAX_NEW_POOLED_TX_HASHES = 4096
+    val MAX_POOLED_TX = 256
   }
 
   override fun handle(connection: WireConnection, messageType: Int, message: 
Bytes) = asyncCompletion {
@@ -58,6 +63,15 @@ internal class EthHandler(
       MessageType.NodeData.code -> handleNodeData(connection, 
NodeData.read(message))
       MessageType.GetReceipts.code -> handleGetReceipts(connection, 
GetReceipts.read(message))
       MessageType.Receipts.code -> handleReceipts(connection, 
Receipts.read(message))
+      MessageType.NewPooledTransactionHashes.code -> 
handleNewPooledTransactionHashes(
+        connection,
+        NewPooledTransactionHashes.read(message)
+      )
+      MessageType.GetPooledTransactions.code -> handleGetPooledTransactions(
+        connection,
+        GetPooledTransactions.read(message)
+      )
+      MessageType.PooledTransactions.code -> 
handlePooledTransactions(PooledTransactions.read(message))
       else -> {
         logger.warn("Unknown message type {}", messageType)
         service.disconnect(connection, DisconnectReason.SUBPROTOCOL_REASON)
@@ -65,6 +79,21 @@ internal class EthHandler(
     }
   }
 
+  private suspend fun handlePooledTransactions(read: PooledTransactions) {
+    controller.addNewPooledTransactions(read.transactions)
+  }
+
+  private suspend fun handleGetPooledTransactions(connection: WireConnection, 
read: GetPooledTransactions) {
+    val tx = controller.findPooledTransactions(read.hashes)
+    logger.debug("Responding to GetPooledTransactions with {} transactions", 
tx.size)
+    service.send(
+      EthSubprotocol.ETH65,
+      MessageType.PooledTransactions.code,
+      connection,
+      PooledTransactions(tx).toBytes()
+    )
+  }
+
   private suspend fun handleTransactions(transactions: Transactions) {
     controller.addNewTransactions(transactions.transactions)
   }
@@ -88,6 +117,41 @@ internal class EthHandler(
     }
   }
 
+  private suspend fun handleNewPooledTransactionHashes(
+    connection: WireConnection,
+    newPooledTransactionHashes: NewPooledTransactionHashes
+  ) {
+    if (newPooledTransactionHashes.hashes.size > MAX_NEW_POOLED_TX_HASHES) {
+      service.disconnect(connection, DisconnectReason.SUBPROTOCOL_REASON)
+      return
+    }
+    var missingTx = ArrayList<Hash>()
+    var message = GetPooledTransactions(missingTx)
+    for (hash in newPooledTransactionHashes.hashes) {
+      if (!controller.pendingTransactionsPool.contains(hash)) {
+        missingTx.add(hash)
+      }
+      if (missingTx.size == MAX_POOLED_TX) {
+        service.send(
+          EthSubprotocol.ETH65,
+          MessageType.GetPooledTransactions.code,
+          connection,
+          message.toBytes()
+        )
+        missingTx = ArrayList()
+        message = GetPooledTransactions(missingTx)
+      }
+    }
+    if (!missingTx.isEmpty()) {
+      service.send(
+        EthSubprotocol.ETH65,
+        MessageType.GetPooledTransactions.code,
+        connection,
+        message.toBytes()
+      )
+    }
+  }
+
   private suspend fun handleReceipts(connection: WireConnection, receipts: 
Receipts) {
     controller.addNewTransactionReceipts(connection, 
receipts.transactionReceipts)
   }
diff --git 
a/devp2p-eth/src/main/kotlin/org/apache/tuweni/devp2p/eth/EthRequestsManager.kt 
b/devp2p-eth/src/main/kotlin/org/apache/tuweni/devp2p/eth/EthRequestsManager.kt
index 10b8e75..6db8649 100644
--- 
a/devp2p-eth/src/main/kotlin/org/apache/tuweni/devp2p/eth/EthRequestsManager.kt
+++ 
b/devp2p-eth/src/main/kotlin/org/apache/tuweni/devp2p/eth/EthRequestsManager.kt
@@ -22,6 +22,7 @@ import org.apache.tuweni.concurrent.CompletableAsyncCompletion
 import org.apache.tuweni.eth.BlockBody
 import org.apache.tuweni.eth.BlockHeader
 import org.apache.tuweni.eth.Hash
+import org.apache.tuweni.eth.Transaction
 import org.apache.tuweni.eth.TransactionReceipt
 import org.apache.tuweni.rlpx.wire.WireConnection
 
@@ -108,6 +109,12 @@ interface EthRequestsManager {
     connection: WireConnection,
     transactionReceipts: List<List<TransactionReceipt>>
   ): Request?
+
+  /**
+   * Submits a new pending transaction to the transaction pool to be gossiped 
to peers.
+   * @param tx a new transaction
+   */
+  suspend fun submitPooledTransaction(vararg tx: Transaction)
 }
 
 /**
diff --git 
a/devp2p-eth/src/main/kotlin/org/apache/tuweni/devp2p/eth/EthSubprotocol.kt 
b/devp2p-eth/src/main/kotlin/org/apache/tuweni/devp2p/eth/EthSubprotocol.kt
index a1ebf5d..6f03863 100644
--- a/devp2p-eth/src/main/kotlin/org/apache/tuweni/devp2p/eth/EthSubprotocol.kt
+++ b/devp2p-eth/src/main/kotlin/org/apache/tuweni/devp2p/eth/EthSubprotocol.kt
@@ -18,6 +18,7 @@ package org.apache.tuweni.devp2p.eth
 
 import kotlinx.coroutines.Dispatchers
 import org.apache.tuweni.eth.repository.BlockchainRepository
+import org.apache.tuweni.eth.repository.TransactionPool
 import org.apache.tuweni.rlpx.RLPxService
 import org.apache.tuweni.rlpx.wire.SubProtocol
 import org.apache.tuweni.rlpx.wire.SubProtocolClient
@@ -30,6 +31,7 @@ class EthSubprotocol(
   private val coroutineContext: CoroutineContext = Dispatchers.Default,
   private val blockchainInfo: BlockchainInformation,
   private val repository: BlockchainRepository,
+  private val pendingTransactionsPool: TransactionPool,
   private val listener: (WireConnection, Status) -> Unit = { _, _ -> }
 ) : SubProtocol {
 
@@ -37,12 +39,14 @@ class EthSubprotocol(
     val ETH62 = SubProtocolIdentifier.of("eth", 62)
     val ETH63 = SubProtocolIdentifier.of("eth", 63)
     val ETH64 = SubProtocolIdentifier.of("eth", 64)
+    val ETH65 = SubProtocolIdentifier.of("eth", 65)
   }
-  override fun id(): SubProtocolIdentifier = ETH64
+  override fun id(): SubProtocolIdentifier = ETH65
 
   override fun supports(subProtocolIdentifier: SubProtocolIdentifier): Boolean 
{
     return "eth".equals(subProtocolIdentifier.name()) && 
(subProtocolIdentifier.version() == ETH62.version() ||
-      subProtocolIdentifier.version() == ETH63.version() || 
subProtocolIdentifier.version() == ETH64.version())
+      subProtocolIdentifier.version() == ETH63.version() || 
subProtocolIdentifier.version() == ETH64.version() ||
+      subProtocolIdentifier.version() == ETH65.version())
   }
 
   override fun versionRange(version: Int): Int {
@@ -54,13 +58,13 @@ class EthSubprotocol(
   }
 
   override fun createHandler(service: RLPxService, client: SubProtocolClient): 
SubProtocolHandler {
-    val controller = EthController(repository, client as EthRequestsManager, 
listener)
+    val controller = EthController(repository, pendingTransactionsPool, client 
as EthRequestsManager, listener)
     return EthHandler(coroutineContext, blockchainInfo, service, controller)
   }
 
-  override fun getCapabilities() = mutableListOf(ETH62, ETH63, ETH64)
+  override fun getCapabilities() = mutableListOf(ETH62, ETH63, ETH64, ETH65)
 
   override fun createClient(service: RLPxService): SubProtocolClient {
-    return EthClient(service)
+    return EthClient(service, pendingTransactionsPool)
   }
 }
diff --git 
a/devp2p-eth/src/main/kotlin/org/apache/tuweni/devp2p/eth/Messages.kt 
b/devp2p-eth/src/main/kotlin/org/apache/tuweni/devp2p/eth/Messages.kt
index 0539bab..75b4cfd 100644
--- a/devp2p-eth/src/main/kotlin/org/apache/tuweni/devp2p/eth/Messages.kt
+++ b/devp2p-eth/src/main/kotlin/org/apache/tuweni/devp2p/eth/Messages.kt
@@ -41,7 +41,10 @@ internal enum class MessageType(val code: Int) {
   GetNodeData(0x0d),
   NodeData(0x0e),
   GetReceipts(0x0f),
-  Receipts(0x10)
+  Receipts(0x10),
+  NewPooledTransactionHashes(0x08),
+  GetPooledTransactions(0x09),
+  PooledTransactions(0x0a)
 }
 
 internal data class StatusMessage(
@@ -333,3 +336,62 @@ internal data class Transactions(val transactions: 
List<Transaction>) {
     }
   }
 }
+
+internal data class NewPooledTransactionHashes(val hashes: List<Hash>) {
+  companion object {
+
+    fun read(payload: Bytes): NewPooledTransactionHashes = 
RLP.decodeList(payload) {
+      val hashes = ArrayList<Hash>()
+      while (!it.isComplete) {
+        val tx = Hash.fromBytes(it.readValue())
+        hashes.add(tx)
+      }
+      NewPooledTransactionHashes(hashes)
+    }
+  }
+
+  fun toBytes(): Bytes = RLP.encodeList { writer ->
+    hashes.forEach {
+      writer.writeValue(it)
+    }
+  }
+}
+internal data class GetPooledTransactions(val hashes: List<Hash>) {
+  companion object {
+
+    fun read(payload: Bytes): GetPooledTransactions = RLP.decodeList(payload) {
+      val hashes = ArrayList<Hash>()
+      while (!it.isComplete) {
+        val tx = Hash.fromBytes(it.readValue())
+        hashes.add(tx)
+      }
+      GetPooledTransactions(hashes)
+    }
+  }
+
+  fun toBytes(): Bytes = RLP.encodeList { writer ->
+    hashes.forEach {
+      writer.writeValue(it)
+    }
+  }
+}
+
+internal data class PooledTransactions(val transactions: List<Transaction>) {
+  companion object {
+
+    fun read(payload: Bytes): PooledTransactions = RLP.decodeList(payload) {
+      val transactions = ArrayList<Transaction>()
+      while (!it.isComplete) {
+        val tx = it.readList(Transaction::readFrom)
+        transactions.add(tx)
+      }
+      PooledTransactions(transactions)
+    }
+  }
+
+  fun toBytes(): Bytes = RLP.encodeList { writer ->
+    transactions.forEach {
+      writer.writeList(it::writeTo)
+    }
+  }
+}
diff --git 
a/devp2p-eth/src/test/kotlin/org/apache/tuweni/devp2p/eth/EthHandlerTest.kt 
b/devp2p-eth/src/test/kotlin/org/apache/tuweni/devp2p/eth/EthHandlerTest.kt
index b14a9ca..5d6b817 100644
--- a/devp2p-eth/src/test/kotlin/org/apache/tuweni/devp2p/eth/EthHandlerTest.kt
+++ b/devp2p-eth/src/test/kotlin/org/apache/tuweni/devp2p/eth/EthHandlerTest.kt
@@ -33,6 +33,7 @@ import org.apache.tuweni.eth.Transaction
 import org.apache.tuweni.eth.TransactionReceipt
 import org.apache.tuweni.eth.repository.BlockchainIndex
 import org.apache.tuweni.eth.repository.BlockchainRepository
+import org.apache.tuweni.eth.repository.MemoryTransactionPool
 import org.apache.tuweni.junit.BouncyCastleExtension
 import org.apache.tuweni.junit.LuceneIndexWriter
 import org.apache.tuweni.junit.LuceneIndexWriterExtension
@@ -122,7 +123,7 @@ class EthHandlerTest {
           emptyList()
         ),
         service = service,
-        controller = EthController(repository, requestsManager)
+        controller = EthController(repository, MemoryTransactionPool(), 
requestsManager)
       )
 
       for (i in 1..10) {
diff --git 
a/devp2p-eth/src/test/kotlin/org/apache/tuweni/devp2p/eth/EthSubprotocolTest.kt 
b/devp2p-eth/src/test/kotlin/org/apache/tuweni/devp2p/eth/EthSubprotocolTest.kt
index 444f1fd..37c3082 100644
--- 
a/devp2p-eth/src/test/kotlin/org/apache/tuweni/devp2p/eth/EthSubprotocolTest.kt
+++ 
b/devp2p-eth/src/test/kotlin/org/apache/tuweni/devp2p/eth/EthSubprotocolTest.kt
@@ -21,6 +21,7 @@ import org.apache.tuweni.bytes.Bytes32
 import org.apache.tuweni.eth.Hash
 import org.apache.tuweni.eth.repository.BlockchainIndex
 import org.apache.tuweni.eth.repository.BlockchainRepository
+import org.apache.tuweni.eth.repository.MemoryTransactionPool
 import org.apache.tuweni.junit.BouncyCastleExtension
 import org.apache.tuweni.junit.LuceneIndexWriter
 import org.apache.tuweni.junit.LuceneIndexWriterExtension
@@ -58,9 +59,10 @@ class EthSubprotocolTest {
     )
     val eth = EthSubprotocol(
       blockchainInfo = blockchainInfo,
-      repository = repository
+      repository = repository,
+      pendingTransactionsPool = MemoryTransactionPool()
     )
-    assertEquals(SubProtocolIdentifier.of("eth", 64), eth.id())
+    assertEquals(SubProtocolIdentifier.of("eth", 65), eth.id())
   }
 
   @Test
@@ -76,8 +78,10 @@ class EthSubprotocolTest {
     )
     val eth = EthSubprotocol(
       blockchainInfo = blockchainInfo,
-      repository = repository
+      repository = repository,
+      pendingTransactionsPool = MemoryTransactionPool()
     )
+    assertTrue(eth.supports(SubProtocolIdentifier.of("eth", 65)))
     assertTrue(eth.supports(SubProtocolIdentifier.of("eth", 64)))
     assertTrue(eth.supports(SubProtocolIdentifier.of("eth", 63)))
     assertTrue(eth.supports(SubProtocolIdentifier.of("eth", 62)))
@@ -98,10 +102,12 @@ class EthSubprotocolTest {
     )
     val eth = EthSubprotocol(
       blockchainInfo = blockchainInfo,
-      repository = repository
+      repository = repository,
+      pendingTransactionsPool = MemoryTransactionPool()
     )
     assertEquals(8, eth.versionRange(62))
     assertEquals(17, eth.versionRange(63))
     assertEquals(17, eth.versionRange(64))
+    assertEquals(17, eth.versionRange(65))
   }
 }
diff --git 
a/devp2p-eth/src/test/kotlin/org/apache/tuweni/devp2p/eth/MessagesTest.kt 
b/devp2p-eth/src/test/kotlin/org/apache/tuweni/devp2p/eth/MessagesTest.kt
index 85851cb..62a649a 100644
--- a/devp2p-eth/src/test/kotlin/org/apache/tuweni/devp2p/eth/MessagesTest.kt
+++ b/devp2p-eth/src/test/kotlin/org/apache/tuweni/devp2p/eth/MessagesTest.kt
@@ -231,4 +231,37 @@ class MessagesTest {
     val read = NodeData.read(nodeData.toBytes())
     assertEquals(nodeData, read)
   }
+
+  @Test
+  fun newPooledTransactionHashesRoundTrip() {
+    val newPooledTransactionHashes = 
NewPooledTransactionHashes(listOf(Hash.fromBytes(Bytes32.random())))
+    val read = 
NewPooledTransactionHashes.read(newPooledTransactionHashes.toBytes())
+    assertEquals(newPooledTransactionHashes, read)
+  }
+
+  @Test
+  fun getPooledTransactionsRoundTrip() {
+    val getPooledTransactions = 
GetPooledTransactions(listOf(Hash.fromBytes(Bytes32.random())))
+    val read = GetPooledTransactions.read(getPooledTransactions.toBytes())
+    assertEquals(getPooledTransactions, read)
+  }
+
+  @Test
+  fun pooledTransactionsRoundTrip() {
+    val pooledTransactions = PooledTransactions(
+      listOf(
+        Transaction(
+          UInt256.ZERO,
+          Wei.valueOf(20),
+          Gas.valueOf(20),
+          Address.fromBytes(Bytes.random(20)),
+          Wei.valueOf(20),
+          Bytes.fromHexString("deadbeef"),
+          SECP256K1.KeyPair.random(),
+          1
+        )
+      ))
+    val read = PooledTransactions.read(pooledTransactions.toBytes())
+    assertEquals(pooledTransactions, read)
+  }
 }
diff --git a/devp2p-eth/src/test/resources/besu-dev.json 
b/devp2p-eth/src/test/resources/besu-dev.json
new file mode 100644
index 0000000..1e670f9
--- /dev/null
+++ b/devp2p-eth/src/test/resources/besu-dev.json
@@ -0,0 +1,41 @@
+{
+  "config": {
+    "chainId": 2018,
+    "homesteadBlock": 0,
+    "daoForkBlock": 0,
+    "eip150Block": 0,
+    "eip155Block": 0,
+    "eip158Block": 0,
+    "byzantiumBlock": 0,
+    "constantinopleBlock": 0,
+    "petersburgBlock": 0,
+    "contractSizeLimit": 2147483647,
+    "ethash": {
+      "fixeddifficulty": 100
+    }
+  },
+  "nonce": "0x42",
+  "timestamp": "0x0",
+  "extraData": 
"0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa",
+  "gasLimit": "0x1fffffffffffff",
+  "difficulty": "0x10000",
+  "mixHash": 
"0x0000000000000000000000000000000000000000000000000000000000000000",
+  "coinbase": "0x0000000000000000000000000000000000000000",
+  "alloc": {
+    "fe3b557e8fb62b89f4916b721be55ceb828dbd73": {
+      "privateKey": 
"8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63",
+      "comment": "private key and this comment are ignored.  In a real chain, 
the private key should NOT be stored",
+      "balance": "0xad78ebc5ac6200000"
+    },
+    "627306090abaB3A6e1400e9345bC60c78a8BEf57": {
+      "privateKey": 
"c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3",
+      "comment": "private key and this comment are ignored.  In a real chain, 
the private key should NOT be stored",
+      "balance": "90000000000000000000000"
+    },
+    "f17f52151EbEF6C7334FAD080c5704D77216b732": {
+      "privateKey": 
"ae6ae8e5ccbfb04590405997ee2d52d2b330726137b875053c36d94e974d162f",
+      "comment": "private key and this comment are ignored.  In a real chain, 
the private key should NOT be stored",
+      "balance": "90000000000000000000000"
+    }
+  }
+}
\ No newline at end of file
diff --git 
a/eth-client/src/main/kotlin/org/apache/tuweni/ethclient/EthereumClient.kt 
b/eth-client/src/main/kotlin/org/apache/tuweni/ethclient/EthereumClient.kt
index b343f6d..68599c6 100644
--- a/eth-client/src/main/kotlin/org/apache/tuweni/ethclient/EthereumClient.kt
+++ b/eth-client/src/main/kotlin/org/apache/tuweni/ethclient/EthereumClient.kt
@@ -32,6 +32,7 @@ import 
org.apache.tuweni.devp2p.eth.SimpleBlockchainInformation
 import org.apache.tuweni.eth.genesis.GenesisFile
 import org.apache.tuweni.eth.repository.BlockchainIndex
 import org.apache.tuweni.eth.repository.BlockchainRepository
+import org.apache.tuweni.eth.repository.MemoryTransactionPool
 import org.apache.tuweni.kv.LevelDBKeyValueStore
 import org.apache.tuweni.kv.MapKeyValueStore
 import org.apache.tuweni.peer.repository.PeerRepository
@@ -130,7 +131,8 @@ class EthereumClient(
             blockchainInfo = SimpleBlockchainInformation(
               UInt256.valueOf(genesisFile!!.chainId.toLong()), 
genesisBlock.header.difficulty,
               genesisBlock.header.hash, genesisBlock.header.number, 
genesisBlock.header.hash, genesisFile.forks
-            )
+            ),
+            pendingTransactionsPool = MemoryTransactionPool()
           )
         ),
         rlpxConfig.clientName(),
diff --git 
a/eth-repository/src/main/kotlin/org/apache/tuweni/eth/repository/TransactionPool.kt
 
b/eth-repository/src/main/kotlin/org/apache/tuweni/eth/repository/TransactionPool.kt
new file mode 100644
index 0000000..37f0dd4
--- /dev/null
+++ 
b/eth-repository/src/main/kotlin/org/apache/tuweni/eth/repository/TransactionPool.kt
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tuweni.eth.repository
+
+import org.apache.tuweni.concurrent.ExpiringMap
+import org.apache.tuweni.eth.Hash
+import org.apache.tuweni.eth.Transaction
+
+interface TransactionPool {
+
+  suspend fun contains(hash: Hash): Boolean
+  suspend fun get(hash: Hash): Transaction?
+  suspend fun add(transaction: Transaction)
+}
+
+class MemoryTransactionPool : TransactionPool {
+
+  val storage = ExpiringMap<Hash, Transaction>()
+
+  override suspend fun contains(hash: Hash): Boolean = 
storage.containsKey(hash)
+
+  override suspend fun get(hash: Hash) = storage.get(hash)
+
+  override suspend fun add(transaction: Transaction) {
+    storage.put(transaction.getHash(), transaction)
+  }
+}
diff --git 
a/eth-repository/src/test/kotlin/org/apache/tuweni/eth/repository/TransactionPoolTest.kt
 
b/eth-repository/src/test/kotlin/org/apache/tuweni/eth/repository/TransactionPoolTest.kt
new file mode 100644
index 0000000..4ba3e95
--- /dev/null
+++ 
b/eth-repository/src/test/kotlin/org/apache/tuweni/eth/repository/TransactionPoolTest.kt
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tuweni.eth.repository
+
+import kotlinx.coroutines.runBlocking
+import org.apache.tuweni.bytes.Bytes
+import org.apache.tuweni.crypto.SECP256K1
+import org.apache.tuweni.eth.Address
+import org.apache.tuweni.eth.Transaction
+import org.apache.tuweni.junit.BouncyCastleExtension
+import org.apache.tuweni.units.bigints.UInt256
+import org.apache.tuweni.units.ethereum.Gas
+import org.apache.tuweni.units.ethereum.Wei
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Assertions.assertFalse
+import org.junit.jupiter.api.Assertions.assertTrue
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.extension.ExtendWith
+
+@ExtendWith(BouncyCastleExtension::class)
+class TransactionPoolTest {
+
+  @Test
+  fun testContains() = runBlocking {
+    val tx = Transaction(
+      UInt256.valueOf(1),
+      Wei.valueOf(2),
+      Gas.valueOf(2),
+      Address.fromBytes(Bytes.random(20)),
+      Wei.valueOf(2),
+      Bytes.random(12),
+      SECP256K1.KeyPair.random()
+    )
+    val transactionPool = MemoryTransactionPool()
+    assertFalse(transactionPool.contains(tx.hash))
+    transactionPool.add(tx)
+    assertTrue(transactionPool.contains(tx.hash))
+    assertEquals(tx, transactionPool.get(tx.hash))
+  }
+}
diff --git a/eth/src/main/java/org/apache/tuweni/eth/genesis/GenesisFile.java 
b/eth/src/main/java/org/apache/tuweni/eth/genesis/GenesisFile.java
index f781e8b..a49b474 100644
--- a/eth/src/main/java/org/apache/tuweni/eth/genesis/GenesisFile.java
+++ b/eth/src/main/java/org/apache/tuweni/eth/genesis/GenesisFile.java
@@ -28,6 +28,7 @@ import org.apache.tuweni.units.ethereum.Gas;
 import org.apache.tuweni.units.ethereum.Wei;
 
 import java.io.IOException;
+import java.math.BigInteger;
 import java.time.Instant;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -103,7 +104,27 @@ public class GenesisFile {
     this.gasLimit = Gas.valueOf(Bytes.fromHexString(gasLimit).toLong());
     this.allocs = new HashMap<>();
     for (Map.Entry<String, String> entry : allocs.entrySet()) {
-      this.allocs.put(Address.fromHexString(entry.getKey()), 
Wei.valueOf(UInt256.fromHexString(entry.getValue())));
+      Address addr = null;
+      try {
+        addr = Address.fromHexString(entry.getKey());
+      } catch (IllegalArgumentException e) {
+        throw new IllegalArgumentException("Invalid address " + 
entry.getKey(), e);
+      }
+      Wei value = null;
+      if (entry.getValue().startsWith("0x")) {
+        try {
+          value = Wei.valueOf(UInt256.fromHexString(entry.getValue()));
+        } catch (IllegalArgumentException e) {
+          throw new IllegalArgumentException("Invalid balance " + 
entry.getValue(), e);
+        }
+      } else {
+        try {
+          value = Wei.valueOf(UInt256.valueOf(new 
BigInteger(entry.getValue())));
+        } catch (IllegalArgumentException e) {
+          throw new IllegalArgumentException("Invalid balance " + 
entry.getValue(), e);
+        }
+      }
+      this.allocs.put(addr, value);
     }
     this.chainId = chainId;
     this.forks = forks;
@@ -176,25 +197,28 @@ public class GenesisFile {
     Map<String, String> allocs = new HashMap<>();
     String name = null;
     String value = null;
-    boolean closed = false;
+    int depth = 1;
     while (!parser.isClosed()) {
       JsonToken jsonToken = parser.nextToken();
       if (JsonToken.FIELD_NAME.equals(jsonToken)) {
-        closed = false;
         String fieldName = parser.getCurrentName();
         if ("balance".equals(fieldName)) {
           parser.nextToken();
           value = parser.getValueAsString();
           allocs.put(name, value);
+          name = null;
         } else {
-          name = parser.getValueAsString();
+          if (depth == 1) {
+            name = parser.getValueAsString();
+          }
         }
       } else if (JsonToken.END_OBJECT.equals(jsonToken)) {
-        if (closed) {
+        depth--;
+        if (depth == 0) {
           return allocs;
-        } else {
-          closed = true;
         }
+      } else if (JsonToken.START_OBJECT.equals(jsonToken)) {
+        depth++;
       }
     }
     return allocs;
diff --git 
a/eth/src/test/java/org/apache/tuweni/eth/genesis/GenesisFileTest.java 
b/eth/src/test/java/org/apache/tuweni/eth/genesis/GenesisFileTest.java
index a693701..e618e57 100644
--- a/eth/src/test/java/org/apache/tuweni/eth/genesis/GenesisFileTest.java
+++ b/eth/src/test/java/org/apache/tuweni/eth/genesis/GenesisFileTest.java
@@ -16,9 +16,13 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
+import org.apache.tuweni.bytes.Bytes;
 import org.apache.tuweni.eth.Address;
 import org.apache.tuweni.eth.Hash;
 import org.apache.tuweni.junit.BouncyCastleExtension;
+import org.apache.tuweni.units.bigints.UInt256;
+import org.apache.tuweni.units.bigints.UInt64;
+import org.apache.tuweni.units.ethereum.Gas;
 import org.apache.tuweni.units.ethereum.Wei;
 
 import java.io.IOException;
@@ -72,4 +76,39 @@ class GenesisFileTest {
     assertThrows(IllegalArgumentException.class, () -> 
GenesisFile.read(contents));
   }
 
+  @Test
+  void testBesuDev() throws IOException {
+    InputStream input = 
GenesisFileTest.class.getResourceAsStream("/besu-dev.json");
+    byte[] contents = input.readAllBytes();
+    GenesisFile file = GenesisFile.read(contents);
+    assertEquals(3, file.getAllocations().size());
+    assertEquals(UInt64.valueOf(66L), file.toBlock().getHeader().getNonce());
+    assertEquals(
+        Address.fromHexString("0x0000000000000000000000000000000000000000"),
+        file.toBlock().getHeader().getCoinbase());
+    assertEquals(
+        
Hash.fromHexString("0x0000000000000000000000000000000000000000000000000000000000000000"),
+        file.toBlock().getHeader().getMixHash());
+    assertEquals(Gas.valueOf(UInt256.fromHexString("0x1fffffffffffff")), 
file.toBlock().getHeader().getGasLimit());
+    assertEquals(UInt256.fromHexString("0x10000"), 
file.toBlock().getHeader().getDifficulty());
+    assertEquals(
+        
Bytes.fromHexString("0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa"),
+        file.toBlock().getHeader().getExtraData());
+    assertEquals(
+        
file.getAllocations().get(Address.fromHexString("fe3b557e8fb62b89f4916b721be55ceb828dbd73")),
+        Wei.valueOf(UInt256.fromHexString("0xad78ebc5ac6200000")));
+    assertEquals(
+        
file.getAllocations().get(Address.fromHexString("627306090abaB3A6e1400e9345bC60c78a8BEf57")),
+        Wei.valueOf(new BigInteger("90000000000000000000000")));
+    assertEquals(
+        
file.getAllocations().get(Address.fromHexString("f17f52151EbEF6C7334FAD080c5704D77216b732")),
+        Wei.valueOf(new BigInteger("90000000000000000000000")));
+    assertEquals(
+        
Hash.fromHexString("0x166ed98eea93ab2b6f6b1a425526994adc2d675bf9a0d77d600ed1e02d8f77df"),
+        file.toBlock().getHeader().getStateRoot());
+    assertEquals(
+        
Hash.fromHexString("0xa08d1edb37ba1c62db764ef7c2566cbe368b850f5b3762c6c24114a3fd97b87f"),
+        file.toBlock().getHeader().getHash());
+  }
+
 }
diff --git a/eth/src/test/resources/besu-dev.json 
b/eth/src/test/resources/besu-dev.json
new file mode 100644
index 0000000..1e670f9
--- /dev/null
+++ b/eth/src/test/resources/besu-dev.json
@@ -0,0 +1,41 @@
+{
+  "config": {
+    "chainId": 2018,
+    "homesteadBlock": 0,
+    "daoForkBlock": 0,
+    "eip150Block": 0,
+    "eip155Block": 0,
+    "eip158Block": 0,
+    "byzantiumBlock": 0,
+    "constantinopleBlock": 0,
+    "petersburgBlock": 0,
+    "contractSizeLimit": 2147483647,
+    "ethash": {
+      "fixeddifficulty": 100
+    }
+  },
+  "nonce": "0x42",
+  "timestamp": "0x0",
+  "extraData": 
"0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa",
+  "gasLimit": "0x1fffffffffffff",
+  "difficulty": "0x10000",
+  "mixHash": 
"0x0000000000000000000000000000000000000000000000000000000000000000",
+  "coinbase": "0x0000000000000000000000000000000000000000",
+  "alloc": {
+    "fe3b557e8fb62b89f4916b721be55ceb828dbd73": {
+      "privateKey": 
"8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63",
+      "comment": "private key and this comment are ignored.  In a real chain, 
the private key should NOT be stored",
+      "balance": "0xad78ebc5ac6200000"
+    },
+    "627306090abaB3A6e1400e9345bC60c78a8BEf57": {
+      "privateKey": 
"c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3",
+      "comment": "private key and this comment are ignored.  In a real chain, 
the private key should NOT be stored",
+      "balance": "90000000000000000000000"
+    },
+    "f17f52151EbEF6C7334FAD080c5704D77216b732": {
+      "privateKey": 
"ae6ae8e5ccbfb04590405997ee2d52d2b330726137b875053c36d94e974d162f",
+      "comment": "private key and this comment are ignored.  In a real chain, 
the private key should NOT be stored",
+      "balance": "90000000000000000000000"
+    }
+  }
+}
\ No newline at end of file
diff --git a/les/src/main/kotlin/org/apache/tuweni/les/LESSubprotocol.kt 
b/les/src/main/kotlin/org/apache/tuweni/les/LESSubprotocol.kt
index 88b3120..c38d4af 100644
--- a/les/src/main/kotlin/org/apache/tuweni/les/LESSubprotocol.kt
+++ b/les/src/main/kotlin/org/apache/tuweni/les/LESSubprotocol.kt
@@ -23,6 +23,7 @@ import org.apache.tuweni.devp2p.eth.EthController
 import org.apache.tuweni.devp2p.eth.EthRequestsManager
 import org.apache.tuweni.devp2p.eth.Status
 import org.apache.tuweni.eth.repository.BlockchainRepository
+import org.apache.tuweni.eth.repository.TransactionPool
 import org.apache.tuweni.rlpx.RLPxService
 import org.apache.tuweni.rlpx.wire.SubProtocol
 import org.apache.tuweni.rlpx.wire.SubProtocolClient
@@ -61,6 +62,7 @@ class LESSubprotocol(
   private val flowControlMaximumRequestCostTable: UInt256,
   private val flowControlMinimumRateOfRecharge: UInt256,
   private val repo: BlockchainRepository,
+  private val pendingTransactionsPool: TransactionPool,
   private val listener: (WireConnection, Status) -> Unit = { _, _ -> }
 ) : SubProtocol {
 
@@ -71,7 +73,7 @@ class LESSubprotocol(
    * @return a new client for the subprotocol, bound to the service.
    */
   override fun createClient(service: RLPxService): SubProtocolClient {
-    return EthClient(service)
+    return EthClient(service, pendingTransactionsPool)
   }
 
   /**
@@ -107,7 +109,7 @@ class LESSubprotocol(
    * @return a new handler for the subprotocol, bound to the service.
    */
   override fun createHandler(service: RLPxService, client: SubProtocolClient): 
SubProtocolHandler {
-    val controller = EthController(repo, client as EthRequestsManager, 
listener)
+    val controller = EthController(repo, pendingTransactionsPool, client as 
EthRequestsManager, listener)
     return LESSubProtocolHandler(
       service,
       LES_ID,
diff --git 
a/les/src/test/kotlin/org/apache/tuweni/les/LESSubProtocolHandlerTest.kt 
b/les/src/test/kotlin/org/apache/tuweni/les/LESSubProtocolHandlerTest.kt
index 7f26b6b..124f9ea 100644
--- a/les/src/test/kotlin/org/apache/tuweni/les/LESSubProtocolHandlerTest.kt
+++ b/les/src/test/kotlin/org/apache/tuweni/les/LESSubProtocolHandlerTest.kt
@@ -37,6 +37,7 @@ import org.apache.tuweni.eth.Hash
 import org.apache.tuweni.eth.Transaction
 import org.apache.tuweni.eth.repository.BlockchainIndex
 import org.apache.tuweni.eth.repository.BlockchainRepository
+import org.apache.tuweni.eth.repository.MemoryTransactionPool
 import org.apache.tuweni.junit.BouncyCastleExtension
 import org.apache.tuweni.junit.LuceneIndexWriter
 import org.apache.tuweni.junit.LuceneIndexWriterExtension
@@ -182,7 +183,8 @@ constructor() {
           BlockchainIndex(writer),
           block
         )
-      val controller = EthController(repo, EthClient(service))
+      val pool = MemoryTransactionPool()
+      val controller = EthController(repo, pool, EthClient(service, pool))
 
       val handler = LESSubProtocolHandler(
         service,
@@ -237,7 +239,8 @@ constructor() {
           BlockchainIndex(writer),
           block
         )
-      val controller = EthController(repo, EthClient(service))
+      val pool = MemoryTransactionPool()
+      val controller = EthController(repo, pool, EthClient(service, pool))
 
       val handler = LESSubProtocolHandler(
         service,
@@ -276,7 +279,8 @@ constructor() {
       MapKeyValueStore(),
       BlockchainIndex(writer)
     )
-    val controller = EthController(repo, EthClient(service))
+    val pool = MemoryTransactionPool()
+    val controller = EthController(repo, pool, EthClient(service, pool))
 
     val handler = LESSubProtocolHandler(
       service,
@@ -316,7 +320,8 @@ constructor() {
           BlockchainIndex(writer),
           block
         )
-      val controller = EthController(repo, EthClient(service))
+      val pool = MemoryTransactionPool()
+      val controller = EthController(repo, pool, EthClient(service, pool))
       val handler = LESSubProtocolHandler(
         service,
         LES_ID,
@@ -384,7 +389,8 @@ constructor() {
           BlockchainIndex(writer),
           block
         )
-      val controller = EthController(repo, EthClient(service))
+      val pool = MemoryTransactionPool()
+      val controller = EthController(repo, pool, EthClient(service, pool))
       val handler = LESSubProtocolHandler(
         service,
         LES_ID,
@@ -440,7 +446,8 @@ constructor() {
           BlockchainIndex(writer),
           block
         )
-      val controller = EthController(repo, EthClient(service))
+      val pool = MemoryTransactionPool()
+      val controller = EthController(repo, pool, EthClient(service, pool))
 
       val handler = LESSubProtocolHandler(
         service,
diff --git a/les/src/test/kotlin/org/apache/tuweni/les/LESSubprotocolTest.kt 
b/les/src/test/kotlin/org/apache/tuweni/les/LESSubprotocolTest.kt
index 2df848d..cef9841 100644
--- a/les/src/test/kotlin/org/apache/tuweni/les/LESSubprotocolTest.kt
+++ b/les/src/test/kotlin/org/apache/tuweni/les/LESSubprotocolTest.kt
@@ -23,6 +23,7 @@ import 
org.apache.tuweni.devp2p.eth.SimpleBlockchainInformation
 import org.apache.tuweni.eth.Hash
 import org.apache.tuweni.eth.repository.BlockchainIndex
 import org.apache.tuweni.eth.repository.BlockchainRepository
+import org.apache.tuweni.eth.repository.MemoryTransactionPool
 import org.apache.tuweni.junit.LuceneIndexWriter
 import org.apache.tuweni.junit.LuceneIndexWriterExtension
 import org.apache.tuweni.junit.TempDirectoryExtension
@@ -68,7 +69,8 @@ internal class LESSubprotocolTest {
         MapKeyValueStore(),
         MapKeyValueStore(),
         BlockchainIndex(writer)
-      )
+      ),
+      MemoryTransactionPool()
     )
     assertTrue(sp.supports(SubProtocolIdentifier.of("les", 2)))
   }
@@ -94,7 +96,8 @@ internal class LESSubprotocolTest {
         MapKeyValueStore(),
         MapKeyValueStore(),
         BlockchainIndex(writer)
-      )
+      ),
+      MemoryTransactionPool()
     )
     assertFalse(sp.supports(SubProtocolIdentifier.of("les", 3)))
   }
@@ -119,7 +122,8 @@ internal class LESSubprotocolTest {
         MapKeyValueStore(),
         MapKeyValueStore(),
         BlockchainIndex(writer)
-      )
+      ),
+      MemoryTransactionPool()
     )
     assertFalse(sp.supports(SubProtocolIdentifier.of("eth", 2)))
   }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to