Merge pull request #5775 from ethereum/codeAccess

Provide access to code of contract types.
This commit is contained in:
chriseth
2019-01-18 00:16:06 +01:00
committed by GitHub
51 changed files with 641 additions and 72 deletions
@@ -277,7 +277,7 @@
"name" : "this",
"nodeType" : "Identifier",
"overloadedDeclarations" : [],
"referencedDeclaration" : 65,
"referencedDeclaration" : 66,
"src" : "217:4:1",
"typeDescriptions" :
{
@@ -424,7 +424,7 @@
[
null
],
"referencedDeclaration" : 65,
"referencedDeclaration" : 66,
"type" : "contract C",
"value" : "this"
},
+2 -1
View File
@@ -46,6 +46,7 @@ namespace dev
{
namespace solidity
{
class Contract;
namespace test
{
@@ -84,7 +85,7 @@ eth::AssemblyItems compileContract(std::shared_ptr<CharStream> _sourceCode)
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
Compiler compiler(dev::test::Options::get().evmVersion());
compiler.compileContract(*contract, map<ContractDefinition const*, Assembly const*>{}, bytes());
compiler.compileContract(*contract, map<ContractDefinition const*, shared_ptr<Compiler const>>{}, bytes());
return compiler.runtimeAssemblyItems();
}
+107
View File
@@ -14234,6 +14234,113 @@ BOOST_AUTO_TEST_CASE(base_access_to_function_type_variables)
ABI_CHECK(callContractFunction("h()"), encodeArgs(2));
}
BOOST_AUTO_TEST_CASE(code_access)
{
char const* sourceCode = R"(
contract C {
function lengths() public pure returns (bool) {
uint crLen = type(D).creationCode.length;
uint runLen = type(D).runtimeCode.length;
require(runLen < crLen);
require(crLen >= 0x20);
require(runLen >= 0x20);
return true;
}
function creation() public pure returns (bytes memory) {
return type(D).creationCode;
}
function runtime() public pure returns (bytes memory) {
return type(D).runtimeCode;
}
function runtimeAllocCheck() public pure returns (bytes memory) {
uint[] memory a = new uint[](2);
bytes memory c = type(D).runtimeCode;
uint[] memory b = new uint[](2);
a[0] = 0x1111;
a[1] = 0x2222;
b[0] = 0x3333;
b[1] = 0x4444;
return c;
}
}
contract D {
function f() public pure returns (uint) { return 7; }
}
)";
compileAndRun(sourceCode, 0, "C");
ABI_CHECK(callContractFunction("lengths()"), encodeArgs(true));
bytes codeCreation = callContractFunction("creation()");
bytes codeRuntime1 = callContractFunction("runtime()");
bytes codeRuntime2 = callContractFunction("runtimeAllocCheck()");
ABI_CHECK(codeRuntime1, codeRuntime2);
}
BOOST_AUTO_TEST_CASE(code_access_create)
{
char const* sourceCode = R"(
contract C {
function test() public returns (uint) {
bytes memory c = type(D).creationCode;
D d;
assembly {
d := create(0, add(c, 0x20), mload(c))
}
return d.f();
}
}
contract D {
uint x;
constructor() public { x = 7; }
function f() public view returns (uint) { return x; }
}
)";
compileAndRun(sourceCode, 0, "C");
ABI_CHECK(callContractFunction("test()"), encodeArgs(7));
}
BOOST_AUTO_TEST_CASE(code_access_content)
{
char const* sourceCode = R"(
contract C {
function testRuntime() public returns (bool) {
D d = new D();
bytes32 runtimeHash = keccak256(type(D).runtimeCode);
bytes32 otherHash;
uint size;
assembly {
size := extcodesize(d)
extcodecopy(d, mload(0x40), 0, size)
otherHash := keccak256(mload(0x40), size)
}
require(size == type(D).runtimeCode.length);
require(runtimeHash == otherHash);
return true;
}
function testCreation() public returns (bool) {
D d = new D();
bytes32 creationHash = keccak256(type(D).creationCode);
require(creationHash == d.x());
return true;
}
}
contract D {
bytes32 public x;
constructor() public {
bytes32 codeHash;
assembly {
let size := codesize()
codecopy(mload(0x40), 0, size)
codeHash := keccak256(mload(0x40), size)
}
x = codeHash;
}
}
)";
compileAndRun(sourceCode, 0, "C");
ABI_CHECK(callContractFunction("testRuntime()"), encodeArgs(true));
ABI_CHECK(callContractFunction("testCreation()"), encodeArgs(true));
}
BOOST_AUTO_TEST_SUITE_END()
}
-1
View File
@@ -539,7 +539,6 @@ BOOST_AUTO_TEST_CASE(keyword_is_reserved)
"supports",
"switch",
"try",
"type",
"typedef",
"typeof",
"unchecked"
@@ -0,0 +1,12 @@
contract Test {
function creationOther() public pure returns (bytes memory) {
return type(Other).creationCode;
}
function runtimeOther() public pure returns (bytes memory) {
return type(Other).runtimeCode;
}
}
contract Other {
function f(uint) public pure returns (uint) {}
}
// ----
@@ -0,0 +1,10 @@
contract Test {
function creationOther() public pure returns (bytes memory) {
return type(Other).creationCode;
}
}
contract Other {
function f(uint) public returns (uint);
}
// ----
// TypeError: (97-121): Member "creationCode" not found or not visible after argument-dependent lookup in type(contract Other).
@@ -0,0 +1,10 @@
contract Test {
function runtime() public pure returns (bytes memory) {
return type(Other).runtimeCode;
}
}
contract Other {
function f(uint) public returns (uint);
}
// ----
// TypeError: (91-114): Member "runtimeCode" not found or not visible after argument-dependent lookup in type(contract Other).
@@ -0,0 +1,22 @@
contract Base {
function f() public pure returns (uint) {}
}
contract Test is Base {
function creation() public pure returns (bytes memory) {
return type(Test).creationCode;
}
function runtime() public pure returns (bytes memory) {
return type(Test).runtimeCode;
}
function creationBase() public pure returns (bytes memory) {
return type(Base).creationCode;
}
function runtimeBase() public pure returns (bytes memory) {
return type(Base).runtimeCode;
}
}
// ----
// TypeError: (165-188): Circular reference for contract code access.
// TypeError: (271-293): Circular reference for contract code access.
// TypeError: (381-404): Circular reference for contract code access.
// TypeError: (491-513): Circular reference for contract code access.
@@ -0,0 +1,12 @@
contract A {
function f() public pure {
type(B).runtimeCode;
}
}
contract B {
function f() public pure {
type(A).runtimeCode;
}
}
// ----
// TypeError: (133-152): Circular reference for contract code access.
@@ -0,0 +1,7 @@
contract Test {
bytes constant c = type(B).creationCode;
bytes constant r = type(B).runtimeCode;
}
contract B { function f() public pure {} }
// ----
@@ -0,0 +1,12 @@
contract Test {
function creationOther() public pure returns (bytes memory) {
return type(Library).creationCode;
}
function runtime() public pure returns (bytes memory) {
return type(Library).runtimeCode;
}
}
contract Library {
function f(uint) public pure returns (uint) {}
}
// ----
@@ -0,0 +1,10 @@
contract Test {
function f() public pure {
type(C).creationCode = new bytes(6);
type(C).runtimeCode = new bytes(6);
}
}
contract C {}
// ----
// TypeError: (55-75): Expression has to be an lvalue.
// TypeError: (100-119): Expression has to be an lvalue.
@@ -0,0 +1,7 @@
contract Test {
function creation() public pure returns (bytes memory) {
type();
}
}
// ----
// TypeError: (85-91): This function takes one argument, but 0 were provided.
@@ -0,0 +1,17 @@
contract Test {
function f() public pure returns (uint) {
return type(C).runtimeCode.length +
type(D).runtimeCode.length +
type(C).creationCode.length +
type(D).creationCode.length;
}
}
contract C {
constructor() public { assembly {} }
}
contract D is C {
constructor() public {}
}
// ----
// Warning: (77-96): The constructor of the contract (or its base) uses inline assembly. Because of that, it might be that the deployed bytecode is different from type(...).runtimeCode.
// Warning: (118-137): The constructor of the contract (or its base) uses inline assembly. Because of that, it might be that the deployed bytecode is different from type(...).runtimeCode.
@@ -0,0 +1,7 @@
contract Test {
function creation() public pure returns (bytes memory) {
type(1, 2);
}
}
// ----
// TypeError: (85-95): This function takes one argument, but 2 were provided.
@@ -0,0 +1,3 @@
contract type { }
// ----
// ParserError: (9-13): Expected identifier but got 'type'
@@ -0,0 +1,6 @@
contract Test {
function type() public pure {
}
}
// ----
// ParserError: (29-33): Expected identifier but got 'type'
@@ -0,0 +1,6 @@
contract Test {
function f(uint type) public pure {
}
}
// ----
// ParserError: (36-40): Expected ',' but got 'type'
@@ -0,0 +1,5 @@
contract Test {
uint type;
}
// ----
// ParserError: (25-29): Expected identifier but got 'type'
@@ -0,0 +1,7 @@
contract Test {
function f() public pure {
uint type;
}
}
// ----
// ParserError: (60-64): Expected ';' but got 'type'
@@ -0,0 +1,6 @@
contract Test {
function f() public pure returns (bytes memory) {
type(Test);
}
}
// ----
@@ -0,0 +1,8 @@
contract Test {
function f() public pure {
type(type(type(Test)));
}
}
// ----
// TypeError: (65-75): Invalid type for argument in function call. Contract type required, but type(contract Test) provided.
// TypeError: (60-76): Invalid type for argument in function call. Contract type required, but tuple() provided.
@@ -0,0 +1,9 @@
contract Test {
struct S { uint x; }
function f() public pure {
// Unsupported for now, but might be supported in the future
type(S);
}
}
// ----
// TypeError: (154-155): Invalid type for argument in function call. Contract type required, but type(struct Test.S) provided.