You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tuweni.apache.org by to...@apache.org on 2020/06/01 06:03:18 UTC
[incubator-tuweni] 02/02: Add tests for getting headers
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
commit 42cf5ad1cc353499bd04a9d1200006805eeb6e3f
Author: Antoine Toulme <an...@lunar-ocean.com>
AuthorDate: Sun May 31 23:03:00 2020 -0700
Add tests for getting headers
---
dependency-versions.gradle | 2 +
devp2p-eth/build.gradle | 1 +
.../org/apache/tuweni/devp2p/eth/EthHandler.kt | 39 +++---
.../org/apache/tuweni/devp2p/eth/Messages.kt | 8 +-
.../org/apache/tuweni/devp2p/eth/EthHandlerTest.kt | 138 +++++++++++++++++++--
.../tuweni/eth/repository/BlockchainIndex.kt | 4 +-
.../tuweni/eth/repository/BlockchainRepository.kt | 9 +-
7 files changed, 161 insertions(+), 40 deletions(-)
diff --git a/dependency-versions.gradle b/dependency-versions.gradle
index bbe202d..f7f75f8 100644
--- a/dependency-versions.gradle
+++ b/dependency-versions.gradle
@@ -66,6 +66,8 @@ dependencyManagement {
}
dependency('org.mapdb:mapdb:3.0.7')
dependency('org.miracl.milagro.amcl:milagro-crypto-java:0.4.0')
+ dependency('org.mockito:mockito-junit-jupiter:3.3.3')
+
dependency('org.rocksdb:rocksdbjni:5.17.2')
dependency('org.slf4j:slf4j-api:1.7.30')
diff --git a/devp2p-eth/build.gradle b/devp2p-eth/build.gradle
index ffd74ac..fb31487 100644
--- a/devp2p-eth/build.gradle
+++ b/devp2p-eth/build.gradle
@@ -33,6 +33,7 @@ dependencies {
testCompile 'org.bouncycastle:bcprov-jdk15on'
testCompile 'org.junit.jupiter:junit-jupiter-api'
testCompile 'org.junit.jupiter:junit-jupiter-params'
+ testCompile 'org.mockito:mockito-junit-jupiter'
testRuntime 'org.junit.jupiter:junit-jupiter-engine'
testRuntime 'ch.qos.logback:logback-classic'
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 8293270..ae16b05 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
@@ -130,30 +130,29 @@ internal class EthHandler(
}
private suspend fun handleGetBlockHeaders(connectionId: String, blockHeaderRequest: GetBlockHeaders) {
- val matches = repository.findBlockByHashOrNumber(blockHeaderRequest.hash)
- if (matches.isEmpty()) {
- return
- }
+ val matches = repository.findBlockByHashOrNumber(blockHeaderRequest.block)
val headers = ArrayList<BlockHeader>()
- val header = repository.retrieveBlockHeader(matches[0])
- header?.let {
- headers.add(it)
- var blockNumber = it.number
- for (i in 1..blockHeaderRequest.maxHeaders) {
- blockNumber = if (blockHeaderRequest.reverse) {
- blockNumber.subtract(blockHeaderRequest.skip)
- } else {
- blockNumber.add(blockHeaderRequest.skip)
- }
- val nextMatches = repository.findBlockByHashOrNumber(blockNumber.toBytes())
- if (nextMatches.isEmpty()) {
- break
+ if (matches.isNotEmpty()) {
+ val header = repository.retrieveBlockHeader(matches[0])
+ header?.let {
+ headers.add(it)
+ var blockNumber = it.number
+ for (i in 2..blockHeaderRequest.maxHeaders) {
+ blockNumber = if (blockHeaderRequest.reverse) {
+ blockNumber.subtract(blockHeaderRequest.skip + 1)
+ } else {
+ blockNumber.add(blockHeaderRequest.skip + 1)
+ }
+ val nextMatches = repository.findBlockByHashOrNumber(blockNumber.toBytes())
+ if (nextMatches.isEmpty()) {
+ break
+ }
+ val nextHeader = repository.retrieveBlockHeader(nextMatches[0]) ?: break
+ headers.add(nextHeader)
}
- val nextHeader = repository.retrieveBlockHeader(nextMatches[0]) ?: break
- headers.add(nextHeader)
}
- service.send(EthSubprotocol.ETH64, MessageType.BlockHeaders.code, connectionId, BlockHeaders(headers).toBytes())
}
+ service.send(EthSubprotocol.ETH64, MessageType.BlockHeaders.code, connectionId, BlockHeaders(headers).toBytes())
}
private suspend fun handleNewBlockHashes(message: NewBlockHashes) {
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 c3ea031..356b043 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
@@ -112,20 +112,20 @@ data class NewBlockHashes(val hashes: List<Pair<Hash, Long>>) {
}
}
-data class GetBlockHeaders(val hash: Hash, val maxHeaders: Long, val skip: Long, val reverse: Boolean) {
+data class GetBlockHeaders(val block: Bytes, val maxHeaders: Long, val skip: Long, val reverse: Boolean) {
companion object {
fun read(payload: Bytes): GetBlockHeaders = RLP.decodeList(payload) {
- val hash = Hash.fromBytes(it.readValue())
+ val block = it.readValue()
val maxHeaders = it.readLong()
val skip = it.readLong()
val reverse = it.readInt() == 1
- GetBlockHeaders(hash, maxHeaders, skip, reverse)
+ GetBlockHeaders(block, maxHeaders, skip, reverse)
}
}
fun toBytes(): Bytes = RLP.encodeList { writer ->
- writer.writeValue(hash)
+ writer.writeValue(block)
writer.writeLong(maxHeaders)
writer.writeLong(skip)
writer.writeInt(if (reverse) 1 else 0)
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 86b1ffb..22148ea 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
@@ -16,31 +16,151 @@
*/
package org.apache.tuweni.devp2p.eth
-import io.vertx.core.Vertx
import kotlinx.coroutines.runBlocking
import org.apache.lucene.index.IndexWriter
+import org.apache.tuweni.bytes.Bytes
+import org.apache.tuweni.bytes.Bytes32
import org.apache.tuweni.concurrent.coroutines.await
import org.apache.tuweni.crypto.SECP256K1
-import org.apache.tuweni.eth.genesis.GenesisFile
+import org.apache.tuweni.devp2p.eth.EthSubprotocol.Companion.ETH64
+import org.apache.tuweni.eth.Address
+import org.apache.tuweni.eth.Block
+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.repository.BlockchainIndex
import org.apache.tuweni.eth.repository.BlockchainRepository
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.rlp.RLP
+import org.apache.tuweni.rlpx.RLPxService
import org.apache.tuweni.units.bigints.UInt256
-import org.junit.jupiter.api.Assertions.assertFalse
-import org.junit.jupiter.api.Assertions.assertNotNull
-import org.junit.jupiter.api.Assertions.assertTrue
-import org.junit.jupiter.api.Disabled
+import org.apache.tuweni.units.bigints.UInt64
+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.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
-import java.net.InetSocketAddress
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.verify
+import java.time.Instant
+import java.time.temporal.ChronoUnit
@ExtendWith(LuceneIndexWriterExtension::class, VertxExtension::class, BouncyCastleExtension::class)
class EthHandlerTest {
+ private lateinit var genesisBlock: Block
+ private lateinit var handler: EthHandler
+ private lateinit var repository: BlockchainRepository
+ private lateinit var service: RLPxService
+
+ @BeforeEach
+ fun setup(@LuceneIndexWriter writer: IndexWriter) = runBlocking {
+ val header = BlockHeader(
+ Hash.fromBytes(Bytes32.random()),
+ Hash.fromBytes(Bytes32.random()),
+ Address.fromBytes(Bytes.random(20)),
+ Hash.fromBytes(Bytes32.random()),
+ Hash.fromBytes(Bytes32.random()),
+ Hash.fromBytes(Bytes32.random()),
+ Bytes32.random(),
+ UInt256.fromBytes(Bytes32.random()),
+ UInt256.ZERO,
+ Gas.valueOf(3000),
+ Gas.valueOf(2000),
+ Instant.now().plusSeconds(30).truncatedTo(ChronoUnit.SECONDS),
+ Bytes.of(2, 3, 4, 5, 6, 7, 8, 9, 10),
+ Hash.fromBytes(Bytes32.random()),
+ UInt64.ZERO
+ )
+ val body = BlockBody(
+ listOf(
+ Transaction(
+ UInt256.valueOf(1),
+ Wei.valueOf(2),
+ Gas.valueOf(2),
+ Address.fromBytes(Bytes.random(20)),
+ Wei.valueOf(2),
+ Bytes.random(12),
+ SECP256K1.KeyPair.random()
+ )
+ ),
+ listOf(header)
+ )
+ genesisBlock = Block(header, body)
+ repository = BlockchainRepository.init(
+ MapKeyValueStore(),
+ MapKeyValueStore(),
+ MapKeyValueStore(),
+ MapKeyValueStore(),
+ BlockchainIndex(writer),
+ genesisBlock
+ )
+ service = mock(RLPxService::class.java)
+ handler = EthHandler(
+ blockchainInfo = SimpleBlockchainInformation(
+ UInt256.valueOf(42L),
+ UInt256.ONE,
+ genesisBlock.header.hash,
+ genesisBlock.header.hash,
+ emptyList()
+ ),
+ service = service,
+ repository = repository
+ )
+ }
+
+ private fun createChildBlockHeader(parentBlock: BlockHeader): BlockHeader {
+ val emptyListHash = Hash.hash(RLP.encodeList { })
+ val emptyHash = Hash.hash(RLP.encode { writer -> writer.writeValue(Bytes.EMPTY) })
+ return BlockHeader(
+ parentBlock.hash,
+ emptyListHash,
+ Address.fromBytes(Bytes.random(20)),
+ parentBlock.stateRoot,
+ emptyHash,
+ emptyHash,
+ Bytes.wrap(ByteArray(256)),
+ parentBlock.difficulty,
+ parentBlock.number.add(1),
+ parentBlock.gasLimit,
+ Gas.valueOf(0),
+ Instant.now(),
+ Bytes.random(45),
+ Hash.fromBytes(Bytes32.random()),
+ UInt64.ZERO
+ )
+ }
+
+ @Test
+ fun testGetHeaders() = runBlocking {
+ var header = repository.retrieveGenesisBlock().header
+ for (i in 1..10) {
+ val newHeader = createChildBlockHeader(header)
+ repository.storeBlockHeader(newHeader)
+ header = newHeader
+ }
+
+ handler.handle(
+ "foo",
+ MessageType.GetBlockHeaders.code,
+ GetBlockHeaders(genesisBlock.header.hash, 3, 1, false).toBytes()
+ ).await()
+
+ val messageCapture = ArgumentCaptor.forClass(Bytes::class.java)
+ verify(service).send(eq(ETH64), eq(MessageType.BlockHeaders.code), eq("foo"), messageCapture.capture())
+
+ val messageRead = BlockHeaders.read(messageCapture.value)
+ assertEquals(3, messageRead.headers.size)
+ assertEquals(UInt256.ZERO, messageRead.headers[0].number)
+ assertEquals(UInt256.valueOf(2), messageRead.headers[1].number)
+ assertEquals(UInt256.valueOf(4), messageRead.headers[2].number)
+ }
}
diff --git a/eth-repository/src/main/kotlin/org/apache/tuweni/eth/repository/BlockchainIndex.kt b/eth-repository/src/main/kotlin/org/apache/tuweni/eth/repository/BlockchainIndex.kt
index ce7cf6a..a4b0edd 100644
--- a/eth-repository/src/main/kotlin/org/apache/tuweni/eth/repository/BlockchainIndex.kt
+++ b/eth-repository/src/main/kotlin/org/apache/tuweni/eth/repository/BlockchainIndex.kt
@@ -143,7 +143,7 @@ interface BlockchainIndexReader {
* @param hashOrNumber the hash of a block header, or its number as a 32-byte word
* @return the matching block header hashes.
*/
- fun findByHashOrNumber(hashOrNumber: Bytes32): List<Hash>
+ fun findByHashOrNumber(hashOrNumber: Bytes): List<Hash>
/**
* Find a value in a range.
@@ -582,7 +582,7 @@ class BlockchainIndex(private val indexWriter: IndexWriter) : BlockchainIndexWri
).firstOrNull()
}
- override fun findByHashOrNumber(hashOrNumber: Bytes32): List<Hash> {
+ override fun findByHashOrNumber(hashOrNumber: Bytes): List<Hash> {
val query = BooleanQuery.Builder()
.setMinimumNumberShouldMatch(1)
.add(BooleanClause(TermQuery(Term("_id", toBytesRef(hashOrNumber))), BooleanClause.Occur.SHOULD))
diff --git a/eth-repository/src/main/kotlin/org/apache/tuweni/eth/repository/BlockchainRepository.kt b/eth-repository/src/main/kotlin/org/apache/tuweni/eth/repository/BlockchainRepository.kt
index aa9f1ca..6cb0887 100644
--- a/eth-repository/src/main/kotlin/org/apache/tuweni/eth/repository/BlockchainRepository.kt
+++ b/eth-repository/src/main/kotlin/org/apache/tuweni/eth/repository/BlockchainRepository.kt
@@ -17,7 +17,6 @@
package org.apache.tuweni.eth.repository
import org.apache.tuweni.bytes.Bytes
-import org.apache.tuweni.bytes.Bytes32
import org.apache.tuweni.eth.Block
import org.apache.tuweni.eth.BlockBody
import org.apache.tuweni.eth.BlockHeader
@@ -235,7 +234,7 @@ class BlockchainRepository
*/
suspend fun retrieveChainHeadHeader(): BlockHeader? {
return blockchainIndex.findByLargest(BlockHeaderFields.TOTAL_DIFFICULTY)
- ?.let { retrieveBlockHeader(it) } ?: retrieveGenesisBlock()?.getHeader()
+ ?.let { retrieveBlockHeader(it) } ?: retrieveGenesisBlock().getHeader()
}
/**
@@ -243,8 +242,8 @@ class BlockchainRepository
*
* @return the genesis block
*/
- suspend fun retrieveGenesisBlock(): Block? {
- return chainMetadata.get(GENESIS_BLOCK)?.let { retrieveBlock(it) }
+ suspend fun retrieveGenesisBlock(): Block {
+ return chainMetadata.get(GENESIS_BLOCK).let { retrieveBlock(it!!)!! }
}
/**
@@ -284,7 +283,7 @@ class BlockchainRepository
* @param blockNumberOrBlockHash the number or hash of the block
* @return the matching blocks
*/
- fun findBlockByHashOrNumber(blockNumberOrBlockHash: Bytes32): List<Hash> {
+ fun findBlockByHashOrNumber(blockNumberOrBlockHash: Bytes): List<Hash> {
return blockchainIndex.findByHashOrNumber(blockNumberOrBlockHash)
}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@tuweni.apache.org
For additional commands, e-mail: commits-help@tuweni.apache.org