Browse Source

Merge remote-tracking branch 'upstream/develop' into newTests

Conflicts:
	test/vm.cpp
cl-refactor
Christoph Jentzsch 10 years ago
parent
commit
42c26ab127
  1. 1
      libdevcore/FixedHash.h
  2. 9
      libdevcrypto/Common.cpp
  3. 10
      libdevcrypto/Common.h
  4. 4
      libethereum/State.cpp
  5. 4
      libethereum/Transaction.cpp
  6. 93
      libsolidity/Compiler.cpp
  7. 13
      libsolidity/Compiler.h
  8. 1
      test/TestHelper.cpp
  9. 35
      test/recursiveCreateFiller.json
  10. 22
      test/solidityEndToEndTest.cpp
  11. 85
      test/vm.cpp

1
libdevcore/FixedHash.h

@ -83,7 +83,6 @@ public:
bool operator==(FixedHash const& _c) const { return m_data == _c.m_data; }
bool operator!=(FixedHash const& _c) const { return m_data != _c.m_data; }
bool operator<(FixedHash const& _c) const { return m_data < _c.m_data; }
bool operator>=(FixedHash const& _c) const { return m_data >= _c.m_data; }
// The obvious binary operators.
FixedHash& operator^=(FixedHash const& _c) { for (unsigned i = 0; i < N; ++i) m_data[i] ^= _c.m_data[i]; return *this; }

9
libdevcrypto/Common.cpp

