Skip to content

Commit

Permalink
Function to check that TypeNode is ABI-encodable (#209)
Browse files Browse the repository at this point in the history
* Introduce InferType.isABIEncodable() method and related unit test. Tweak InferType.toABIEncodedType() to check if type is encodable. Other small fixes.

* Address review remark: invert logic of InferType.isABIEncodable() to explicitl list allowed types instead of listing disallowed ones.

* Fix test due to contract constructor signature change
  • Loading branch information
blitz-1306 authored May 31, 2023
1 parent e2439c7 commit 1e5c4a1
Show file tree
Hide file tree
Showing 4 changed files with 410 additions and 22 deletions.
3 changes: 2 additions & 1 deletion src/types/ast/import_ref_type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ export class ImportRefType extends TypeNode {

assert(
importStmt.vSymbolAliases.length === 0 && importStmt.unitAlias !== "",
`ImportRefTypes only applicable to unit alias imports, not ${importStmt.print()}`
"ImportRefTypes only applicable to unit alias imports, not {0}",
importStmt
);

this.importStmt = importStmt;
Expand Down
84 changes: 64 additions & 20 deletions src/types/infer.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
import { Decimal } from "decimal.js";
import { gte, lt } from "semver";
import {
ASTNode,
AnyResolvable,
ArrayTypeName,
Assignment,
ASTNode,
BinaryOperation,
Conditional,
ContractDefinition,
ContractKind,
ElementaryTypeName,
ElementaryTypeNameExpression,
encodeEventSignature,
encodeFuncSignature,
EnumDefinition,
ErrorDefinition,
EventDefinition,
Expand All @@ -37,7 +35,6 @@ import {
ModifierDefinition,
NewExpression,
ParameterList,
resolveAny,
SourceUnit,
StateVariableVisibility,
StructDefinition,
Expand All @@ -48,7 +45,10 @@ import {
UserDefinedTypeName,
UserDefinedValueTypeDefinition,
VariableDeclaration,
VariableDeclarationStatement
VariableDeclarationStatement,
encodeEventSignature,
encodeFuncSignature,
resolveAny
} from "../ast";
import { DataLocation } from "../ast/constants";
import { assert, eq, forAll, forAny, pp } from "../misc";
Expand Down Expand Up @@ -94,25 +94,25 @@ import {
} from "./builtins";
import { evalConstantExpr } from "./eval_const";
import { SolTypeError } from "./misc";
import { applySubstitution, buildSubstitutions, TypeSubstituion } from "./polymorphic";
import { TypeSubstituion, applySubstitution, buildSubstitutions } from "./polymorphic";
import { types } from "./reserved";
import {
BINARY_OPERATOR_GROUPS,
SUBDENOMINATION_MULTIPLIERS,
castable,
decimalToRational,
enumToIntType,
generalizeType,
getABIEncoderVersion,
getFallbackRecvFuns,
getFQDefName,
getFallbackRecvFuns,
inferCommonVisiblity,
isReferenceType,
isVisiblityExternallyCallable,
mergeFunTypes,
smallestFittingType,
specializeType,
stripSingletonParens,
SUBDENOMINATION_MULTIPLIERS
stripSingletonParens
} from "./utils";

const unaryImpureOperators = ["++", "--"];
Expand Down Expand Up @@ -201,7 +201,7 @@ function isSupportedByEncoderV1(type: TypeNode): boolean {
const [baseT] = generalizeType(type.elementT);

return (
isSupportedByEncoderV1(baseT) ||
isSupportedByEncoderV1(baseT) &&
!(baseT instanceof ArrayType && baseT.size === undefined)
);
}
Expand Down Expand Up @@ -2341,6 +2341,54 @@ export class InferType {
return false;
}

isABIEncodable(type: TypeNode, encoderVersion: ABIEncoderVersion): boolean {
if (
type instanceof AddressType ||
type instanceof BoolType ||
type instanceof BytesType ||
type instanceof FixedBytesType ||
(type instanceof FunctionType &&
(type.visibility === FunctionVisibility.External ||
type.visibility === FunctionVisibility.Public)) ||
type instanceof IntType ||
type instanceof IntLiteralType ||
type instanceof StringLiteralType ||
type instanceof StringType
) {
return true;
}

if (type instanceof PointerType) {
return this.isABIEncodable(type.to, encoderVersion);
}

if (encoderVersion === ABIEncoderVersion.V1 && !isSupportedByEncoderV1(type)) {
return false;
}

if (type instanceof ArrayType) {
return this.isABIEncodable(type.elementT, encoderVersion);
}

if (type instanceof UserDefinedType) {
if (
type.definition instanceof ContractDefinition ||
type.definition instanceof EnumDefinition ||
type.definition instanceof UserDefinedValueTypeDefinition
) {
return true;
}

if (type.definition instanceof StructDefinition) {
return type.definition.vMembers.every((field) =>
this.isABIEncodable(this.variableDeclarationToTypeNode(field), encoderVersion)
);
}
}

return false;
}

/**
* Convert an internal TypeNode to the external TypeNode that would correspond to it
* after ABI-encoding with encoder version `encoderVersion`. Follows the following rules:
Expand All @@ -2359,9 +2407,12 @@ export class InferType {
encoderVersion: ABIEncoderVersion,
normalizePointers = false
): TypeNode {
if (type instanceof MappingType) {
throw new Error("Cannot abi-encode mapping types");
}
assert(
this.isABIEncodable(type, encoderVersion),
'Can not ABI-encode type "{0}" with encoder "{1}"',
type,
encoderVersion
);

if (type instanceof ArrayType) {
const elT = this.toABIEncodedType(type.elementT, encoderVersion);
Expand Down Expand Up @@ -2401,13 +2452,6 @@ export class InferType {
}

if (type.definition instanceof StructDefinition) {
assert(
encoderVersion !== ABIEncoderVersion.V1 || isSupportedByEncoderV1(type),
"Type {0} is not supported by encoder {1}",
type,
encoderVersion
);

const fieldTs = type.definition.vMembers.map((fieldT) =>
this.variableDeclarationToTypeNode(fieldT)
);
Expand Down
Loading

0 comments on commit 1e5c4a1

Please sign in to comment.