Merge pull request #428 from atoulme/custom_opcode_support
Add support for new custom opcode in DSL
diff --git a/evm-dsl/src/main/kotlin/org/apache/tuweni/evmdsl/Code.kt b/evm-dsl/src/main/kotlin/org/apache/tuweni/evmdsl/Code.kt
index abe1db3..1afd665 100644
--- a/evm-dsl/src/main/kotlin/org/apache/tuweni/evmdsl/Code.kt
+++ b/evm-dsl/src/main/kotlin/org/apache/tuweni/evmdsl/Code.kt
@@ -51,6 +51,8 @@
var stackSize = 0
val visited = mutableSetOf<Int>()
var index = 0
+ val jumpDests = mutableSetOf<Int>()
+ val jumpSrcs = mutableMapOf<Int, Int>()
while (visited.add(index)) {
val currentInstruction = instructions.getOrNull(index) ?: break
if (currentInstruction.stackItemsNeeded() > stackSize) {
@@ -63,11 +65,15 @@
if (currentInstruction is Invalid) {
return CodeValidationError(currentInstruction, index, Error.HIT_INVALID_OPCODE)
}
- // TODO cannot follow jumps right now.
if (currentInstruction == Jump || currentInstruction == Jumpi) {
- break
+ jumpSrcs.put(index, stackSize)
}
-
+ if (currentInstruction == JumpDest) {
+ jumpDests.add(index)
+ }
+ if (currentInstruction.end()) {
+ stackSize = 0
+ }
index++
}
return null
diff --git a/evm-dsl/src/main/kotlin/org/apache/tuweni/evmdsl/Instructions.kt b/evm-dsl/src/main/kotlin/org/apache/tuweni/evmdsl/Instructions.kt
index 04be3e1..3cc12ec 100644
--- a/evm-dsl/src/main/kotlin/org/apache/tuweni/evmdsl/Instructions.kt
+++ b/evm-dsl/src/main/kotlin/org/apache/tuweni/evmdsl/Instructions.kt
@@ -31,6 +31,8 @@
fun stackItemsConsumed(): Int
fun stackItemsProduced(): Int
+
+ fun end(): Boolean = false
}
data class InstructionModel(val opcode: Byte, val additionalBytesToRead: Int = 0, val creator: (code: Bytes, index: Int) -> Instruction)
@@ -665,6 +667,7 @@
override fun toString(): String = "RETURN"
override fun stackItemsConsumed() = 2
override fun stackItemsProduced() = 0
+ override fun end() = true
}
object DelegateCall : Instruction {
@@ -696,6 +699,7 @@
override fun toString(): String = "REVERT"
override fun stackItemsConsumed() = 2
override fun stackItemsProduced() = 0
+ override fun end() = true
}
object SelfDestruct : Instruction {
@@ -728,3 +732,10 @@
override fun stackItemsConsumed() = logIndex + 2
override fun stackItemsProduced() = 0
}
+
+data class Custom(val bytes: Bytes, val str: String, val consumed: Int, val produced: Int) : Instruction {
+ override fun toBytes() = bytes
+ override fun toString() = str
+ override fun stackItemsConsumed() = consumed
+ override fun stackItemsProduced() = produced
+}
diff --git a/evm-dsl/src/test/kotlin/org/apache/tuweni/evmdsl/CodeTest.kt b/evm-dsl/src/test/kotlin/org/apache/tuweni/evmdsl/CodeTest.kt
index dcdd648..e1cc2c4 100644
--- a/evm-dsl/src/test/kotlin/org/apache/tuweni/evmdsl/CodeTest.kt
+++ b/evm-dsl/src/test/kotlin/org/apache/tuweni/evmdsl/CodeTest.kt
@@ -17,6 +17,7 @@
package org.apache.tuweni.evmdsl
import org.apache.tuweni.bytes.Bytes
+import org.apache.tuweni.bytes.Bytes32
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.Test
@@ -114,4 +115,47 @@
)
assertNull(code.validate()?.error)
}
+
+ @Test
+ fun testSomeWorkshopCode() {
+ val code = Code(
+ buildList {
+ this.add(Push(Bytes.fromHexString("0x00")))
+ this.add(CallDataLoad) // load from position 0
+ this.add(Custom(Bytes.fromHexString("0xf6"), "SHAREDSECRET", 1, 1))
+ this.add(Push(Bytes.fromHexString("0x11"))) // push jump destination
+ this.add(Jumpi) // if custom returns 0, keep going, else go to jumpdest at byte 0x11
+ this.add(Push(Bytes.fromHexString("0x00"))) // value
+ this.add(Push(Bytes.fromHexString("0x00"))) // location
+ this.add(Mstore) // store 0 in memory
+ this.add(Push(Bytes.fromHexString("0x01"))) // length
+ this.add(Push(Bytes.fromHexString("0x1f"))) // location
+ this.add(Return) // return 0
+ this.add(JumpDest)
+ this.add(Push(Bytes.fromHexString("0x01"))) // value
+ this.add(Push(Bytes.fromHexString("0x00"))) // location
+ this.add(Mstore) // store 1 in memory
+ this.add(Push(Bytes.fromHexString("0x01"))) // length
+ this.add(Push(Bytes.fromHexString("0x1f"))) // location
+ this.add(Return)
+ }
+ )
+ assertNull(code.validate()?.error)
+ println(code.toString())
+ println(code.toBytes().toHexString())
+
+ // surround with instructions to deploy.
+ val deployment = Code(
+ buildList {
+ this.add(Push(Bytes32.rightPad(code.toBytes()))) // pad the code with zeros to create a word
+ this.add(Push(Bytes.fromHexString("0x00"))) // set the location of the memory to store
+ this.add(Mstore)
+ this.add(Push(Bytes.ofUnsignedInt(code.toBytes().size().toLong()))) // length
+ this.add(Push(Bytes.fromHexString("0x00"))) // location
+ this.add(Return) // return the code
+ }
+ )
+
+ println(deployment.toBytes().toHexString())
+ }
}