Skip to content

Commit

Permalink
fill blockchain invalid blocks WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
winsvega committed Dec 10, 2019
1 parent e838bee commit ed21d67
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 37 deletions.
7 changes: 7 additions & 0 deletions retesteth/RPCSession.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ class RPCSession: public boost::noncopyable

/// Returns empty string if last RPC call had no errors, error string if there was an error
DataObject const& getLastRPCError() const { return m_lastRPCError; }
string const& getLastRPCErrorMessage() const
{
if (m_lastRPCError.type() != DataType::Null)
return m_lastRPCError.atKey("error").asString();
static const string empty;
return empty;
}
Socket::SocketType getSocketType() const { return m_socket.type(); }
std::string const& getSocketPath() const { return m_socket.path(); }

Expand Down
2 changes: 1 addition & 1 deletion retesteth/TestSuite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,7 @@ void TestSuite::executeTest(string const& _testFolder, fs::path const& _testFile
}
catch (std::exception const& _ex)
{
ETH_STDERROR_MESSAGE("ERROR OCCURED FILLING TESTS: " + string(_ex.what()));
ETH_MARK_ERROR("ERROR OCCURED FILLING TESTS: " + string(_ex.what()));
RPCSession::sessionEnd(TestOutputHelper::getThreadID(), RPCSession::SessionStatus::HasFinished);
wasErrors = true;
}
Expand Down
21 changes: 15 additions & 6 deletions retesteth/configs/ClientConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,18 @@ class ClientConfig : public object
{
m_configFilePath = _clientConfigPath;
requireJsonFields(_obj, "ClientConfig (" + m_configFilePath.string() + ")",
{
{"name", {DataType::String}},
{"socketType", {DataType::String}},
{{"name", {DataType::String}}, {"socketType", {DataType::String}},
{"socketAddress", {DataType::String, DataType::Array}},
{"forks", {DataType::Array}},
{"additionalForks", {DataType::Array}},
},
{"forks", {DataType::Array}}, {"additionalForks", {DataType::Array}},
{"exceptions", {DataType::Object}}},
true);

for (auto const& name : m_data.atKey("forks").getSubObjects())
m_networks.push_back(name.asString());
for (auto const& name : m_data.atKey("additionalForks").getSubObjects())
m_additional_networks.push_back(name.asString());
for (auto const& except : m_data.atKey("exceptions").getSubObjects())
m_exceptions[except.getKey()] = except.asString();

std::string const& socketTypeStr = _obj.atKey("socketType").asString();
if (socketTypeStr == "ipc")
Expand Down Expand Up @@ -96,6 +95,15 @@ class ClientConfig : public object
"\"path to .ipc socket\"], [tcp, \"address:port\"]");
}

std::string const& getExceptionString(string const& _exceptionName) const
{
if (m_exceptions.count(_exceptionName))
return m_exceptions.at(_exceptionName);
ETH_ERROR_MESSAGE(
"Config::getExceptionString '" + _exceptionName + "' not found in client config!");
static string const notfound = "";
return notfound;
}
fs::path const& getConfigFilePath() const { return m_configFilePath; }
fs::path const& getCorrectMiningRewardConfigFilePath() const { return m_configCorrectMiningRewardFilePath; }
fs::path const& getShellPath() const { return m_shellPath; }
Expand Down Expand Up @@ -138,6 +146,7 @@ class ClientConfig : public object
std::vector<string> m_networks; ///< Allowed forks as network name
std::vector<string> m_additional_networks; ///< Allowed forks as network name
std::map<string, DataObject> m_genesisTemplate; ///< Template For test_setChainParams
std::map<string, string> m_exceptions; ///< Exception Translation
DataObject m_correctReward; ///< Correct mining reward info for StateTests->BlockchainTests
};
} // namespace test
33 changes: 10 additions & 23 deletions retesteth/ethObjects/blockchainTest/scheme_blockchainTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ using namespace test;

