Skip to content
This repository has been archived by the owner on Jan 29, 2019. It is now read-only.

Commit

Permalink
#185 ETH: calculate block & uncle rewards from block traces (#256)
Browse files Browse the repository at this point in the history
  • Loading branch information
KevinLiLu authored and hleb-albau committed Aug 26, 2018
1 parent 512df30 commit 4f9f2a5
Show file tree
Hide file tree
Showing 16 changed files with 374 additions and 112 deletions.
14 changes: 0 additions & 14 deletions common/src/main/kotlin/fund/cyber/search/model/ethereum/Block.kt
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
package fund.cyber.search.model.ethereum

import fund.cyber.search.model.chains.BlockEntity
import fund.cyber.search.model.chains.ChainInfo
import java.math.BigDecimal
import java.math.BigInteger
import java.time.Instant

const val ETHEREUM_CLASSIC_REWARD_CHANGED_BLOCK_NUMBER = 5000000
const val ETHEREUM_REWARD_CHANGED_BLOCK_NUMBER = 4370000

data class EthereumBlock(
override val number: Long, //parsed from hex
val hash: String,
Expand All @@ -33,13 +29,3 @@ data class EthereumBlock(
val unclesReward: BigDecimal, //including uncles reward, todo rename
val txFees: BigDecimal
) : BlockEntity

//todo change to use block trace rewards operations
//todo: 1) add properly support of new classic fork. 2) add support of custom reward functions in forks
fun getBlockReward(chainInfo: ChainInfo, number: Long): BigDecimal {
return if (chainInfo.name == "ETHEREUM_CLASSIC") {
if (number < ETHEREUM_CLASSIC_REWARD_CHANGED_BLOCK_NUMBER) BigDecimal("5") else BigDecimal("4")
} else {
if (number < ETHEREUM_REWARD_CHANGED_BLOCK_NUMBER) BigDecimal("5") else BigDecimal("3")
}
}
17 changes: 0 additions & 17 deletions common/src/main/kotlin/fund/cyber/search/model/ethereum/Uncle.kt
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
package fund.cyber.search.model.ethereum

import fund.cyber.common.decimal32
import fund.cyber.common.decimal8
import fund.cyber.search.model.chains.ChainEntity
import fund.cyber.search.model.chains.ChainInfo
import java.math.BigDecimal
import java.math.RoundingMode
import java.time.Instant

const val DIVIDE_SCALE = 18

data class EthereumUncle(
val hash: String,
Expand All @@ -21,15 +16,3 @@ data class EthereumUncle(
val miner: String,
val uncleReward: BigDecimal
) : ChainEntity

//todo: add support of custom reward functions in forks
fun getUncleReward(chainInfo: ChainInfo, uncleNumber: Long, blockNumber: Long): BigDecimal {

val blockReward = getBlockReward(chainInfo, blockNumber)
return if (chainInfo.name == "ETHEREUM_CLASSIC") {
getBlockReward(chainInfo, blockNumber).divide(decimal32, DIVIDE_SCALE, RoundingMode.FLOOR).stripTrailingZeros()
} else {
((uncleNumber.toBigDecimal() + decimal8 - blockNumber.toBigDecimal()) * blockReward)
.divide(decimal8, DIVIDE_SCALE, RoundingMode.FLOOR).stripTrailingZeros()
}
}
2 changes: 1 addition & 1 deletion gradle/coverage-subprojects.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
apply plugin: "jacoco"

jacoco {
toolVersion = "0.8.2-SNAPSHOT"
toolVersion = "0.8.2"
}

test {
Expand Down
2 changes: 1 addition & 1 deletion gradle/coverage.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ repositories {
}

jacoco {
toolVersion = "0.8.2-SNAPSHOT"
toolVersion = "0.8.2"
}

def allTestCoverageFile = "$buildDir/jacoco/rootTestsCoverage.exec"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package fund.cyber.pump.ethereum.client

import fund.cyber.common.DECIMAL_SCALE
import fund.cyber.common.decimal32
import fund.cyber.common.hexToLong
import fund.cyber.common.sum
Expand All @@ -11,15 +10,13 @@ import fund.cyber.search.model.ethereum.EthereumBlock
import fund.cyber.search.model.ethereum.EthereumTx
import fund.cyber.search.model.ethereum.EthereumUncle
import fund.cyber.search.model.ethereum.TxTrace
import fund.cyber.search.model.ethereum.getBlockReward
import fund.cyber.search.model.ethereum.getUncleReward
import fund.cyber.search.model.ethereum.weiToEthRate
import org.springframework.stereotype.Component
import org.web3j.protocol.core.methods.response.EthBlock
import org.web3j.protocol.core.methods.response.Transaction
import org.web3j.protocol.core.methods.response.TransactionReceipt
import org.web3j.protocol.parity.methods.response.Trace
import java.math.BigDecimal
import java.math.RoundingMode
import java.time.Instant

@Component
Expand All @@ -31,8 +28,8 @@ class ParityToEthereumBundleConverter(
fun convert(rawData: BundleRawData): EthereumBlockBundle {

val transactions = parityTransactionsToDao(rawData)
val block = parityBlockToDao(rawData.block, transactions)
val blockUncles = parityUnclesToDao(block, rawData.uncles)
val block = parityBlockToDao(rawData.block, transactions, rawData.calls)
val blockUncles = parityUnclesToDao(block, rawData.uncles, rawData.calls)

//todo parent hash test, reorganisation
return EthereumBlockBundle(
Expand All @@ -57,7 +54,9 @@ class ParityToEthereumBundleConverter(
)
}

private fun parityUnclesToDao(block: EthereumBlock, uncles: List<EthBlock.Block>): List<EthereumUncle> {
private fun parityUnclesToDao(block: EthereumBlock, uncles: List<EthBlock.Block>, traces: List<Trace>)
: List<EthereumUncle>
{
return uncles.mapIndexed { index, uncle ->
val uncleNumber = uncle.number.toLong()
EthereumUncle(
Expand All @@ -66,7 +65,7 @@ class ParityToEthereumBundleConverter(
timestamp = Instant.ofEpochSecond(uncle.timestampRaw.hexToLong()),
blockNumber = block.number, blockTime = block.timestamp,
blockHash = block.hash.toSearchHashFormat(),
uncleReward = getUncleReward(chainInfo, uncleNumber, block.number)
uncleReward = getUncleReward(traces, uncle.miner.toSearchHashFormat(), index)
)
}
}
Expand Down Expand Up @@ -109,14 +108,16 @@ class ParityToEthereumBundleConverter(
}


private fun parityBlockToDao(parityBlock: EthBlock.Block, transactions: List<EthereumTx>): EthereumBlock {

private fun parityBlockToDao(parityBlock: EthBlock.Block, transactions: List<EthereumTx>, traces: List<Trace>)
: EthereumBlock
{
val blockTxesFees = transactions.map { tx -> tx.fee }

val number = parityBlock.numberRaw.hexToLong()
val blockReward = getBlockReward(chainInfo, number)
val uncleReward = (blockReward * parityBlock.uncles.size.toBigDecimal())
.divide(decimal32, DECIMAL_SCALE, RoundingMode.FLOOR).stripTrailingZeros()
val blockReward = getBlockReward(traces)
val uncleInclusionReward = blockReward.multiply(parityBlock.uncles.size.toBigDecimal())
.divide(decimal32.plus(parityBlock.uncles.size.toBigDecimal())).stripTrailingZeros()
val staticBlockReward = blockReward.minus(uncleInclusionReward).stripTrailingZeros()

return EthereumBlock(
hash = parityBlock.hash.toSearchHashFormat(), parentHash = parityBlock.parentHash.toSearchHashFormat(),
Expand All @@ -131,7 +132,28 @@ class ParityToEthereumBundleConverter(
stateRoot = parityBlock.stateRoot.toSearchHashFormat(),
sha3Uncles = parityBlock.sha3Uncles.toSearchHashFormat(), uncles = parityBlock.uncles,
txNumber = parityBlock.transactions.size, nonce = parityBlock.nonce.toLong(),
txFees = blockTxesFees.sum(), blockReward = blockReward, unclesReward = uncleReward
txFees = blockTxesFees.sum(), blockReward = staticBlockReward,
unclesReward = uncleInclusionReward
)
}

fun getRewardTraces(traces: List<Trace>): List<Trace.RewardAction> {
return traces
.map { trace -> trace.action }
.filterIsInstance<Trace.RewardAction>()
}

fun getBlockReward(traces: List<Trace>): BigDecimal {
return getRewardTraces(traces)
.filter { rewardAction -> rewardAction.rewardType == "block" }[0].value.toBigDecimal() * weiToEthRate
}

fun getUncleReward(traces: List<Trace>, miner: String, unclePosition: Int): BigDecimal {
val uncleRewardTracesByMiner = getRewardTraces(traces)
.filter { rewardAction ->
rewardAction.rewardType == "uncle" && rewardAction.author.toSearchHashFormat() == miner}
if (uncleRewardTracesByMiner.size == 1)
return uncleRewardTracesByMiner.get(0).value.toBigDecimal() * weiToEthRate
return uncleRewardTracesByMiner[unclePosition].value.toBigDecimal() * weiToEthRate
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class EthereumBlockchainInterfaceTest {
@Test
fun blockBundleByNumberTest() {

val blockNumber = 1L
val blockNumber = 6149845L
val ethBlock = testData.getBlock(blockNumber)
val uncle = testData.getUncle(ethBlock.block.hash, 0)
val receipt = testData.getReceipt("0xd7b10b163b1de8f8967d824ea73d996c476588a91a4c714ad897b135cf7fa4c5")
Expand All @@ -57,15 +57,15 @@ class EthereumBlockchainInterfaceTest {
val blockchainInterface = EthereumBlockchainInterface(jsonRpcParity, converter, genesisMock, SimpleMeterRegistry())
val bundle = blockchainInterface.blockBundleByNumber(1)

Assertions.assertThat(bundle.number).isEqualTo(1)
Assertions.assertThat(bundle.hash).isEqualTo("B")
Assertions.assertThat(bundle.number).isEqualTo(6149845)
Assertions.assertThat(bundle.hash).isEqualTo("02e2ee54556a80710af3f80691781bf4eae37ae869d40bcf46b4186aeb6ce4d7")
Assertions.assertThat(bundle.parentHash).isEqualTo("A")
Assertions.assertThat(bundle.txes).hasSize(1)
Assertions.assertThat(bundle.uncles).hasSize(1)
Assertions.assertThat(bundle.uncles).hasSize(2)
Assertions.assertThat(bundle.block).isNotNull()
Assertions.assertThat(bundle.block.hash).isEqualTo("B")
Assertions.assertThat(bundle.block.hash).isEqualTo("02e2ee54556a80710af3f80691781bf4eae37ae869d40bcf46b4186aeb6ce4d7")
Assertions.assertThat(bundle.txes[0].hash).isEqualTo("d7b10b163b1de8f8967d824ea73d996c476588a91a4c714ad897b135cf7fa4c5")
Assertions.assertThat(bundle.uncles[0].hash).isEqualTo("UB")
Assertions.assertThat(bundle.uncles[0].hash).isEqualTo("772205e8de3b9d52bc6c410c7adf1348abb32c97adbee6aebdd9a2bb33f7fbf8")
}

@Test
Expand Down
Loading

0 comments on commit 4f9f2a5

Please sign in to comment.