Skip to content

Commit

Permalink
Merge branch 'phase/daedalus' of github.com:input-output-hk/mantis in…
Browse files Browse the repository at this point in the history
…to feature/testECIP1017
  • Loading branch information
Nicolas Tallar committed Sep 25, 2017
2 parents e348412 + 83e63b3 commit ee76e15
Show file tree
Hide file tree
Showing 23 changed files with 200 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ trait BlockchainTestConfig extends BlockchainConfig {
//Enabling maxGasLimit in all Configs and all blocks
override val eip106BlockNumber: BigInt = 0
// unused
override val maxCodeSize: Option[BigInt] = None
override val difficultyBombPauseBlockNumber: BigInt = 3000000
override val difficultyBombContinueBlockNumber: BigInt = 5000000
override val chainId: Byte = 0x3d.toByte
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ object ScenarioBuilder {
}

def getConfig(blockNumber: BigInt): EvmConfig = {
val baseConfig = EvmConfig.HomesteadConfig
val baseConfig = EvmConfig.HomesteadConfigBuilder(None)
val opCodes = baseConfig.opCodes.diff(List(CREATE, CALL, CALLCODE)) ++ List(TestCREATE, TestCALL, TestCALLCODE)
baseConfig.copy(
opCodes = opCodes,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ trait EvmTestEnv {

import EvmTestEnv._

val config = EvmConfig.PostEIP160Config
val config = EvmConfig.PostEIP160ConfigBuilder(None)

private var contractsAddresses: Map[String, Address] = Map.empty
private var contractsAbis: Map[String, Seq[ABI]] = Map.empty
Expand Down
1 change: 1 addition & 0 deletions src/it/scala/io/iohk/ethereum/txExecTest/ForksTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class ForksTest extends FlatSpec with Matchers {
override val monetaryPolicyConfig: MonetaryPolicyConfig = MonetaryPolicyConfig(5000000, 0.2, 5000000000000000000L)

// unused
override val maxCodeSize: Option[BigInt] = None
override val customGenesisFileOpt: Option[String] = None
override val difficultyBombPauseBlockNumber: BigInt = Long.MaxValue
override val difficultyBombContinueBlockNumber: BigInt = Long.MaxValue
Expand Down
5 changes: 5 additions & 0 deletions src/main/resources/application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,11 @@ mantis {
# Doc: https://github.com/ethereum/EIPs/issues/160
eip160-block-number = "3000000"

# EIP-170 max code size (Eth only)
# Doc: https://github.com/ethereum/EIPs/issues/170
# null value indicates there's no max code size for the contract code
max-code-size = null

# Difficulty bomb pause block number
# Doc: https://github.com/ethereumproject/ECIPs/blob/master/ECIPs/ECIP-1010.md
difficulty-bomb-pause-block-number = "3000000"
Expand Down
27 changes: 17 additions & 10 deletions src/main/scala/io/iohk/ethereum/ledger/Ledger.scala
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ class LedgerImpl(vm: VM, blockchain: BlockchainImpl, blockchainConfig: Blockchai
* @param tx Target transaction
* @return Upfront cost
*/
private[ledger] def calculateUpfrontCost(tx: Transaction): UInt256 =
private def calculateUpfrontCost(tx: Transaction): UInt256 =
UInt256(calculateUpfrontGas(tx) + tx.value)

/**
Expand All @@ -302,13 +302,13 @@ class LedgerImpl(vm: VM, blockchain: BlockchainImpl, blockchainConfig: Blockchai
* @param worldStateProxy
* @return
*/
private[ledger] def updateSenderAccountBeforeExecution(stx: SignedTransaction, worldStateProxy: InMemoryWorldStateProxy): InMemoryWorldStateProxy = {
private def updateSenderAccountBeforeExecution(stx: SignedTransaction, worldStateProxy: InMemoryWorldStateProxy): InMemoryWorldStateProxy = {
val senderAddress = stx.senderAddress
val account = worldStateProxy.getGuaranteedAccount(senderAddress)
worldStateProxy.saveAccount(senderAddress, account.increaseBalance(-calculateUpfrontGas(stx.tx)).increaseNonce)
}

private[ledger] def prepareProgramContext(stx: SignedTransaction, blockHeader: BlockHeader, worldStateProxy: InMemoryWorldStateProxy,
private def prepareProgramContext(stx: SignedTransaction, blockHeader: BlockHeader, worldStateProxy: InMemoryWorldStateProxy,
config: EvmConfig): PC = {
stx.tx.receivingAddress match {
case None =>
Expand All @@ -329,14 +329,21 @@ class LedgerImpl(vm: VM, blockchain: BlockchainImpl, blockchainConfig: Blockchai
result
}

private def saveNewContract(address: Address, result: PR, config: EvmConfig): PR = {
val codeDepositCost = config.calcCodeDepositCost(result.returnData)
if (result.gasRemaining < codeDepositCost) {
if (config.exceptionalFailedCodeDeposit)
result.copy(error = Some(OutOfGas))
else
result
private[ledger] def saveNewContract(address: Address, result: PR, config: EvmConfig): PR = {
val contractCode = result.returnData
val codeDepositCost = config.calcCodeDepositCost(contractCode)

val maxCodeSizeExceeded = blockchainConfig.maxCodeSize.exists(codeSizeLimit => contractCode.size > codeSizeLimit)
val codeStoreOutOfGas = result.gasRemaining < codeDepositCost

if (maxCodeSizeExceeded || (codeStoreOutOfGas && config.exceptionalFailedCodeDeposit)) {
// Code size too big or code storage causes out-of-gas with exceptionalFailedCodeDeposit enabled
result.copy(error = Some(OutOfGas))
} else if (codeStoreOutOfGas && !config.exceptionalFailedCodeDeposit) {
// Code storage causes out-of-gas with exceptionalFailedCodeDeposit disabled
result
} else {
// Code storage succeeded
result.copy(
gasRemaining = result.gasRemaining - codeDepositCost,
world = result.world.saveCode(address, result.returnData))
Expand Down
2 changes: 2 additions & 0 deletions src/main/scala/io/iohk/ethereum/utils/Config.scala
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ trait BlockchainConfig {
val eip155BlockNumber: BigInt
val eip160BlockNumber: BigInt
val eip106BlockNumber: BigInt
val maxCodeSize: Option[BigInt]
val difficultyBombPauseBlockNumber: BigInt
val difficultyBombContinueBlockNumber: BigInt

Expand All @@ -317,6 +318,7 @@ object BlockchainConfig {
override val eip150BlockNumber: BigInt = BigInt(blockchainConfig.getString("eip150-block-number"))
override val eip155BlockNumber: BigInt = BigInt(blockchainConfig.getString("eip155-block-number"))
override val eip160BlockNumber: BigInt = BigInt(blockchainConfig.getString("eip160-block-number"))
override val maxCodeSize: Option[BigInt] = Try(BigInt(blockchainConfig.getString("max-code-size"))).toOption
override val difficultyBombPauseBlockNumber: BigInt = BigInt(blockchainConfig.getString("difficulty-bomb-pause-block-number"))
override val difficultyBombContinueBlockNumber: BigInt = BigInt(blockchainConfig.getString("difficulty-bomb-continue-block-number"))

Expand Down
26 changes: 16 additions & 10 deletions src/main/scala/io/iohk/ethereum/vm/EvmConfig.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import io.iohk.ethereum.utils.BlockchainConfig
// scalastyle:off magic.number
object EvmConfig {

type EvmConfigBuilder = Option[BigInt] => EvmConfig

val MaxCallDepth: Int = 1024

val MaxMemory: UInt256 = UInt256(Int.MaxValue) /* used to artificially limit memory usage by incurring maximum gas cost */
Expand All @@ -17,41 +19,44 @@ object EvmConfig {
* returns the evm config that should be used for given block
*/
def forBlock(blockNumber: BigInt, blockchainConfig: BlockchainConfig): EvmConfig = {
val transitionBlockToConfigMapping: Map[BigInt, EvmConfig] = Map(
blockchainConfig.frontierBlockNumber -> FrontierConfig,
blockchainConfig.homesteadBlockNumber -> HomesteadConfig,
blockchainConfig.eip150BlockNumber -> PostEIP150Config,
blockchainConfig.eip160BlockNumber -> PostEIP160Config)
val transitionBlockToConfigMapping: Map[BigInt, EvmConfigBuilder] = Map(
blockchainConfig.frontierBlockNumber -> FrontierConfigBuilder,
blockchainConfig.homesteadBlockNumber -> HomesteadConfigBuilder,
blockchainConfig.eip150BlockNumber -> PostEIP150ConfigBuilder,
blockchainConfig.eip160BlockNumber -> PostEIP160ConfigBuilder)

// highest transition block that is less/equal to `blockNumber`
transitionBlockToConfigMapping
val evmConfigBuilder = transitionBlockToConfigMapping
.filterKeys(_ <= blockNumber)
.maxBy(_._1)
._2
evmConfigBuilder(blockchainConfig.maxCodeSize)
}

val FrontierConfig = EvmConfig(
val FrontierConfigBuilder: EvmConfigBuilder = maxCodeSize => EvmConfig(
feeSchedule = new FeeSchedule.FrontierFeeSchedule,
opCodes = OpCodes.FrontierOpCodes,
exceptionalFailedCodeDeposit = false,
subGasCapDivisor = None,
chargeSelfDestructForNewAccount = false,
maxCodeSize = maxCodeSize,
traceInternalTransactions = false)

val HomesteadConfig = EvmConfig(
val HomesteadConfigBuilder: EvmConfigBuilder = maxCodeSize => EvmConfig(
feeSchedule = new FeeSchedule.HomesteadFeeSchedule,
opCodes = OpCodes.HomesteadOpCodes,
exceptionalFailedCodeDeposit = true,
subGasCapDivisor = None,
chargeSelfDestructForNewAccount = false,
maxCodeSize = maxCodeSize,
traceInternalTransactions = false)

val PostEIP150Config = HomesteadConfig.copy(
val PostEIP150ConfigBuilder: EvmConfigBuilder = maxCodeSize => HomesteadConfigBuilder(maxCodeSize).copy(
feeSchedule = new FeeSchedule.PostEIP150FeeSchedule,
subGasCapDivisor = Some(64),
chargeSelfDestructForNewAccount = true)

val PostEIP160Config = PostEIP150Config.copy(
val PostEIP160ConfigBuilder: EvmConfigBuilder = maxCodeSize => PostEIP150ConfigBuilder(maxCodeSize).copy(
feeSchedule = new FeeSchedule.PostEIP160FeeSchedule)
}

Expand All @@ -61,6 +66,7 @@ case class EvmConfig(
exceptionalFailedCodeDeposit: Boolean,
subGasCapDivisor: Option[Long],
chargeSelfDestructForNewAccount: Boolean,
maxCodeSize: Option[BigInt],
traceInternalTransactions: Boolean) {

import feeSchedule._
Expand Down
8 changes: 6 additions & 2 deletions src/main/scala/io/iohk/ethereum/vm/OpCode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -701,12 +701,16 @@ abstract class CreateOp extends OpCode(0xf0, 3, 1, _.G_create) {
val context = ProgramContext[W, S](newEnv, newAddress, startGas, worldAfterInitialisation, state.config, state.addressesToDelete)
val result = VM.run(context)

val codeDepositGas = state.config.calcCodeDepositCost(result.returnData)
val contractCode = result.returnData
val codeDepositGas = state.config.calcCodeDepositCost(contractCode)
val gasUsedInVm = startGas - result.gasRemaining
val totalGasRequired = gasUsedInVm + codeDepositGas

val maxCodeSizeExceeded = state.config.maxCodeSize.exists(codeSizeLimit => contractCode.size > codeSizeLimit)
val enoughGasForDeposit = totalGasRequired <= startGas

val creationFailed = result.error.isDefined || !enoughGasForDeposit && state.config.exceptionalFailedCodeDeposit
val creationFailed = maxCodeSizeExceeded || result.error.isDefined ||
!enoughGasForDeposit && state.config.exceptionalFailedCodeDeposit

if (creationFailed) {
val stack2 = stack1.push(UInt256.Zero)
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/io/iohk/ethereum/vm/Program.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ case class Program(code: ByteString) {
if(pos < 0 || pos >= length) accum
else {
val byte = code(pos)
val opCode = EvmConfig.FrontierConfig.byteToOpCode.get(byte) // we only need to check PushOp and JUMPDEST, they are both present in Frontier
val opCode = EvmConfig.FrontierConfigBuilder(None).byteToOpCode.get(byte) // we only need to check PushOp and JUMPDEST, they are both present in Frontier
opCode match {
case Some(pushOp: PushOp) => validJumpDestinationsAfterPosition(pos + pushOp.i + 2, accum)
case Some(JUMPDEST) => validJumpDestinationsAfterPosition(pos + 1, accum + pos)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ class PersonalServiceSpec extends FlatSpec with Matchers with MockFactory with S
override val chainId: Byte = 0x03.toByte

//unused
override val maxCodeSize: Option[BigInt] = None
override val frontierBlockNumber: BigInt = 0
override val homesteadBlockNumber: BigInt = 0
override val eip150BlockNumber: BigInt = 0
Expand Down
89 changes: 89 additions & 0 deletions src/test/scala/io/iohk/ethereum/ledger/ContractCreationSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package io.iohk.ethereum.ledger

import akka.util.ByteString
import akka.util.ByteString.{empty => bEmpty}
import io.iohk.ethereum.Mocks.MockVM
import io.iohk.ethereum.blockchain.sync.EphemBlockchainTestSetup
import io.iohk.ethereum.crypto.{generateKeyPair, kec256}
import io.iohk.ethereum.domain.{Address, BlockchainImpl, TxLogEntry, UInt256}
import io.iohk.ethereum.ledger.Ledger.PR
import io.iohk.ethereum.nodebuilder.SecureRandomBuilder
import io.iohk.ethereum.utils.{BlockchainConfig, Config, DaoForkConfig, MonetaryPolicyConfig}
import io.iohk.ethereum.vm._
import org.scalatest.{FlatSpec, Matchers}
import org.scalatest.prop.PropertyChecks
import org.spongycastle.crypto.AsymmetricCipherKeyPair
import org.spongycastle.crypto.params.ECPublicKeyParameters

class ContractCreationSpec extends FlatSpec with PropertyChecks with Matchers {

def createResult(world: InMemoryWorldStateProxy,
gasUsed: BigInt,
gasLimit: BigInt,
gasRefund: BigInt,
error: Option[ProgramError] = None,
returnData: ByteString = bEmpty,
logs: Seq[TxLogEntry] = Nil,
addressesToDelete: Set[Address] = Set.empty): PR =
ProgramResult(
returnData = returnData,
gasRemaining = gasLimit - gasUsed,
world = world,
addressesToDelete = addressesToDelete,
logs = logs,
gasRefund = gasRefund,
internalTxs = Nil,
error = error
)

it should "return an error if it's size is larger than the limit" in new TestSetup {
val longContractCode = ByteString(Array.fill(codeSizeLimit + 1)(1.toByte))
val resultBeforeSaving = createResult(emptyWorld, gasUsed = defaultGasLimit / 2,
gasLimit = defaultGasLimit, gasRefund = 0, error = None, returnData = longContractCode)

val ledger = new LedgerImpl(new MockVM(), blockchain, blockchainConfig)
val resultAfterSaving = ledger.saveNewContract(contractAddress, resultBeforeSaving, config)
resultAfterSaving.error shouldBe Some(OutOfGas)
}

it should "not return an error if it's size is smaller than the limit" in new TestSetup {
val shortContractCode = ByteString(Array.fill(codeSizeLimit - 1)(1.toByte))
val resultBeforeSaving = createResult(emptyWorld, gasUsed = defaultGasLimit / 2,
gasLimit = defaultGasLimit, gasRefund = 0, error = None, returnData = shortContractCode)

val ledger = new LedgerImpl(new MockVM(), blockchain, blockchainConfig)
val resultAfterSaving = ledger.saveNewContract(contractAddress, resultBeforeSaving, config)
resultAfterSaving.error shouldBe None
}

trait TestSetup extends EphemBlockchainTestSetup with SecureRandomBuilder {
val keyPair: AsymmetricCipherKeyPair = generateKeyPair(secureRandom)
val contractAddress = Address(kec256(keyPair.getPublic.asInstanceOf[ECPublicKeyParameters].getQ.getEncoded(false).tail))

val codeSizeLimit = 10
val defaultGasLimit = 5000
val config = EvmConfig.FrontierConfigBuilder(None)

val emptyWorld = BlockchainImpl(storagesInstance.storages).getWorldStateProxy(-1, UInt256.Zero, None)

val defaultBlockchainConfig = BlockchainConfig(Config.config)
val blockchainConfig = new BlockchainConfig {
override val maxCodeSize: Option[BigInt] = Some(codeSizeLimit)

//unused
override val daoForkConfig: Option[DaoForkConfig] = None
override val customGenesisFileOpt: Option[String] = defaultBlockchainConfig.customGenesisFileOpt
override val difficultyBombContinueBlockNumber: BigInt = defaultBlockchainConfig.difficultyBombContinueBlockNumber
override val eip160BlockNumber: BigInt = defaultBlockchainConfig.eip160BlockNumber
override val eip150BlockNumber: BigInt = defaultBlockchainConfig.eip150BlockNumber
override val eip155BlockNumber: BigInt = defaultBlockchainConfig.eip155BlockNumber
override val eip106BlockNumber: BigInt = defaultBlockchainConfig.eip106BlockNumber
override val chainId: Byte = defaultBlockchainConfig.chainId
override val frontierBlockNumber: BigInt = defaultBlockchainConfig.frontierBlockNumber
override val monetaryPolicyConfig: MonetaryPolicyConfig = defaultBlockchainConfig.monetaryPolicyConfig
override val difficultyBombPauseBlockNumber: BigInt = defaultBlockchainConfig.difficultyBombPauseBlockNumber
override val homesteadBlockNumber: BigInt = defaultBlockchainConfig.homesteadBlockNumber
override val accountStartNonce: UInt256 = defaultBlockchainConfig.accountStartNonce
}
}
}
3 changes: 2 additions & 1 deletion src/test/scala/io/iohk/ethereum/ledger/LedgerSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -911,8 +911,9 @@ class LedgerSpec extends FlatSpec with PropertyChecks with Matchers with MockFac
override val chainId: Byte = 0x01.toByte
override val difficultyBombContinueBlockNumber: BigInt = blockchainConfig.difficultyBombContinueBlockNumber
override val daoForkConfig: Option[DaoForkConfig] = Some(supportDaoForkConfig)
override val customGenesisFileOpt: Option[String] = None
override val customGenesisFileOpt: Option[String] = None
override val eip106BlockNumber = Long.MaxValue
override val maxCodeSize: Option[BigInt] = None
}

(testBlockchain.getBlockHeaderByHash _).expects(proDaoBlock.header.parentHash).returning(Some(Fixtures.Blocks.DaoParentBlock.header))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ class BlockGeneratorSpec extends FlatSpec with Matchers with PropertyChecks with
override val monetaryPolicyConfig: MonetaryPolicyConfig = MonetaryPolicyConfig(5000000, 0.2, BigInt("5000000000000000000"))

// unused
override val maxCodeSize: Option[BigInt] = None
override val eip160BlockNumber: BigInt = Long.MaxValue
override val eip150BlockNumber: BigInt = Long.MaxValue
override val accountStartNonce: UInt256 = UInt256.Zero
Expand Down Expand Up @@ -265,6 +266,7 @@ class BlockGeneratorSpec extends FlatSpec with Matchers with PropertyChecks with
override val monetaryPolicyConfig: MonetaryPolicyConfig = MonetaryPolicyConfig(5000000, 0.2, BigInt("5000000000000000000"))

// unused
override val maxCodeSize: Option[BigInt] = None
override val eip160BlockNumber: BigInt = Long.MaxValue
override val eip150BlockNumber: BigInt = Long.MaxValue
override val accountStartNonce: UInt256 = UInt256.Zero
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ class EtcHandshakerSpec extends FlatSpec with Matchers {

val blockchainConfig = new BlockchainConfig {
//unused
override val maxCodeSize: Option[BigInt] = None
override val frontierBlockNumber: BigInt = 0
override val homesteadBlockNumber: BigInt = 0
override val eip150BlockNumber: BigInt = 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ class BlockHeaderValidatorSpec extends FlatSpec with Matchers with PropertyCheck
})

// unused
override val maxCodeSize: Option[BigInt] = None
override val eip155BlockNumber: BigInt = Long.MaxValue
override val eip160BlockNumber: BigInt = Long.MaxValue
override val eip150BlockNumber: BigInt = Long.MaxValue
Expand Down
Loading

0 comments on commit ee76e15

Please sign in to comment.