scheme_blockchainTestBase::fieldChecker::fieldChecker(DataObject const& _test)
{
requireJsonFields(_test, "blockchainTest " + _test.getKey(),
requireJsonFields(_test, "blockchainTestBase " + _test.getKey(),
{{"_info", {{DataType::Object}, jsonField::Optional}},
{"blocks", {{DataType::Array}, jsonField::Required}},
{"expect", {{DataType::Array}, jsonField::Optional}},
Expand All @@ -17,13 +17,12 @@ scheme_blockchainTestBase::fieldChecker::fieldChecker(DataObject const& _test)
{"postState", {{DataType::Object}, jsonField::Optional}},
{"postStateHash", {{DataType::String}, jsonField::Optional}},
{"pre", {{DataType::Object}, jsonField::Required}},
{"sealEngine", {{DataType::String}, jsonField::Optional}}
});
{"sealEngine", {{DataType::String}, jsonField::Optional}}});
}

scheme_blockchainTest::fieldChecker::fieldChecker(DataObject const& _test)
{
requireJsonFields(_test, "blockchainTest " + _test.getKey(),
requireJsonFields(_test, "blockchainTest" + _test.getKey(),
{{"_info", {{DataType::Object}, jsonField::Required}},
{"blocks", {{DataType::Array}, jsonField::Required}},
{"genesisBlockHeader", {{DataType::Object}, jsonField::Required}},
Expand All @@ -33,8 +32,7 @@ scheme_blockchainTest::fieldChecker::fieldChecker(DataObject const& _test)
{"postState", {{DataType::Object}, jsonField::Optional}},
{"postStateHash", {{DataType::String}, jsonField::Optional}},
{"pre", {{DataType::Object}, jsonField::Required}},
{"sealEngine", {{DataType::String}, jsonField::Optional}}
});
{"sealEngine", {{DataType::String}, jsonField::Optional}}});
}

scheme_blockchainTestBase::scheme_blockchainTestBase(DataObject const& _test)
Expand All @@ -59,40 +57,29 @@ scheme_blockchainTest::scheme_blockchainTest(DataObject const& _test)
// Valid block json description
if (blockSection.count("blockHeader"))
{
requireJsonFields(blockSection, "blockchainTest " + _test.getKey(),
{ {"blockHeader", {{DataType::Object}, jsonField::Required}},
requireJsonFields(blockSection, "blockchainTestValidblock " + _test.getKey(),
{{"blockHeader", {{DataType::Object}, jsonField::Required}},
{"rlp", {{DataType::String}, jsonField::Required}},
{"transactions", {{DataType::Array}, jsonField::Required}},
{"uncleHeaders", {{DataType::Array}, jsonField::Optional}},
{"blocknumber", {{DataType::String}, jsonField::Optional}},
{"chainname", {{DataType::String}, jsonField::Optional}},
{"chainnetwork", {{DataType::String}, jsonField::Optional}}
});
{"chainnetwork", {{DataType::String}, jsonField::Optional}}});
scheme_blockHeader(blockSection.atKey("blockHeader"));
for (auto const& trSection: blockSection.atKey("transactions").getSubObjects())
m_transactions.push_back(scheme_transaction(trSection));
}
else
{
// Invalid block json description
requireJsonFields(blockSection, "blockchainTest " + _test.getKey(),
{ {"blockHeader", {{DataType::Object}, jsonField::Optional}},
requireJsonFields(blockSection, "blockchainTestInvalidblock " + _test.getKey(),
{{"blockHeader", {{DataType::Object}, jsonField::Optional}},
{"rlp", {{DataType::String}, jsonField::Required}},
{"transactions", {{DataType::Array}, jsonField::Optional}},
{"uncleHeaders", {{DataType::Array}, jsonField::Optional}},
{"blocknumber", {{DataType::String}, jsonField::Optional}},
{"chainname", {{DataType::String}, jsonField::Optional}},
{"chainnetwork", {{DataType::String}, jsonField::Optional}},
{"expectExceptionByzantium", {{DataType::String}, jsonField::Optional}},
{"expectExceptionConstantinople", {{DataType::String}, jsonField::Optional}},
{"expectExceptionConstantinopleFix", {{DataType::String}, jsonField::Optional}},
{"expectExceptionEIP150", {{DataType::String}, jsonField::Optional}},
{"expectExceptionEIP158", {{DataType::String}, jsonField::Optional}},
{"expectExceptionFrontier", {{DataType::String}, jsonField::Optional}},
{"expectExceptionHomestead", {{DataType::String}, jsonField::Optional}},
{"expectExceptionIstanbul", {{DataType::String}, jsonField::Optional}},
{"expectExceptionALL", {{DataType::String}, jsonField::Optional}}
});
{"chainnetwork", {{DataType::String}, jsonField::Optional}}});
}

