blob: e1cc2c4b1d0d3b20d7e83dd0475d9dc1306eaa40 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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
import org.junit.jupiter.api.assertDoesNotThrow
class CodeTest {
@Test
fun testRoundtrip() {
val code = Code(
buildList {
this.add(Push(Bytes.fromHexString("0xdeadbeef")))
this.add(Push(Bytes.fromHexString("0xf00ba3")))
this.add(Push(Bytes.fromHexString("0xf000")))
}
)
assertEquals(Bytes.fromHexString("0x63deadbeef62f00ba361f000"), code.toBytes())
val codeRead = Code.read(code.toBytes())
assertEquals(3, codeRead.instructions.size)
assertEquals(Bytes.fromHexString("0xdeadbeef"), (codeRead.instructions[0] as Push).bytesToPush)
assertEquals(Bytes.fromHexString("0xf00ba3"), (codeRead.instructions[1] as Push).bytesToPush)
assertEquals(Bytes.fromHexString("0xf000"), (codeRead.instructions[2] as Push).bytesToPush)
}
@Test
fun decodeExistingContract() {
// decode the contract:
val ba = CodeTest::class.java.getResourceAsStream("/contract.txt")!!.readAllBytes()
val contract = Bytes.fromHexString(String(ba))
val code = assertDoesNotThrow {
Code.read(contract)
}
assertEquals("PUSH 0x80", code.toString().splitToSequence('\n').first())
val codeStr = code.toString()
val reread = Code.read(code.toBytes())
assertEquals(codeStr, reread.toString())
}
@Test
fun testValidateUnderFlow() {
val code = Code(
buildList {
this.add(Push(Bytes.fromHexString("0x4567")))
this.add(Push(Bytes.fromHexString("0x456778")))
this.add(Call)
}
)
val err = code.validate()!!
assertEquals(2, err.index)
assertEquals(Error.STACK_UNDERFLOW, err.error)
assertEquals(Call, err.instruction)
}
@Test
fun testValidateInvalid() {
val code = Code(
buildList {
this.add(Push(Bytes.fromHexString("0x4567")))
this.add(Push(Bytes.fromHexString("0x456778")))
this.add(Invalid(0xfe.toByte()))
this.add(Push(Bytes.fromHexString("0x456778")))
}
)
val err = code.validate()!!
assertEquals(2, err.index)
assertEquals(Error.HIT_INVALID_OPCODE, err.error)
assertEquals(Invalid(0xfe.toByte()), err.instruction)
}
@Test
fun testValidateOverFlow() {
val code = Code(
buildList {
for (i in 0..1024) {
this.add(Push(Bytes.fromHexString("0x4567")))
}
}
)
val err = code.validate()!!
assertEquals(1024, err.index)
assertEquals(Error.STACK_OVERFLOW, err.error)
}
@Test
fun testCreateASimpleReturn() {
val code = Code(
buildList {
this.add(Push(Bytes.wrap("hello world".toByteArray())))
this.add(Push(Bytes.fromHexString("0x00")))
this.add(Mstore)
this.add(Push(Bytes.of("hello world".toByteArray().size)))
this.add(Push(Bytes.of(32 - "hello world".toByteArray().size)))
this.add(Return)
}
)
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())
}
}