Skip to content
This repository has been archived by the owner on Oct 28, 2021. It is now read-only.

Commit

Permalink
add validation schemes
Browse files Browse the repository at this point in the history
  • Loading branch information
winsvega committed May 28, 2018
1 parent 4e071f6 commit 7372e65
Show file tree
Hide file tree
Showing 7 changed files with 241 additions and 134 deletions.
13 changes: 8 additions & 5 deletions libdevcore/JsonUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ std::string dev::jsonTypeAsString(json_spirit::Value_type _type)
}

void dev::requireJsonFields(json_spirit::mObject const& _o, std::string const& _config,
std::map<std::string, possibleJsonType> const& _validationMap,
std::map<std::string, jsonTypeSet> const& _validationMap,
std::set<std::string> const& _ignoreFields)
{
// check for unexpected fiedls
Expand Down Expand Up @@ -85,17 +85,20 @@ void dev::requireJsonFields(json_spirit::mObject const& _o, std::string const& _
}

bool matched = false;
std::string sTypes;
for (auto const& type : vmap.second)
{
if (sTypes.size())
sTypes += ", or ";
sTypes += jsonTypeAsString(type);
if (_o.at(vmap.first).type() == type)
matched = true;
}
if (matched == false)
{
std::string sTypes;
for (auto const& type : vmap.second)
{
if (sTypes.size())
sTypes += ", or ";
sTypes += jsonTypeAsString(type);
}
std::string const comment =
"Field '" + vmap.first + "' expected to be " + sTypes + ", but set to " +
jsonTypeAsString(_o.at(vmap.first).type()) + " in " + _config;
Expand Down
4 changes: 2 additions & 2 deletions libdevcore/JsonUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ std::string jsonTypeAsString(json_spirit::Value_type _type);

// Check json _o with validation map that reuires certain field of certain type to be present in
// json
typedef std::set<json_spirit::Value_type> possibleJsonType;
using jsonTypeSet = std::set<json_spirit::Value_type>;
void requireJsonFields(json_spirit::mObject const& _o, std::string const& _config,
std::map<std::string, possibleJsonType> const& _validationMap,
std::map<std::string, jsonTypeSet> const& _validationMap,
std::set<std::string> const& _ignoreFields = {});
}
31 changes: 6 additions & 25 deletions libethereum/Account.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@
*/

#include "Account.h"
#include "ValidationSchemes.h"
#include <libdevcore/JsonUtils.h>
#include <libethcore/ChainOperationParams.h>
#include <libethcore/Precompiled.h>

using namespace std;
using namespace dev;
using namespace dev::eth;
using namespace dev::eth::validation;

namespace fs = boost::filesystem;

Expand Down Expand Up @@ -81,29 +83,8 @@ PrecompiledContract createPrecompiledContract(js::mObject& _precompiled)
throw;
}
}

}
namespace
{
string const c_wei = "wei";
string const c_finney = "finney";
string const c_balance = "balance";
string const c_nonce = "nonce";
string const c_code = "code";
string const c_codeFromFile = "codeFromFile"; ///< A file containg a code as bytes.
string const c_storage = "storage";
string const c_shouldnotexist = "shouldnotexist";
string const c_precompiled = "precompiled";
std::set<string> const c_knownAccountFields = {
c_wei, c_finney, c_balance, c_nonce, c_code, c_codeFromFile, c_storage, c_shouldnotexist,
c_code, c_precompiled
};
void validateAccountMapObj(js::mObject const& _o)
{
for (auto const& field: _o)
validateFieldNames(field.second.get_obj(), c_knownAccountFields);
}
}

