@@ -5,6 +5,8 @@ import org.scalatest.matchers.should.Matchers
5
5
import org .scalatestplus .scalacheck .ScalaCheckPropertyChecks
6
6
7
7
import io .iohk .ethereum .domain .Address
8
+ import io .iohk .ethereum .domain .UInt256
9
+ import io .iohk .ethereum .domain .UInt256 ._
8
10
import io .iohk .ethereum .vm .Generators ._
9
11
10
12
import Fixtures .blockchainConfig
@@ -47,4 +49,64 @@ class OpCodeGasSpecPostEip2929 extends AnyFunSuite with OpCodeTesting with Match
47
49
stateOut.accessedAddresses should contain(addr)
48
50
}
49
51
}
52
+
53
+ test(EXTCODECOPY ) { op =>
54
+ val table = Table [UInt256 , Boolean , BigInt ](
55
+ (" size" , " accessed" , " expectedGas" ),
56
+ (0 , false , G_cold_account_access ),
57
+ (0 , true , G_warm_storage_read ),
58
+ (1 , false , G_cold_account_access + G_copy * 1 ),
59
+ (1 , true , G_warm_storage_read + G_copy * 1 ),
60
+ (32 , false , G_cold_account_access + G_copy * 1 ),
61
+ (32 , true , G_warm_storage_read + G_copy * 1 ),
62
+ (33 , false , G_cold_account_access + G_copy * 2 ),
63
+ (33 , true , G_warm_storage_read + G_copy * 2 ),
64
+ (Two ** 16 , false , G_cold_account_access + G_copy * 2048 ),
65
+ (Two ** 16 , true , G_warm_storage_read + G_copy * 2048 ),
66
+ (Two ** 16 + 1 , false , G_cold_account_access + G_copy * 2049 ),
67
+ (Two ** 16 + 1 , true , G_warm_storage_read + G_copy * 2049 )
68
+ )
69
+
70
+ forAll(table) { (size, accessed, expectedGas) =>
71
+ val initState = getProgramStateGen(
72
+ evmConfig = config,
73
+ blockNumberGen = getUInt256Gen(Fixtures .MagnetoBlockNumber )
74
+ ).sample.get
75
+ // Pick an address (small, so it fits into memory) that is not on the precompiles list
76
+ val addr = getUInt256Gen(max = 1000 ).map(Address (_)).retryUntil(! initState.accessedAddresses.contains(_)).sample.get
77
+ val stackIn = Stack .empty().push(Seq (size, Zero , Zero , addr.toUInt256))
78
+ val memIn = Memory .empty.store(addr.toUInt256, Array .fill[Byte ](size.toInt)(- 1 ))
79
+ val stateIn = initState.withStack(stackIn).withMemory(memIn).copy(gas = expectedGas)
80
+
81
+ val stateOut = if (accessed) op.execute(stateIn.addAccessedAddress(addr)) else op.execute(stateIn)
82
+
83
+ verifyGas(expectedGas, stateIn, stateOut, allowOOG = false )
84
+ stateOut.accessedAddresses should contain(addr)
85
+ }
86
+
87
+ val maxGas = 2 * (G_cold_account_access + G_copy * 8 )
88
+ val stateGen = getProgramStateGen(
89
+ evmConfig = config,
90
+ blockNumberGen = getUInt256Gen(Fixtures .MagnetoBlockNumber ),
91
+ stackGen = getStackGen(elems = 4 , maxUInt = UInt256 (256 )),
92
+ gasGen = getBigIntGen(max = maxGas),
93
+ memGen = getMemoryGen(256 )
94
+ )
95
+
96
+ forAll(stateGen) { stateIn =>
97
+ val stateOut = op.execute(stateIn)
98
+
99
+ val (Seq (address, offset, _, size), _) = stateIn.stack.pop(4 )
100
+ val addr = Address (address)
101
+ val memCost = config.calcMemCost(stateIn.memory.size, offset, size)
102
+ val copyCost = G_copy * wordsForBytes(size)
103
+ val expectedGas =
104
+ if (stateIn.accessedAddresses.contains(addr))
105
+ G_warm_storage_read + memCost + copyCost
106
+ else G_cold_account_access + memCost + copyCost
107
+
108
+ verifyGas(expectedGas, stateIn, stateOut)
109
+ }
110
+ }
111
+
50
112
}
0 commit comments