@ -33,15 +33,6 @@ using namespace dev::crypto;
static Secp256k1 s_secp256k1;
bool dev::SignatureStruct::isValid()
{
if (this->v > 1 ||
this->r >= h256("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141") ||
this->s >= h256("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"))
return false;
return true;
}
Public dev::toPublic(Secret const& _secret)
{
Public p;

10
libdevcrypto/Common.h

@ -43,15 +43,7 @@ using Public = h512;
/// @NOTE This is not endian-specific; it's just a bunch of bytes.
using Signature = h520;
struct SignatureStruct
{
/// @returns true if r,s,v values are valid, otherwise false
bool isValid();
h256 r;
h256 s;
byte v;
};
struct SignatureStruct { h256 r; h256 s; byte v; };
/// An Ethereum address: 20 bytes.
/// @NOTE This is not endian-specific; it's just a bunch of bytes.

4
libethereum/State.cpp

@ -55,10 +55,6 @@ void ecrecoverCode(bytesConstRef _in, bytesRef _out)
memcpy(&in, _in.data(), min(_in.size(), sizeof(in)));
SignatureStruct sig{in.r, in.s, (byte)((int)(u256)in.v - 27)};
if (!sig.isValid() || in.v > 28)
return;
byte pubkey[65];
int pubkeylen = 65;
secp256k1_start();

4
libethereum/Transaction.cpp

@ -82,9 +82,7 @@ Address Transaction::sender() const
void Transaction::sign(Secret _priv)
{
auto sig = dev::sign(_priv, sha3(WithoutSignature));
SignatureStruct sigStruct = *(SignatureStruct const*)&sig;
if (sigStruct.isValid())
m_vrs = sigStruct;
m_vrs = *(SignatureStruct const*)&sig;
}
void Transaction::streamRLP(RLPStream& _s, IncludeSignature _sig) const

93
libsolidity/Compiler.cpp

@ -43,42 +43,59 @@ void Compiler::compileContract(ContractDefinition& _contract)
{
m_context = CompilerContext(); // clear it just in case
//@todo constructor
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
m_context.addFunction(*function);
//@todo sort them?
for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables())
m_context.addStateVariable(*variable);
if (function->getName() != _contract.getName()) // don't add the constructor here
m_context.addFunction(*function);
registerStateVariables(_contract);
appendFunctionSelector(_contract.getDefinedFunctions());
appendFunctionSelector(_contract);
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
function->accept(*this);
if (function->getName() != _contract.getName()) // don't add the constructor here
function->accept(*this);
packIntoContractCreator();
packIntoContractCreator(_contract);
}
void Compiler::packIntoContractCreator()
void Compiler::packIntoContractCreator(ContractDefinition const& _contract)
{
CompilerContext creatorContext;
eth::AssemblyItem sub = creatorContext.addSubroutine(m_context.getAssembly());
CompilerContext runtimeContext;
swap(m_context, runtimeContext);
registerStateVariables(_contract);
FunctionDefinition* constructor = nullptr;
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
if (function->getName() == _contract.getName())
{
constructor = function.get();
break;
}
if (constructor)
{
eth::AssemblyItem returnTag = m_context.pushNewTag();
m_context.addFunction(*constructor); // note that it cannot be called due to syntactic reasons
//@todo copy constructor arguments from calldata to memory prior to this
//@todo calling other functions inside the constructor should either trigger a parse error
//or we should copy them here (register them above and call "accept") - detecting which
// functions are referenced / called needs to be done in a recursive way.
appendCalldataUnpacker(*constructor, true);
m_context.appendJumpTo(m_context.getFunctionEntryLabel(*constructor));
constructor->accept(*this);
m_context << returnTag;
}
eth::AssemblyItem sub = m_context.addSubroutine(runtimeContext.getAssembly());
// stack contains sub size
creatorContext << eth::Instruction::DUP1 << sub << u256(0) << eth::Instruction::CODECOPY;
creatorContext << u256(0) << eth::Instruction::RETURN;
swap(m_context, creatorContext);
m_context << eth::Instruction::DUP1 << sub << u256(0) << eth::Instruction::CODECOPY;
m_context << u256(0) << eth::Instruction::RETURN;
}
void Compiler::appendFunctionSelector(vector<ASTPointer<FunctionDefinition>> const& _functions)
void Compiler::appendFunctionSelector(ContractDefinition const& _contract)
{
// sort all public functions and store them together with a tag for their argument decoding section
map<string, pair<FunctionDefinition const*, eth::AssemblyItem>> publicFunctions;
for (ASTPointer<FunctionDefinition> const& f: _functions)
if (f->isPublic())
publicFunctions.insert(make_pair(f->getName(), make_pair(f.get(), m_context.newTag())));
vector<FunctionDefinition const*> interfaceFunctions = _contract.getInterfaceFunctions();
vector<eth::AssemblyItem> callDataUnpackerEntryPoints;
//@todo remove constructor
if (publicFunctions.size() > 255)
if (interfaceFunctions.size() > 255)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("More than 255 public functions for contract."));
// retrieve the first byte of the call data, which determines the called function
@ -90,21 +107,20 @@ void Compiler::appendFunctionSelector(vector<ASTPointer<FunctionDefinition>> con
<< eth::dupInstruction(2);
// stack here: 1 0 <funid> 0, stack top will be counted up until it matches funid
for (pair<string, pair<FunctionDefinition const*, eth::AssemblyItem>> const& f: publicFunctions)
for (unsigned funid = 0; funid < interfaceFunctions.size(); ++funid)
{
eth::AssemblyItem const& callDataUnpackerEntry = f.second.second;
callDataUnpackerEntryPoints.push_back(m_context.newTag());
m_context << eth::dupInstruction(2) << eth::dupInstruction(2) << eth::Instruction::EQ;
m_context.appendConditionalJumpTo(callDataUnpackerEntry);
m_context.appendConditionalJumpTo(callDataUnpackerEntryPoints.back());
m_context << eth::dupInstruction(4) << eth::Instruction::ADD;
//@todo avoid the last ADD (or remove it in the optimizer)
}
m_context << eth::Instruction::STOP; // function not found
for (pair<string, pair<FunctionDefinition const*, eth::AssemblyItem>> const& f: publicFunctions)
for (unsigned funid = 0; funid < interfaceFunctions.size(); ++funid)
{
FunctionDefinition const& function = *f.second.first;
eth::AssemblyItem const& callDataUnpackerEntry = f.second.second;
m_context << callDataUnpackerEntry;
FunctionDefinition const& function = *interfaceFunctions[funid];
m_context << callDataUnpackerEntryPoints[funid];
eth::AssemblyItem returnTag = m_context.pushNewTag();
appendCalldataUnpacker(function);
m_context.appendJumpTo(m_context.getFunctionEntryLabel(function));
@ -113,10 +129,11 @@ void Compiler::appendFunctionSelector(vector<ASTPointer<FunctionDefinition>> con
}
}
void Compiler::appendCalldataUnpacker(FunctionDefinition const& _function)
unsigned Compiler::appendCalldataUnpacker(FunctionDefinition const& _function, bool _fromMemory)
{
// We do not check the calldata size, everything is zero-padded.
unsigned dataOffset = 1;
eth::Instruction load = _fromMemory ? eth::Instruction::MLOAD : eth::Instruction::CALLDATALOAD;
//@todo this can be done more efficiently, saving some CALLDATALOAD calls
for (ASTPointer<VariableDeclaration> const& var: _function.getParameters())
@ -127,12 +144,13 @@ void Compiler::appendCalldataUnpacker(FunctionDefinition const& _function)
<< errinfo_sourceLocation(var->getLocation())
<< errinfo_comment("Type " + var->getType()->toString() + " not yet supported."));
if (numBytes == 32)
m_context << u256(dataOffset) << eth::Instruction::CALLDATALOAD;
m_context << u256(dataOffset) << load;
else
m_context << (u256(1) << ((32 - numBytes) * 8)) << u256(dataOffset)
<< eth::Instruction::CALLDATALOAD << eth::Instruction::DIV;
<< load << eth::Instruction::DIV;
dataOffset += numBytes;
}
return dataOffset;
}
void Compiler::appendReturnValuePacker(FunctionDefinition const& _function)
@ -158,6 +176,13 @@ void Compiler::appendReturnValuePacker(FunctionDefinition const& _function)
m_context << u256(dataOffset) << u256(0) << eth::Instruction::RETURN;
}
void Compiler::registerStateVariables(ContractDefinition const& _contract)
{
//@todo sort them?
for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables())
m_context.addStateVariable(*variable);
}
bool Compiler::visit(FunctionDefinition& _function)
{
//@todo to simplify this, the calling convention could by changed such that

13
libsolidity/Compiler.h

@ -40,12 +40,17 @@ public:
static bytes compile(ContractDefinition& _contract, bool _optimize);
private:
/// Creates a new compiler context / assembly and packs the current code into the data part.
void packIntoContractCreator();
void appendFunctionSelector(std::vector<ASTPointer<FunctionDefinition> > const& _functions);
void appendCalldataUnpacker(FunctionDefinition const& _function);
/// Creates a new compiler context / assembly, packs the current code into the data part and
/// adds the constructor code.
void packIntoContractCreator(ContractDefinition const& _contract);
void appendFunctionSelector(ContractDefinition const& _contract);
/// Creates code that unpacks the arguments for the given function, from memory if
/// @a _fromMemory is true, otherwise from call data. @returns the size of the data in bytes.
unsigned appendCalldataUnpacker(FunctionDefinition const& _function, bool _fromMemory = false);
void appendReturnValuePacker(FunctionDefinition const& _function);
void registerStateVariables(ContractDefinition const& _contract);
virtual bool visit(FunctionDefinition& _function) override;
virtual bool visit(IfStatement& _ifStatement) override;
virtual bool visit(WhileStatement& _whileStatement) override;

1
test/TestHelper.cpp

@ -386,6 +386,7 @@ void executeTests(const string& _name, const string& _testPathAppendix, std::fun
{
BOOST_ERROR("Failed test with Exception: " << _e.what());
}
break;
}
}

