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

Commit

Permalink
#161 Ethereum contract summary should use traces to calculate balances
Browse files Browse the repository at this point in the history
--method affected addresses shuld return same contracts as in delta
--fix review comments
  • Loading branch information
hleb-albau committed May 18, 2018
1 parent d5024fb commit a03fcb3
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,25 @@ data class OperationTrace(
@JsonDeserialize(using = OperationResultDeserializer::class)
val result: OperationResult?, //null for reward and destroy contract operations
val subtraces: List<OperationTrace>
)
) {

/**
* Do not returns child contracts.
*/
fun contractsUsedInCurrentOp(): List<String> {

return when (operation) {
is CallOperation -> listOf(operation.from, operation.to)
is RewardOperation -> listOf(operation.address)
is DestroyContractOperation -> listOf(operation.contractToDestroy, operation.refundContract)
is CreateContractOperation -> {
if (result is CreateContractOperationResult) listOf(operation.from, result.address)
else listOf(operation.from)
}
}
}
}


class OperationsDeserializer : JsonDeserializer<Operation>() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ data class TxTrace(
val rootOperationTrace: OperationTrace
) {

fun isRootOperationFailed() = rootOperationTrace.result is ErroredOperationResult

fun getAllOperationsTraces() = allSuboperations(listOf(rootOperationTrace))

private fun allSuboperations(operations: List<OperationTrace>): List<OperationTrace> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import fund.cyber.search.model.ethereum.EthereumTx
import fund.cyber.search.model.ethereum.OperationResult
import fund.cyber.search.model.ethereum.OperationTrace
import fund.cyber.search.model.ethereum.RewardOperation
import fund.cyber.search.model.ethereum.TxTrace
import fund.cyber.search.model.events.PumpEvent
import org.apache.kafka.clients.consumer.ConsumerRecord
import org.springframework.stereotype.Component
Expand Down Expand Up @@ -40,9 +41,8 @@ class EthereumTxDeltaProcessor : DeltaProcessor<EthereumTx, CqlEthereumContractS

override fun affectedContracts(records: List<ConsumerRecord<PumpEvent, EthereumTx>>): Set<String> {
val allContracts: List<String> = records.flatMap { record ->
val inContract = record.value().from
val outContract = (record.value().to ?: record.value().createdSmartContract)!!
return@flatMap listOf(inContract, outContract)
if (record.key() == PumpEvent.NEW_POOL_TX) return@flatMap emptyList<String>()
return@flatMap record.value().trace!!.contractsChangingState()
}

return allContracts.filter { contract -> contract.isNotEmpty() }.toSet()
Expand Down Expand Up @@ -76,7 +76,7 @@ class EthereumTxDeltaProcessor : DeltaProcessor<EthereumTx, CqlEthereumContractS
}

/**
* If parent operation failed, should no be invoked. Returns delta for destroyed contract and refund contract.
* For suboperations: If parent operation failed, should no be invoked. Returns delta for call in-out contracts.
*/
@SuppressWarnings("ComplexMethod")
private fun deltasByCallOperationTrace(
Expand Down Expand Up @@ -108,7 +108,8 @@ class EthereumTxDeltaProcessor : DeltaProcessor<EthereumTx, CqlEthereumContractS
}

/**
* If parent operation failed, should no be invoked. Returns delta for destroyed contract and refund contract.
* For suboperations: If parent operation failed, should no be invoked.
* Returns delta for crated contract and contract creator.
*/
@SuppressWarnings("ComplexMethod")
private fun deltasByCreateOperation(
Expand Down Expand Up @@ -143,7 +144,8 @@ class EthereumTxDeltaProcessor : DeltaProcessor<EthereumTx, CqlEthereumContractS
}

/**
* If parent operation failed, should no be invoked. Returns delta for destroyed contract and refund contract.
* For suboperations: If parent operation failed, should no be invoked.
* Returns delta for destroyed contract and refund contract.
* Can't be root op.
*/
private fun deltasByDestroyOperation(
Expand All @@ -166,3 +168,19 @@ class EthereumTxDeltaProcessor : DeltaProcessor<EthereumTx, CqlEthereumContractS
return listOf(contractDelta, refundDelta)
}
}

