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]