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