Skip to content

Commit

Permalink
Merge pull request EOSIO#186 from enumivo/staging
Browse files Browse the repository at this point in the history
Staging
  • Loading branch information
Enumivo authored Jun 5, 2018
2 parents a9aec4f + d68ab0e commit fd69b56
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 18 deletions.
2 changes: 1 addition & 1 deletion EXCHANGE_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Accepting Deposits
When designing this tutorial we assume that an exchange will poll `enunode` for incoming
transactions and will want to know when a transfer is considered irreversible or final.

With enumivo based chains, finality of a transaction occurs once 2/3+1 of block produers have
With enumivo based chains, finality of a transaction occurs once 2/3+1 of block producers have
either directly or indirectly confirmed the block. This could take from less than a second to
a couple of minutes, but either way enunode will keep you posted on the status.

Expand Down
14 changes: 6 additions & 8 deletions contracts/enu.system/voting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,12 @@ namespace enumivosystem {
auto prod = _producers.find( producer );

if ( prod != _producers.end() ) {
if( producer_key != prod->producer_key ) {
_producers.modify( prod, producer, [&]( producer_info& info ){
info.producer_key = producer_key;
info.is_active = true;
info.url = url;
info.location = location;
});
}
_producers.modify( prod, producer, [&]( producer_info& info ){
info.producer_key = producer_key;
info.is_active = true;
info.url = url;
info.location = location;
});
} else {
_producers.emplace( producer, [&]( producer_info& info ){
info.owner = producer;
Expand Down
2 changes: 2 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/testUtils.py ${CMAKE_CURRENT_BINARY_D
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/enunode_run_test.py ${CMAKE_CURRENT_BINARY_DIR}/enunode_run_test.py COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/enunode_run_remote_test.py ${CMAKE_CURRENT_BINARY_DIR}/enunode_run_remote_test.py COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/consensus-validation-malicious-producers.py ${CMAKE_CURRENT_BINARY_DIR}/consensus-validation-malicious-producers.py COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/validate-dirty-db.py ${CMAKE_CURRENT_BINARY_DIR}/validate-dirty-db.py COPYONLY)

#To run plugin_test with all log from blockchain displayed, put --verbose after --, i.e. plugin_test -- --verbose
add_test(NAME plugin_test COMMAND plugin_test --report_level=detailed --color_output)
Expand All @@ -54,6 +55,7 @@ add_test(NAME distributed-transactions-remote-test COMMAND tests/distributed-tra
# add_test(NAME restart-scenarios-test_replay COMMAND tests/restart-scenarios-test.py -c replay -p4 -v --dump-error-details WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
add_test(NAME restart-scenarios-test_hard_replay COMMAND tests/restart-scenarios-test.py -c hardReplay -p4 -v --dump-error-details WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
# TODO: add_test(NAME consensus-validation-malicious-producers COMMAND tests/consensus-validation-malicious-producers.py -w 80 --dump-error-details WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
add_test(NAME validate_dirty_db_test COMMAND tests/validate-dirty-db.py -v --dump-error-detail WORKING_DIRECTORY ${CMAKE_BINARY_DIR})

if(ENABLE_COVERAGE_TESTING)

Expand Down
6 changes: 5 additions & 1 deletion tests/testUtils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1393,7 +1393,7 @@ def setWalletMgr(self, walletMgr):
# pylint: disable=too-many-return-statements
# pylint: disable=too-many-branches
# pylint: disable=too-many-statements
def launch(self, pnodes=1, totalNodes=1, prodCount=1, topo="mesh", delay=1, onlyBios=False, dontKill=False):
def launch(self, pnodes=1, totalNodes=1, prodCount=1, topo="mesh", delay=1, onlyBios=False, dontKill=False, dontBootstrap=False):
"""Launch cluster.
pnodes: producer nodes count
totalNodes: producer + non-producer nodes count
Expand Down Expand Up @@ -1456,6 +1456,10 @@ def launch(self, pnodes=1, totalNodes=1, prodCount=1, topo="mesh", delay=1, only
Utils.Print("ERROR: Cluster doesn't seem to be in sync. Some nodes missing block 1")
return False

if dontBootstrap:
Utils.Print("Skipping bootstrap.")
return True

Utils.Print("Bootstrap cluster.")
if not Cluster.bootstrap(totalNodes, prodCount, Cluster.__BiosHost, Cluster.__BiosPort, dontKill, onlyBios):
Utils.Print("ERROR: Bootstrap failed.")
Expand Down
129 changes: 129 additions & 0 deletions tests/validate-dirty-db.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
#!/usr/bin/env python3

import testUtils

import argparse
import random
import subprocess
import time
import os
import signal

###############################################################
# Test for validating the dirty db flag sticks repeated enunode restart attempts
###############################################################


Print=testUtils.Utils.Print

def errorExit(msg="", errorCode=1):
Print("ERROR:", msg)
exit(errorCode)

parser = argparse.ArgumentParser()
parser.add_argument("-v", help="verbose logging", action='store_true')
parser.add_argument("--dont-kill", help="Leave cluster running after test finishes", action='store_true')
parser.add_argument("--dump-error-details",
help="Upon error print etc/enumivo/node_*/config.ini and var/lib/node_*/stderr.log to stdout",
action='store_true')
parser.add_argument("--keep-logs", help="Don't delete var/lib/node_* folders upon test completion",
action='store_true')

args = parser.parse_args()
debug=args.v
pnodes=1
topo="mesh"
delay=1
chainSyncStrategyStr=testUtils.Utils.SyncResyncTag
total_nodes = pnodes
killCount=1
killSignal=testUtils.Utils.SigKillTag

killEnuInstances= not args.dont_kill
dumpErrorDetails=args.dump_error_details
keepLogs=args.keep_logs

seed=1
testUtils.Utils.Debug=debug
testSuccessful=False

random.seed(seed) # Use a fixed seed for repeatability.
cluster=testUtils.Cluster(enuwalletd=True)

try:
cluster.setChainStrategy(chainSyncStrategyStr)

cluster.killall()
cluster.cleanup()

Print ("producing nodes: %d, topology: %s, delay between nodes launch(seconds): %d, chain sync strategy: %s" % (
pnodes, topo, delay, chainSyncStrategyStr))

Print("Stand up cluster")
if cluster.launch(pnodes, total_nodes, topo=topo, delay=delay, dontBootstrap=True) is False:
errorExit("Failed to stand up enu cluster.")

node=cluster.getNode(0)
if node is None:
errorExit("Cluster in bad state, received None node")

Print("Kill cluster nodes.")
cluster.killall()

def runEnunodeAndGetOutput(nodeId, timeout=3):
"""Startup enunode, wait for timeout (before forced shutdown) and collect output. Stdout, stderr and return code are returned in a dictionary."""
Print("Launching enunode process id: %d" % (nodeId))
dataDir="var/lib/node_%02d" % (nodeId)
cmd="programs/enunode/enunode --config-dir etc/enumivo/node_bios --data-dir var/lib/node_bios"
Print("cmd: %s" % (cmd))
proc=subprocess.Popen(cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)

output={}
try:
outs,errs = proc.communicate(timeout=timeout)
output["stdout"] = outs.decode("utf-8")
output["stderr"] = errs.decode("utf-8")
output["returncode"] = proc.returncode
except (subprocess.TimeoutExpired) as _:
Print("ERROR: Enunode is running beyond the defined wait time. Hard killing enunode instance.")
proc.send_signal(signal.SIGKILL)
return (False, None)

return (True, output)

Print("Restart enunode repeatedly to ensure dirty database flag sticks.")
nodeId=0
timeout=3

for i in range(0,3):
Print("Attempt %d." % (i))
ret = runEnunodeAndGetOutput(nodeId, timeout)
if not ret or not ret[0]:
exit(1)

#Print(ret)

stderr=ret[1]["stderr"]
retCode=ret[1]["returncode"]
assert(retCode == 2)
assert("database dirty flag set" in stderr)

testSuccessful=True
finally:
if testSuccessful:
Print("Test succeeded.")
else:
Print("Test failed.")

if not testSuccessful and dumpErrorDetails:
cluster.dumpErrorDetails()
Print("== Errors see above ==")

if killEnuInstances:
Print("Shut down the cluster.")
cluster.killall()
if testSuccessful and not keepLogs:
Print("Cleanup cluster data.")
cluster.cleanup()

exit(0)
36 changes: 28 additions & 8 deletions unittests/enu.system_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -565,13 +565,13 @@ BOOST_FIXTURE_TEST_CASE( stake_from_refund, enu_system_tester ) try {
BOOST_FIXTURE_TEST_CASE( producer_register_unregister, enu_system_tester ) try {
issue( "alice1111111", core_from_string("1000.0000"), config::system_account_name );

fc::variant params = producer_parameters_example(1);
//fc::variant params = producer_parameters_example(1);
auto key = fc::crypto::public_key( std::string("ENU6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV") );
BOOST_REQUIRE_EQUAL( success(), push_action(N(alice1111111), N(regproducer), mvo()
("producer", "alice1111111")
("producer_key", key )
("url", "http://block.one")
("location", "0")
("location", 1)
)
);

Expand All @@ -580,13 +580,34 @@ BOOST_FIXTURE_TEST_CASE( producer_register_unregister, enu_system_tester ) try {
BOOST_REQUIRE_EQUAL( 0, info["total_votes"].as_double() );
BOOST_REQUIRE_EQUAL( "http://block.one", info["url"].as_string() );

//call regproducer again to change parameters
fc::variant params2 = producer_parameters_example(2);
//change parameters one by one to check for things like #3783
//fc::variant params2 = producer_parameters_example(2);
BOOST_REQUIRE_EQUAL( success(), push_action(N(alice1111111), N(regproducer), mvo()
("producer", "alice1111111")
("producer_key", key )
("url", "http://block.two")
("location", 1)
)
);
info = get_producer_info( "alice1111111" );
BOOST_REQUIRE_EQUAL( "alice1111111", info["owner"].as_string() );
BOOST_REQUIRE_EQUAL( key, fc::crypto::public_key(info["producer_key"].as_string()) );
BOOST_REQUIRE_EQUAL( "http://block.two", info["url"].as_string() );
BOOST_REQUIRE_EQUAL( 1, info["location"].as_int64() );

auto key2 = fc::crypto::public_key( std::string("ENU5jnmSKrzdBHE9n8hw58y7yxFWBC8SNiG7m8S1crJH3KvAnf9o6") );
BOOST_REQUIRE_EQUAL( success(), push_action(N(alice1111111), N(regproducer), mvo()
("producer", "alice1111111")
("producer_key", key2 )
("url", "http://block.two")
("location", 2)
)
);
info = get_producer_info( "alice1111111" );
BOOST_REQUIRE_EQUAL( "alice1111111", info["owner"].as_string() );
BOOST_REQUIRE_EQUAL( 0, info["total_votes"].as_double() );
BOOST_REQUIRE_EQUAL( "http://block.one", info["url"].as_string() );
BOOST_REQUIRE_EQUAL( key2, fc::crypto::public_key(info["producer_key"].as_string()) );
BOOST_REQUIRE_EQUAL( "http://block.two", info["url"].as_string() );
BOOST_REQUIRE_EQUAL( 2, info["location"].as_int64() );

//unregister producer
BOOST_REQUIRE_EQUAL( success(), push_action(N(alice1111111), N(unregprod), mvo()
Expand All @@ -595,12 +616,11 @@ BOOST_FIXTURE_TEST_CASE( producer_register_unregister, enu_system_tester ) try {
);
info = get_producer_info( "alice1111111" );
//key should be empty
wdump((info));
BOOST_REQUIRE_EQUAL( fc::crypto::public_key(), fc::crypto::public_key(info["producer_key"].as_string()) );
//everything else should stay the same
BOOST_REQUIRE_EQUAL( "alice1111111", info["owner"].as_string() );
BOOST_REQUIRE_EQUAL( 0, info["total_votes"].as_double() );
BOOST_REQUIRE_EQUAL( "http://block.one", info["url"].as_string() );
BOOST_REQUIRE_EQUAL( "http://block.two", info["url"].as_string() );

//unregister bob111111111 who is not a producer
BOOST_REQUIRE_EQUAL( wasm_assert_msg( "producer not found" ),
Expand Down

0 comments on commit fd69b56

Please sign in to comment.