internal fun TxTrace.contractsChangingState() = if (this.isRootOperationFailed()) {
this.rootOperationTrace.contractsUsedInCurrentOp()
} else {
this.rootOperationTrace.contractsUsedInCurrentOp() + contractsChangingState(this.rootOperationTrace.subtraces)
}

private fun contractsChangingState(ops: List<OperationTrace>): List<String> {
return ops
.filter { op -> op.result !is ErroredOperationResult }
.flatMap { op ->
val contracts = op.contractsUsedInCurrentOp()
return@flatMap contracts + contractsChangingState(op.subtraces)
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -271,5 +271,75 @@ class EthereumTxDeltaProcessorTest {
val actualDeltas = deltaProcessor.recordToDeltas(txRecord)
Assertions.assertEquals(expectedDeltas, actualDeltas)
}

/**
*
* Tx -> Ok Call
* | -> Ok Call
* | -> Ok Call
* | -> Ok Create
* | -> Fail Call
* | -> Ok Destroy
* | -> Fail Call
* | -> Ok Call
*/
@Test
@DisplayName("affected addresses should returns same contracts as in record deltas")
fun testAffectedAddressShouldReturnsSameContractsAsDeltas() {
val subtraces13 = listOf(
destroyContractOperationTrace(contractToDelete = "1-1-3-1f", refundContract = "1-1-3-1t")
)
val subtraces1 = listOf(
callOperationTrace(from = "1-1-1f", to = "1-1-1t"),
createContractOperationTrace(from = "1-1-2f", to = "1-1-2t"),
callOperationTrace(from = "1-1-3f", to = "1-1-3t", error = "e", subtraces = subtraces13)
)
val subtraces = listOf(
callOperationTrace(from = "1-1f", to = "1-1t", subtraces = subtraces1),
callOperationTrace(from = "1-2f", to = "1-2t", error = "e"),
callOperationTrace(from = "1-3f", to = "1-3t")
)
val txTrace = TxTrace(
callOperationTrace(from = "1f", to = "1t", subtraces = subtraces)
)

val txRecord = txRecord(trace = txTrace, value = ZERO)

val expectedContracts = setOf("1f", "1t", "1-1f", "1-1t", "1-1-1f", "1-1-1t", "1-1-2f", "1-1-2t", "1-3f", "1-3t")
val affectedContracts = deltaProcessor.affectedContracts(listOf(txRecord))
val deltasContracts = deltaProcessor.recordToDeltas(txRecord).map { d -> d.contract }.toSet()

Assertions.assertEquals(expectedContracts, affectedContracts)
Assertions.assertEquals(expectedContracts, deltasContracts)
}

/**
*
* Tx -> Ok Fail
* | -> Ok Call
* | -> Fail Call
* | -> Ok Call
*/
@Test
@DisplayName("affected addresses should returns same contracts as in record deltas for failed root tx")
fun testAffectedAddressShouldReturnsSameContractsAsDeltasForFailedRootTx() {
val subtraces = listOf(
callOperationTrace(from = "1-1f", to = "1-1t"),
callOperationTrace(from = "1-2f", to = "1-2t", error = "e"),
callOperationTrace(from = "1-3f", to = "1-3t")
)
val txTrace = TxTrace(
callOperationTrace(from = "1f", to = "1t", subtraces = subtraces, error = "e")
)

val txRecord = txRecord(trace = txTrace, value = ZERO)

val expectedContracts = setOf("1f", "1t")
val affectedContracts = deltaProcessor.affectedContracts(listOf(txRecord))
val deltasContracts = deltaProcessor.recordToDeltas(txRecord).map { d -> d.contract }.toSet()

Assertions.assertEquals(expectedContracts, affectedContracts)
Assertions.assertEquals(expectedContracts, deltasContracts)
}
}

0 comments on commit a03fcb3

Please sign in to comment.