35
test/recursiveCreateFiller.json

@ -0,0 +1,35 @@
{
"recursiveCreate": {
"env": {
"previousHash": "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber": "0",
"currentGasLimit": "10000000",
"currentDifficulty": "256",
"currentTimestamp": 1,
"currentCoinbase": "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre": {
"095e7baea6a6c7c4c2dfeb977efac326af552d87": {
"balance": "20000000",
"nonce": 0,
"code": "{(CODECOPY 0 0 32)(CREATE 0 0 32)}",
"storage": {}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
"balance": "1000000000000000000",
"nonce": 0,
"code": "",
"storage": {}
}
},
"transaction": {
"nonce": "0",
"gasPrice": "1",
"gasLimit": "465224",
"to": "095e7baea6a6c7c4c2dfeb977efac326af552d87",
"value": "100000",
"secretKey": "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"data": ""
}
}
}

22
test/solidityEndToEndTest.cpp

@ -700,6 +700,28 @@ BOOST_AUTO_TEST_CASE(structs)
BOOST_CHECK(callContractFunction(0) == bytes({0x01}));
}
BOOST_AUTO_TEST_CASE(constructor)
{
char const* sourceCode = "contract test {\n"
" mapping(uint => uint) data;\n"
" function test() {\n"
" data[7] = 8;\n"
" }\n"
" function get(uint key) returns (uint value) {\n"
" return data[key];"
" }\n"
"}\n";
compileAndRun(sourceCode);
map<u256, byte> data;
data[7] = 8;
auto get = [&](u256 const& _x) -> u256
{
return data[_x];
};
testSolidityAgainstCpp(0, get, u256(6));
testSolidityAgainstCpp(0, get, u256(7));
}
BOOST_AUTO_TEST_SUITE_END()
}

85
test/vm.cpp

