cltester: Hostile Takeover
cltester can fork the state of an existing chain. This example loads an EOS snapshot, replaces the eosio and producer keys, and launches a nodeos instance which acts as 21 producers.
TEST_CASE("Takeover")
{
std::cout << "Loading snapshot...\n";
// This constructor loads a snapshot. The second argument is the max database size.
test_chain chain{"/home/todd/work/snapshot-2021-11-21-22-eos-v4-0216739301.bin",
uint64_t(20) * 1024 * 1024 * 1024};
// Replace production keys and eosio keys. These functions don't push any
// transactions. Instead, they directly modify the chain state in a way which
// violates consensus rules.
std::cout << "Replacing keys...\n";
chain.replace_producer_keys(test_chain::default_pub_key);
chain.replace_account_keys("eosio"_n, "owner"_n, test_chain::default_pub_key);
chain.replace_account_keys("eosio"_n, "active"_n, test_chain::default_pub_key);
// We replaced the production keys, but the system contract can switch
// them back. Let's fix that.
for (auto prod :
{"atticlabeosb"_n, "aus1genereos"_n, "big.one"_n, "binancestake"_n, "bitfinexeos1"_n,
"blockpooleos"_n, "eosasia11111"_n, "eoscannonchn"_n, "eoseouldotio"_n, "eosflytomars"_n,
"eoshuobipool"_n, "eosinfstones"_n, "eosiosg11111"_n, "eoslaomaocom"_n, "eosnationftw"_n,
"hashfineosio"_n, "newdex.bp"_n, "okcapitalbp1"_n, "starteosiobp"_n, "whaleex.com"_n,
"zbeosbp11111"_n})
{
std::cout << " " << prod.to_string() << "\n";
chain.replace_account_keys(prod, "owner"_n, test_chain::default_pub_key);
chain.replace_account_keys(prod, "active"_n, test_chain::default_pub_key);
chain.transact({
action{{{"eosio"_n, "owner"_n}}, "eosio.null"_n, "free.trx"_n, std::tuple{}},
action{{{prod, "owner"_n}},
"eosio"_n,
"regproducer"_n,
std::make_tuple(prod, test_chain::default_pub_key, std::string("url"),
uint16_t(1234))},
});
}
// Make a donation. This works because eosio.rex delegates to eosio,
// and we replaced eosio's keys.
chain.transact({
action{{{"eosio"_n, "owner"_n}}, "eosio.null"_n, "free.trx"_n, std::tuple{}},
action{{{"eosio.rex"_n, "owner"_n}},
"eosio.token"_n,
"transfer"_n,
std::make_tuple("eosio.rex"_n, "genesis.eden"_n, s2a("50000000.0000 EOS"),
std::string("donate"))},
});
// Produce the block
chain.finish_block();
// shut down the chain so we can safely copy the database
std::cout << "Shutdown...\n";
chain.shutdown();
// Copy everything into a fresh directory for nodeos to use
std::cout << "Copy...\n";
eosio::execute("rm -rf forked_chain");
eosio::execute("cp -r " + chain.get_path() + " forked_chain");
// Run nodeos. We must use the build which is packaged with clsdk since we're
// loading the non-portable database.
std::cout << "Start nodeos...\n";
eosio::execute(
"./clsdk/bin/nodeos "
"-d forked_chain "
"--config-dir example_config "
"--plugin eosio::chain_api_plugin "
"--access-control-allow-origin \"*\" "
"--access-control-allow-header \"*\" "
"--http-validate-host 0 "
"--http-server-address 0.0.0.0:8888 "
"--contracts-console "
"-e "
"-p atticlabeosb "
"-p aus1genereos "
"-p big.one "
"-p binancestake "
"-p bitfinexeos1 "
"-p blockpooleos "
"-p eosasia11111 "
"-p eoscannonchn "
"-p eoseouldotio "
"-p eosflytomars "
"-p eoshuobipool "
"-p eosinfstones "
"-p eosiosg11111 "
"-p eoslaomaocom "
"-p eosnationftw "
"-p hashfineosio "
"-p newdex.bp "
"-p okcapitalbp1 "
"-p starteosiobp "
"-p whaleex.com "
"-p zbeosbp11111 ");
}