m_blockRLPs.push_back(blockSection.atKey("rlp").asString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ using namespace test;

scheme_blockchainTestFiller::fieldChecker::fieldChecker(DataObject const& _test)
{
requireJsonFields(_test, "blockchainTest " + _test.getKey(),
requireJsonFields(_test, "blockchainTestFiller " + _test.getKey(),
{{"_info", {{DataType::Object}, jsonField::Optional}},
{"blocks", {{DataType::Array}, jsonField::Required}},
{"expect", {{DataType::Array}, jsonField::Required}},
Expand All @@ -19,18 +19,30 @@ scheme_blockchainTestFiller::scheme_blockchainTestFiller(DataObject const& _test
m_blocks.push_back(blockSection(bl));
}

scheme_blockchainTestFiller::blockSection::blockSection(DataObject const& _data)
scheme_blockchainTestFiller::blockSection::blockSection(DataObject const& _data) : object(_data)
{
requireJsonFields(_data, "blockchainTest blocks section",
requireJsonFields(_data, "blockchainTestFiller::blocks section",
{{"blockHeader", {{DataType::Object}, jsonField::Optional}},
{"blockHeaderPremine", {{DataType::Object}, jsonField::Optional}},
{"blocknumber", {{DataType::String}, jsonField::Optional}},
{"transactions", {{DataType::Array}, jsonField::Required}},
{"uncleHeaders", {{DataType::Array}, jsonField::Required}}});

for (auto const& tr : _data.atKey("transactions").getSubObjects())
{
m_transactons.push_back(tr);

m_noExceptionString = "NoException";
if (_data.count("blockHeader"))
{
requireJsonFields(_data.atKey("blockHeader"),
"blockchainTestFiller::blocks::blockHeader section",
{{"extraData", {{DataType::String}, jsonField::Optional}},
{"updatePoW", {{DataType::String}, jsonField::Required}},
{"expectException", {{DataType::Object}, jsonField::Required}}});

for (auto const& expect :
_data.atKey("blockHeader").atKey("expectException").getSubObjects())
m_expectException[expect.getKey()] = expect.asString();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,22 @@ namespace test
class scheme_blockchainTestFiller : public scheme_blockchainTestBase
{
public:
class blockSection
class blockSection : public object
{
public:
blockSection(DataObject const& _data);
std::vector<scheme_transaction> const& getTransactions() const { return m_transactons; }
string const& getException(string const& _network) const
{
if (m_expectException.count(_network))
return m_expectException.at(_network);
return m_noExceptionString;
}

private:
std::vector<scheme_transaction> m_transactons;
std::map<string, string> m_expectException;
string m_noExceptionString;
};

public:
Expand Down
21 changes: 21 additions & 0 deletions retesteth/ethObjects/rpcResponse/scheme_block.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,27 @@ class scheme_block : public object
return header;
}

void overwriteBlockHeader(DataObject const& _header)
{
m_data["logsBloom"] = _header.atKey("bloom").asString();
m_data["author"] = _header.atKey("coinbase").asString();
m_data["difficulty"] = _header.atKey("difficulty");
m_data["extraData"] = _header.atKey("extraData");
m_data["gasLimit"] = _header.atKey("gasLimit");
m_data["gasUsed"] = _header.atKey("gasUsed");
m_data["hash"] = _header.atKey("hash");
m_data["mixHash"] = _header.atKey("mixHash");
m_data["nonce"] = _header.atKey("nonce");

m_data["number"] = _header.atKey("number");
m_data["parentHash"] = _header.atKey("parentHash");
m_data["receiptsRoot"] = _header.atKey("receiptTrie").asString();
m_data["stateRoot"] = _header.atKey("stateRoot");
m_data["timestamp"] = _header.atKey("timestamp");
m_data["transactionsRoot"] = _header.atKey("transactionsTrie").asString();
m_data["sha3Uncles"] = _header.atKey("uncleHash").asString();
}

// Get Block RLP for state tests
std::string getBlockRLP() const
{
Expand Down
63 changes: 61 additions & 2 deletions retesteth/testSuites/blockchain/BlockchainTestLogic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
#include <retesteth/RPCSession.h>
#include <retesteth/testSuites/Common.h>

void premineBlockHeader(
RPCSession& _session, scheme_blockchainTestFiller::blockSection const& _block);
void postmineBlockHeader(RPCSession& _session,
scheme_blockchainTestFiller::blockSection const& _block, string& _latestBlockNumber,
string const& _network);

namespace test
{
/// Generate blockchain test from filler
Expand Down Expand Up @@ -34,19 +40,27 @@ void FillTest(scheme_blockchainTestFiller const& _testObject, string const& _net
TestInfo errorInfo (_network, number++);
TestOutputHelper::get().setCurrentTestInfo(errorInfo);

// premineBlockHeader(session, block);
DataObject blockSection;
for (auto const& tr : block.getTransactions())
{
session.eth_sendRawTransaction(tr.getSignedRLP());
blockSection["transactions"].addArrayObject(tr.getDataForBCTest());
}
string latestBlockNumber = session.test_mineBlocks(1);
postmineBlockHeader(session, block, latestBlockNumber, _network);
std::cerr << "trCountBeforeMining "
<< session.eth_getBlockByNumber(latestBlockNumber, true).getData().asJson()
<< std::endl;
return;

// SOME TRANSACTIONS MIGHT BE EXPECTED TO FAIL
latestBlock = session.eth_getBlockByNumber(latestBlockNumber, true);
ETH_ERROR_REQUIRE_MESSAGE(
latestBlock.getTransactionCount() == block.getTransactions().size(),
"BlockchainTest transaction execution failed! ");
"BlockchainTest transaction execution failed! (remote " +
toString(latestBlock.getTransactionCount()) + " != test " +
toString(block.getTransactions().size()) + ")");
blockSection["rlp"] = latestBlock.getBlockRLP();
blockSection["blockHeader"] = latestBlock.getBlockHeader();
_testOut["blocks"].addArrayObject(blockSection);
Expand Down Expand Up @@ -164,6 +178,51 @@ DataObject DoTests(DataObject const& _input, TestSuite::TestSuiteOptions& _opt)
TestOutputHelper::get().registerTestRunSuccess();
return tests;
}
} // namespace test

void premineBlockHeader(RPCSession&, scheme_blockchainTestFiller::blockSection const&) {}

void postmineBlockHeader(RPCSession& _session,
scheme_blockchainTestFiller::blockSection const& _block, string& _latestBlockNumber,
string const& _network)
{
if (!_block.getData().count("blockHeader"))
return;

test::scheme_block remoteBlock = _session.eth_getBlockByNumber(_latestBlockNumber, true);
DataObject header = remoteBlock.getBlockHeader();

} // namespace closed
for (auto const& replace : _block.getData().atKey("blockHeader").getSubObjects())
{
if (replace.getKey() == "updatePoW")
continue;
if (header.count(replace.getKey()))
header[replace.getKey()] = replace.asString();
else
ETH_STDERROR_MESSAGE("Header overwrite missing field " + replace.getKey());
}

// replace block with overwritten header
remoteBlock.overwriteBlockHeader(header);
_session.test_rewindToBlock((int)u256(fromHex(_latestBlockNumber)) - 1);
_session.test_importRawBlock(remoteBlock.getBlockRLP());

// check malicious block import exception
if (_block.getException(_network) == "NoException")
ETH_ERROR_REQUIRE_MESSAGE(_session.getLastRPCErrorMessage().empty(),
"Postmine block tweak expected no exception!");
else
{
std::string const& clientExceptionString =
Options::get().getDynamicOptions().getCurrentConfig().getExceptionString(
_block.getException(_network));
size_t pos = _session.getLastRPCErrorMessage().find(clientExceptionString);
ETH_ERROR_REQUIRE_MESSAGE(pos != string::npos,
"'" + clientExceptionString + "' (" + _block.getException(_network) +
") not found in client response to postmine block tweak!");
}
std::cerr << "IMPORT RESULT STRING " << _session.getLastRPCErrorMessage() << std::endl;
std::cerr << "EXPECT EXCEPTION STRING " << _block.getException(_network) << std::endl;
_latestBlockNumber = _session.eth_blockNumber();
// malicious block must be written to the test!!!
}

0 comments on commit ed21d67

Please sign in to comment.