@ -337,6 +337,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
VM vm(fev.gas);
u256 gas;
bool vmExceptionOccured = false;
try
{
output = vm.go(fev, fev.simpleTrace()).toBytes();
@ -345,7 +346,7 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
catch (VMException const& _e)
{
cnote << "VM did throw an exception: " << diagnostic_information(_e);
gas = 0;
vmExceptionOccured = true;
}
catch (Exception const& _e)
{
@ -379,53 +380,63 @@ void doVMTests(json_spirit::mValue& v, bool _fillin)
{
o["env"] = mValue(fev.exportEnv());
o["exec"] = mValue(fev.exportExec());
o["post"] = mValue(fev.exportState());
o["callcreates"] = fev.exportCallCreates();
o["out"] = "0x" + toHex(output);
fev.push(o, "gas", gas);
o["logs"] = mValue(fev.exportLog());
if (!vmExceptionOccured)
{
o["post"] = mValue(fev.exportState());
o["callcreates"] = fev.exportCallCreates();
o["out"] = "0x" + toHex(output);
fev.push(o, "gas", gas);
o["logs"] = mValue(fev.exportLog());
}
}
else
{
BOOST_REQUIRE(o.count("post") > 0);
BOOST_REQUIRE(o.count("callcreates") > 0);
BOOST_REQUIRE(o.count("out") > 0);
BOOST_REQUIRE(o.count("gas") > 0);
BOOST_REQUIRE(o.count("logs") > 0);
if (o.count("post") > 0) // No exceptions expected
{
BOOST_CHECK(!vmExceptionOccured);
dev::test::FakeExtVM test;
test.importState(o["post"].get_obj());
test.importCallCreates(o["callcreates"].get_array());
test.importLog(o["logs"].get_obj());
BOOST_REQUIRE(o.count("post") > 0);
BOOST_REQUIRE(o.count("callcreates") > 0);
BOOST_REQUIRE(o.count("out") > 0);
BOOST_REQUIRE(o.count("gas") > 0);
BOOST_REQUIRE(o.count("logs") > 0);
checkOutput(output, o);
dev::test::FakeExtVM test;
test.importState(o["post"].get_obj());
test.importCallCreates(o["callcreates"].get_array());
test.importLog(o["logs"].get_obj());
BOOST_CHECK_EQUAL(toInt(o["gas"]), gas);
checkOutput(output, o);
auto& expectedAddrs = test.addresses;
auto& resultAddrs = fev.addresses;
for (auto&& expectedPair : expectedAddrs)
{
auto& expectedAddr = expectedPair.first;
auto resultAddrIt = resultAddrs.find(expectedAddr);
if (resultAddrIt == resultAddrs.end())
BOOST_ERROR("Missing expected address " << expectedAddr);
else
{
auto& expectedState = expectedPair.second;
auto& resultState = resultAddrIt->second;
BOOST_CHECK_MESSAGE(std::get<0>(expectedState) == std::get<0>(resultState), expectedAddr << ": incorrect balance " << std::get<0>(resultState) << ", expected " << std::get<0>(expectedState));
BOOST_CHECK_MESSAGE(std::get<1>(expectedState) == std::get<1>(resultState), expectedAddr << ": incorrect txCount " << std::get<1>(resultState) << ", expected " << std::get<1>(expectedState));
BOOST_CHECK_MESSAGE(std::get<3>(expectedState) == std::get<3>(resultState), expectedAddr << ": incorrect code");
BOOST_CHECK_EQUAL(toInt(o["gas"]), gas);
checkStorage(std::get<2>(expectedState), std::get<2>(resultState), expectedAddr);
auto& expectedAddrs = test.addresses;
auto& resultAddrs = fev.addresses;
for (auto&& expectedPair : expectedAddrs)
{
auto& expectedAddr = expectedPair.first;
auto resultAddrIt = resultAddrs.find(expectedAddr);
if (resultAddrIt == resultAddrs.end())
BOOST_ERROR("Missing expected address " << expectedAddr);
else
{
auto& expectedState = expectedPair.second;
auto& resultState = resultAddrIt->second;
BOOST_CHECK_MESSAGE(std::get<0>(expectedState) == std::get<0>(resultState), expectedAddr << ": incorrect balance " << std::get<0>(resultState) << ", expected " << std::get<0>(expectedState));
BOOST_CHECK_MESSAGE(std::get<1>(expectedState) == std::get<1>(resultState), expectedAddr << ": incorrect txCount " << std::get<1>(resultState) << ", expected " << std::get<1>(expectedState));
BOOST_CHECK_MESSAGE(std::get<3>(expectedState) == std::get<3>(resultState), expectedAddr << ": incorrect code");
checkStorage(std::get<2>(expectedState), std::get<2>(resultState), expectedAddr);
}
}
}
checkAddresses<std::map<Address, std::tuple<u256, u256, std::map<u256, u256>, bytes> > >(test.addresses, fev.addresses);
BOOST_CHECK(test.callcreates == fev.callcreates);
checkAddresses<std::map<Address, std::tuple<u256, u256, std::map<u256, u256>, bytes> > >(test.addresses, fev.addresses);
BOOST_CHECK(test.callcreates == fev.callcreates);
checkLog(fev.sub.logs, test.sub.logs);
checkLog(fev.sub.logs, test.sub.logs);
}
else // Exception expected
BOOST_CHECK(vmExceptionOccured);
}
}
}

Loading…
Cancel
Save