AccountMap dev::eth::jsonToAccountMap(std::string const& _json, u256 const& _defaultNonce,
AccountMaskMap* o_mask, PrecompiledContractMap* o_precompiled, const fs::path& _configPath)
{
Expand All @@ -119,12 +100,12 @@ AccountMap dev::eth::jsonToAccountMap(std::string const& _json, u256 const& _def
js::mValue val;
json_spirit::read_string_or_throw(_json, val);
js::mObject o = val.get_obj();
validateAccountMapObj(o);
for (auto const& account: o)
for (auto const& account: o)
{
Address a(fromHex(account.first));
Address a(fromHex(account.first));
// FIXME: Do not copy every account object.
auto o = account.second.get_obj();
validateAccountMapObj(o);

bool haveBalance = (o.count(c_wei) || o.count(c_finney) || o.count(c_balance));
bool haveNonce = o.count(c_nonce);
Expand Down
110 changes: 10 additions & 100 deletions libethereum/ChainParams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,16 @@
#include <libethcore/SealEngine.h>
#include <libethcore/BlockHeader.h>
#include <libethcore/Precompiled.h>
#include "ValidationSchemes.h"
#include "GenesisInfo.h"
#include "State.h"
#include "Account.h"
using namespace std;
using namespace dev;
using namespace eth;
using namespace eth::validation;
namespace js = json_spirit;


ChainParams::ChainParams()
{
for (unsigned i = 1; i <= 4; ++i)
Expand All @@ -52,79 +53,6 @@ ChainParams::ChainParams(string const& _json, h256 const& _stateRoot)
*this = loadConfig(_json, _stateRoot);
}

namespace
{
string const c_sealEngine = "sealEngine";
string const c_params = "params";
string const c_genesis = "genesis";
string const c_accounts = "accounts";

set<string> const c_knownChainConfigFields =
{c_sealEngine, c_params, c_genesis, c_accounts};

string const c_minGasLimit = "minGasLimit";
string const c_maxGasLimit = "maxGasLimit";
string const c_gasLimitBoundDivisor = "gasLimitBoundDivisor";
string const c_homesteadForkBlock = "homesteadForkBlock";
string const c_daoHardforkBlock = "daoHardforkBlock";
string const c_EIP150ForkBlock = "EIP150ForkBlock";
string const c_EIP158ForkBlock = "EIP158ForkBlock";
string const c_byzantiumForkBlock = "byzantiumForkBlock";
string const c_eWASMForkBlock = "eWASMForkBlock";
string const c_constantinopleForkBlock = "constantinopleForkBlock";
string const c_accountStartNonce = "accountStartNonce";
string const c_maximumExtraDataSize = "maximumExtraDataSize";
string const c_tieBreakingGas = "tieBreakingGas";
string const c_blockReward = "blockReward";
string const c_difficultyBoundDivisor = "difficultyBoundDivisor";
string const c_minimumDifficulty = "minimumDifficulty";
string const c_durationLimit = "durationLimit";
string const c_chainID = "chainID";
string const c_networkID = "networkID";
string const c_allowFutureBlocks = "allowFutureBlocks";

set<string> const c_knownParamNames = {c_minGasLimit, c_maxGasLimit, c_gasLimitBoundDivisor,
c_homesteadForkBlock, c_EIP150ForkBlock, c_EIP158ForkBlock, c_accountStartNonce,
c_maximumExtraDataSize, c_tieBreakingGas, c_blockReward, c_byzantiumForkBlock, c_eWASMForkBlock,
c_constantinopleForkBlock, c_daoHardforkBlock, c_minimumDifficulty, c_difficultyBoundDivisor,
c_durationLimit, c_chainID, c_networkID, c_allowFutureBlocks};
} // anonymous namespace

void validateConfigJson(js::mObject const& _obj)
{
requireJsonFields(_obj, "ChainParams::loadConfig",
{{"sealEngine", {json_spirit::str_type}}, {"params", {json_spirit::obj_type}},
{"genesis", {json_spirit::obj_type}}, {"accounts", {json_spirit::obj_type}}});

requireJsonFields(_obj.at("genesis").get_obj(), "ChainParams::loadConfig",
{{"author", {json_spirit::str_type}}, {"nonce", {json_spirit::str_type}},
{"author", {json_spirit::str_type}}, {"gasLimit", {json_spirit::str_type}},
{"timestamp", {json_spirit::str_type}}, {"difficulty", {json_spirit::str_type}},
{"extraData", {json_spirit::str_type}}},
{"mixHash", "parentHash"});

js::mObject const& accounts = _obj.at("accounts").get_obj();
for (auto const& acc : accounts)
{
js::mObject const& account = acc.second.get_obj();
if (account.count("precompiled"))
{
requireJsonFields(account, "ChainParams::loadConfig",
{{"precompiled", {json_spirit::obj_type}}}, {"wei"});
}
else
{
if (account.count("wei"))
requireJsonFields(account, "ChainParams::loadConfig", {{"wei", {json_spirit::str_type}}});
else
{
requireJsonFields(account, "ChainParams::loadConfig",
{{"balance", {json_spirit::str_type}}}, {"code", "nonce", "storage"});
}
}
}
}

ChainParams ChainParams::loadConfig(
string const& _json, h256 const& _stateRoot, const boost::filesystem::path& _configPath) const
{
Expand All @@ -134,13 +62,10 @@ ChainParams ChainParams::loadConfig(
js::mObject obj = val.get_obj();

validateConfigJson(obj);
validateFieldNames(obj, c_knownChainConfigFields);

cp.sealEngineName = obj[c_sealEngine].get_str();
// params
js::mObject params = obj[c_params].get_obj();
validateFieldNames(params, c_knownParamNames);
cp.accountStartNonce = u256(fromBigEndian<u256>(fromHex(params[c_accountStartNonce].get_str())));
cp.accountStartNonce = u256(fromBigEndian<u256>(fromHex(params[c_accountStartNonce].get_str())));
cp.maximumExtraDataSize = u256(fromBigEndian<u256>(fromHex(params[c_maximumExtraDataSize].get_str())));
cp.tieBreakingGas = params.count(c_tieBreakingGas) ? params[c_tieBreakingGas].get_bool() : true;
cp.setBlockReward(u256(fromBigEndian<u256>(fromHex(params[c_blockReward].get_str()))));
Expand Down Expand Up @@ -178,28 +103,15 @@ ChainParams ChainParams::loadConfig(

cp.genesisState = jsonToAccountMap(
genesisStateStr, cp.accountStartNonce, nullptr, &cp.precompiled, _configPath);
cp.stateRoot = _stateRoot ? _stateRoot : cp.calculateStateRoot(true);

return cp;
}
// Strict account check
json_spirit::read_string_or_throw(genesisStateStr, val);
for (auto const& account: val.get_obj())
validateAccountObj(account.second.get_obj());

namespace
{
string const c_parentHash = "parentHash";
string const c_coinbase = "coinbase";
string const c_author = "author";
string const c_difficulty = "difficulty";
string const c_gasLimit = "gasLimit";
string const c_gasUsed = "gasUsed";
string const c_timestamp = "timestamp";
string const c_extraData = "extraData";
string const c_mixHash = "mixHash";
string const c_nonce = "nonce";
cp.stateRoot = _stateRoot ? _stateRoot : cp.calculateStateRoot(true);

set<string> const c_knownGenesisFields = {
c_parentHash, c_coinbase, c_author, c_difficulty, c_gasLimit, c_gasUsed, c_timestamp,
c_extraData, c_mixHash, c_nonce
};
return cp;
}

ChainParams ChainParams::loadGenesis(string const& _json, h256 const& _stateRoot) const
Expand All @@ -210,9 +122,7 @@ ChainParams ChainParams::loadGenesis(string const& _json, h256 const& _stateRoot
json_spirit::read_string(_json, val);
js::mObject genesis = val.get_obj();

validateFieldNames(genesis, c_knownGenesisFields);

cp.parentHash = h256(genesis[c_parentHash].get_str());
cp.parentHash = h256(0); // required by the YP
cp.author = genesis.count(c_coinbase) ? h160(genesis[c_coinbase].get_str()) : h160(genesis[c_author].get_str());
cp.difficulty = genesis.count(c_difficulty) ? u256(fromBigEndian<u256>(fromHex(genesis[c_difficulty].get_str()))) : 0;
cp.gasLimit = u256(fromBigEndian<u256>(fromHex(genesis[c_gasLimit].get_str())));
Expand Down
131 changes: 131 additions & 0 deletions libethereum/ValidationSchemes.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
#include <string>
#include <libdevcore/JsonUtils.h>
#include "ValidationSchemes.h"

using namespace std;
using namespace dev;
namespace js = json_spirit;

namespace dev {
namespace eth {
namespace validation {

string const c_sealEngine = "sealEngine";
string const c_params = "params";
string const c_genesis = "genesis";
string const c_accounts = "accounts";
string const c_balance = "balance";
string const c_wei = "wei";
string const c_finney = "finney";
string const c_author = "author";
string const c_coinbase = "coinbase";
string const c_nonce = "nonce";
string const c_gasLimit = "gasLimit";
string const c_timestamp = "timestamp";
string const c_difficulty = "difficulty";
string const c_extraData = "extraData";
string const c_mixHash = "mixHash";
string const c_parentHash = "parentHash";
string const c_precompiled = "precompiled";
string const c_code = "code";
string const c_storage = "storage";
string const c_gasUsed = "gasUsed";
string const c_codeFromFile = "codeFromFile"; ///< A file containg a code as bytes.
string const c_shouldnotexist = "shouldnotexist";

string const c_minGasLimit = "minGasLimit";
string const c_maxGasLimit = "maxGasLimit";
string const c_gasLimitBoundDivisor = "gasLimitBoundDivisor";
string const c_homesteadForkBlock = "homesteadForkBlock";
string const c_daoHardforkBlock = "daoHardforkBlock";
string const c_EIP150ForkBlock = "EIP150ForkBlock";
string const c_EIP158ForkBlock = "EIP158ForkBlock";
string const c_byzantiumForkBlock = "byzantiumForkBlock";
string const c_eWASMForkBlock = "eWASMForkBlock";
string const c_constantinopleForkBlock = "constantinopleForkBlock";
string const c_accountStartNonce = "accountStartNonce";
string const c_maximumExtraDataSize = "maximumExtraDataSize";
string const c_tieBreakingGas = "tieBreakingGas";
string const c_blockReward = "blockReward";
string const c_difficultyBoundDivisor = "difficultyBoundDivisor";
string const c_minimumDifficulty = "minimumDifficulty";
string const c_durationLimit = "durationLimit";
string const c_chainID = "chainID";
string const c_networkID = "networkID";
string const c_allowFutureBlocks = "allowFutureBlocks";

void validateConfigJson(js::mObject const& _obj)
{
requireJsonFields(_obj, "ChainParams::loadConfig",
{{c_sealEngine, {json_spirit::str_type}},
{c_params, {json_spirit::obj_type}},
{c_genesis, {json_spirit::obj_type}},
{c_accounts, {json_spirit::obj_type}}});

requireJsonFields(_obj.at(c_genesis).get_obj(), "ChainParams::loadConfig::genesis",
{{c_author, {json_spirit::str_type}},
{c_nonce, {json_spirit::str_type}},
{c_gasLimit, {json_spirit::str_type}},
{c_timestamp, {json_spirit::str_type}},
{c_difficulty, {json_spirit::str_type}},
{c_extraData, {json_spirit::str_type}},
{c_mixHash, {json_spirit::str_type}}},
{c_parentHash}); // allowed field, but not required

js::mObject const& accounts = _obj.at(c_accounts).get_obj();
for (auto const& acc : accounts)
validateAccountObj(acc.second.get_obj());
}

void validateAccountMapObj(js::mObject const& _obj)
{
// A map object with possibly defined fields
requireJsonFields(_obj, "validateAccountMapObj", {},
{c_storage, c_balance, c_nonce, c_code, c_precompiled, c_shouldnotexist, c_wei});
}

void validateAccountObj(js::mObject const& _obj)
{
if (_obj.count(c_precompiled))
{
// A precompiled contract
requireJsonFields(_obj, "validateAccountObj",
{{c_precompiled, {json_spirit::obj_type}}},
{c_wei});
}
else if (_obj.size() == 1)
{
// A genesis account with only balance set
if (_obj.count(c_balance))
requireJsonFields(_obj, "validateAccountObj", {{c_balance, {json_spirit::str_type}}});
else
requireJsonFields(_obj, "validateAccountObj", {{c_wei, {json_spirit::str_type}}});
}
else
{
// A standart account with all fields
requireJsonFields(_obj, "validateAccountObj",
{{c_code, {json_spirit::str_type}},
{c_nonce, {json_spirit::str_type}},
{c_storage, {json_spirit::obj_type}},
{c_balance, {json_spirit::str_type}}});
}
}

}}}
Loading

0 comments on commit 7372e65

Please sign in to comment.