Skip to content

Commit

Permalink
Fix bug that bytesX are decoded as dynamic type (#41)
Browse files Browse the repository at this point in the history
  • Loading branch information
rmeissner authored Oct 28, 2017
1 parent fa21e47 commit 0ca13ad
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 17 deletions.
28 changes: 13 additions & 15 deletions bivrost-abi-parser/src/main/kotlin/pm/gnosis/AbiParser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,23 @@ class AbiParser {
fun hash(): String
}

internal class SimpleTypeHolder(private val className: ClassName, private val dynamic: Boolean) : TypeHolder {
internal class SimpleTypeHolder private constructor(private val className: ClassName, private val dynamic: Boolean) : TypeHolder {
override fun toTypeName() = className

override fun isDynamic() = dynamic

override fun hash() = generateHash(listOf(className.toString()))

companion object {
/**
* Create a SimpleTypeHolder for a given type if possible.
* If the given type is not a simple type (e.g. Tuple) null will be returned.
*/
fun forType(type: String): SimpleTypeHolder? {
val baseType = Solidity.types[checkType(type)] ?: return null
return SimpleTypeHolder(ClassName.bestGuess(baseType), SolidityBase.dynamicTypes.contains(type.toLowerCase()))
}
}
}

internal abstract class CollectionTypeHolder(val listType: ClassName, val itemType: TypeHolder) : TypeHolder {
Expand Down Expand Up @@ -165,19 +176,14 @@ class AbiParser {
throw IllegalArgumentException("Invalid parameter definition: ${parameter.type}!")
}
val arrayType = matcher.group(1)
val baseType = generateElementaryType(arrayType) ?: generateTuple(arrayType, parameter, context) ?: throw IllegalArgumentException("Unknown parameter ${parameter.type}!")
val baseType = SimpleTypeHolder.forType(arrayType) ?: generateTuple(arrayType, parameter, context) ?: throw IllegalArgumentException("Unknown parameter ${parameter.type}!")
val arrayDef = matcher.group(2)
if (arrayType.length < parameter.type.length && arrayDef.isNullOrBlank()) {
throw IllegalArgumentException("Invalid parameter definition: ${parameter.type}!")
}
return parseArrayDefinition(arrayDef, baseType, context)
}

private fun generateElementaryType(type: String): TypeHolder? {
val baseType = Solidity.types[checkType(type)] ?: return null
return SimpleTypeHolder(ClassName.bestGuess(baseType), isSolidityDynamicType(type))
}

private fun generateTuple(type: String, parameters: ParameterJson, context: GeneratorContext): TypeHolder? {
if (type != TUPLE_TYPE_NAME || parameters.components == null) {
return null
Expand Down Expand Up @@ -405,14 +411,6 @@ class AbiParser {
return Pair("%T.DECODER", types)
}

private fun isSolidityDynamicType(type: String) = isSolidityBytesType(type) || isSolidityStringType(type) || isSolidityDynamicArray(type)

private fun isSolidityBytesType(type: String) = type.contains("bytes")

private fun isSolidityStringType(type: String) = type.contains("string")

private fun isSolidityDynamicArray(type: String) = type.contains("[]")

private fun isSolidityDynamicType(type: TypeHolder) = type.isDynamic()

private fun isSolidityArray(type: TypeHolder) = type is CollectionTypeHolder
Expand Down
54 changes: 52 additions & 2 deletions bivrost-abi-parser/src/test/kotlin/pm/gnosis/AbiParserTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.asClassName
import com.squareup.kotlinpoet.asTypeName
import org.junit.Assert
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Assert.*
import org.junit.Test
import pm.gnosis.model.AbiRoot
import pm.gnosis.model.ParameterJson
Expand Down Expand Up @@ -57,6 +56,27 @@ class AbiParserTest {
AbiParser.mapType(testParameter("gnosis[1][]"), testContext())
}

@Test()
fun testSimpleTypeHolder() {
assertEquals("Unknown type should return null", AbiParser.SimpleTypeHolder.forType("unknown"), null)
assertEquals("Tuple type should return null", AbiParser.SimpleTypeHolder.forType("tuple"), null)

val bytes32 = AbiParser.SimpleTypeHolder.forType("bytes32")!!
assertFalse("bytes32 should be static", bytes32.isDynamic())

val uint = AbiParser.SimpleTypeHolder.forType("uint")!!
assertFalse("uint should be static", uint.isDynamic())

val int = AbiParser.SimpleTypeHolder.forType("int")!!
assertFalse("int should be static", int.isDynamic())

val bytes = AbiParser.SimpleTypeHolder.forType("bytes")!!
assertTrue("bytes should be dynamic", bytes.isDynamic())

val string = AbiParser.SimpleTypeHolder.forType("string")!!
assertTrue("string should be dynamic", string.isDynamic())
}

@Test()
fun testParseAliasTypes() {
val uintType = AbiParser.mapType(testParameter("uint"), testContext())
Expand Down Expand Up @@ -160,6 +180,36 @@ class AbiParserTest {
assertEquals(Solidity.String::class.asTypeName(), g1Type.toTypeName())
}

@Test()
fun testParseBytesArray() {
val type = AbiParser.mapType(testParameter("bytes[5]"), testContext())
assertType(type, AbiParser.ArrayTypeHolder::class)
val pType = type as AbiParser.ArrayTypeHolder
assertEquals(5, pType.capacity)
assertTrue(pType.isDynamic())
assertEquals(ClassName("com.example.arrays", "Array5"), pType.listType)

// First generic type
val g1Type = pType.itemType
assertEquals(Solidity.Bytes::class.asTypeName(), g1Type.toTypeName())
assertTrue(g1Type.isDynamic())
}

@Test()
fun testParseBytesXArray() {
val type = AbiParser.mapType(testParameter("bytes32[5]"), testContext())
assertType(type, AbiParser.ArrayTypeHolder::class)
val pType = type as AbiParser.ArrayTypeHolder
assertEquals(5, pType.capacity)
assertFalse(pType.isDynamic())
assertEquals(ClassName("com.example.arrays", "Array5"), pType.listType)

// First generic type
val g1Type = pType.itemType
assertEquals(Solidity.Bytes32::class.asTypeName(), g1Type.toTypeName())
assertFalse(g1Type.isDynamic())
}

@Test
fun testDecodeFunctionArguments() {
/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ object SolidityBase {
const val BYTES_PAD = 32
const val PADDED_HEX_LENGTH = BYTES_PAD * 2

val dynamicTypes: List<kotlin.String> = listOf("bytes", "string")

interface Type {
fun encode(): String
}
Expand Down

0 comments on commit 0ca13ad

Please sign in to comment.