From 267029d909bc1923a8e8f29d2957fcb9bd6d90bf Mon Sep 17 00:00:00 2001 From: Matt Witherspoon <32485495+spoonincode@users.noreply.github.com> Date: Wed, 15 Jan 2025 14:17:46 -0500 Subject: [PATCH 1/9] bump eos-vm submodule to fix spurious access violation --- libraries/eos-vm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/eos-vm b/libraries/eos-vm index d111d498d7..d2a81f3951 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit d111d498d739f71492ba8fa5f2b115b5d5e1c361 +Subproject commit d2a81f3951c91533624561950acfcb993189c70f From 4beb8dfb335a916945a3200e0f51903c744d8c08 Mon Sep 17 00:00:00 2001 From: Matt Witherspoon <32485495+spoonincode@users.noreply.github.com> Date: Wed, 15 Jan 2025 22:43:29 -0500 Subject: [PATCH 2/9] add tightloop test contract --- unittests/test-contracts/CMakeLists.txt | 1 + .../test-contracts/tightloop/CMakeLists.txt | 9 ++++++++ .../test-contracts/tightloop/tightloop.abi | 21 ++++++++++++++++++ .../test-contracts/tightloop/tightloop.wasm | Bin 0 -> 116 bytes .../test-contracts/tightloop/tightloop.wast | 17 ++++++++++++++ 5 files changed, 48 insertions(+) create mode 100644 unittests/test-contracts/tightloop/CMakeLists.txt create mode 100644 unittests/test-contracts/tightloop/tightloop.abi create mode 100644 unittests/test-contracts/tightloop/tightloop.wasm create mode 100644 unittests/test-contracts/tightloop/tightloop.wast diff --git a/unittests/test-contracts/CMakeLists.txt b/unittests/test-contracts/CMakeLists.txt index 32195be224..451481faa1 100644 --- a/unittests/test-contracts/CMakeLists.txt +++ b/unittests/test-contracts/CMakeLists.txt @@ -38,6 +38,7 @@ add_subdirectory( test_api ) add_subdirectory( test_api_db ) add_subdirectory( test_api_multi_index ) add_subdirectory( test_ram_limit ) +add_subdirectory( tightloop ) add_subdirectory( action_results ) add_subdirectory( wasm_config_bios ) add_subdirectory( params_test ) diff --git a/unittests/test-contracts/tightloop/CMakeLists.txt b/unittests/test-contracts/tightloop/CMakeLists.txt new file mode 100644 index 0000000000..03d418a763 --- /dev/null +++ b/unittests/test-contracts/tightloop/CMakeLists.txt @@ -0,0 +1,9 @@ +if(EOSIO_COMPILE_TEST_CONTRACTS) + add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/tightloop.wasm" + COMMAND "${CDT_ROOT}/bin/eosio-wast2wasm" "${CMAKE_CURRENT_SOURCE_DIR}/tightloop.wast" -o "${CMAKE_CURRENT_BINARY_DIR}/tightloop.wasm" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/tightloop.wast") + add_custom_target(gen_tightloop_wasm ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/tightloop.wasm") +else() + configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/tightloop.wasm ${CMAKE_CURRENT_BINARY_DIR}/tightloop.wasm COPYONLY ) +endif() +configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/tightloop.abi ${CMAKE_CURRENT_BINARY_DIR}/tightloop.abi COPYONLY ) diff --git a/unittests/test-contracts/tightloop/tightloop.abi b/unittests/test-contracts/tightloop/tightloop.abi new file mode 100644 index 0000000000..17a6f9394b --- /dev/null +++ b/unittests/test-contracts/tightloop/tightloop.abi @@ -0,0 +1,21 @@ +{ + "version": "eosio::abi/1.0", + "types": [], + "structs": [{ + "name": "doit", + "base": "", + "fields": [{"name":"count","type":"uint64"}] + }], + "actions": [{ + "name": "doit", + "type": "doit", + "ricardian_contract": "" + } + ], + "tables": [], + "ricardian_clauses": [], + "error_messages": [], + "abi_extensions": [], + "variants": [], + "action_results": [] +} \ No newline at end of file diff --git a/unittests/test-contracts/tightloop/tightloop.wasm b/unittests/test-contracts/tightloop/tightloop.wasm new file mode 100644 index 0000000000000000000000000000000000000000..4722cb239139d24b3af4bc1346c63e4d10c940da GIT binary patch literal 116 zcmV~$OA5k35Cy>Zrt?vUK$hYvGP>{ Date: Wed, 15 Jan 2025 22:45:20 -0500 Subject: [PATCH 3/9] add test for overlapped ROtrx where 1 hits deadline --- tests/CMakeLists.txt | 5 + tests/read_only_trx_overlap_timeout.py | 161 +++++++++++++++++++++++++ 2 files changed, 166 insertions(+) create mode 100755 tests/read_only_trx_overlap_timeout.py diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a6194c13f1..b47ba8a9f4 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -85,6 +85,7 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/trx_finality_status_forked_test.py ${ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/plugin_http_api_test.py ${CMAKE_CURRENT_BINARY_DIR}/plugin_http_api_test.py COPYONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/plugin_http_api_test_savanna.py ${CMAKE_CURRENT_BINARY_DIR}/plugin_http_api_test_savanna.py COPYONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/nodeos_contrl_c_test.py ${CMAKE_CURRENT_BINARY_DIR}/nodeos_contrl_c_test.py COPYONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/read_only_trx_overlap_timeout.py ${CMAKE_CURRENT_BINARY_DIR}/read_only_trx_overlap_timeout.py COPYONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/read_only_trx_test.py ${CMAKE_CURRENT_BINARY_DIR}/read_only_trx_test.py COPYONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/resource_monitor_plugin_test.py ${CMAKE_CURRENT_BINARY_DIR}/resource_monitor_plugin_test.py COPYONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/light_validation_sync_test.py ${CMAKE_CURRENT_BINARY_DIR}/light_validation_sync_test.py COPYONLY) @@ -204,6 +205,10 @@ add_test(NAME nodeos_protocol_feature_test COMMAND tests/nodeos_protocol_feature set_property(TEST nodeos_protocol_feature_test PROPERTY LABELS nonparallelizable_tests) add_test(NAME compute_transaction_test COMMAND tests/compute_transaction_test.py -v -p 2 -n 3 ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) set_property(TEST compute_transaction_test PROPERTY LABELS nonparallelizable_tests) +add_test(NAME read-only-trx-overlap-timeout-test COMMAND tests/read_only_trx_overlap_timeout.py ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) +set_property(TEST read-only-trx-overlap-timeout-test PROPERTY LABELS nonparallelizable_tests) +add_test(NAME read-only-trx-overlap-timeout-no-oc-test COMMAND tests/read_only_trx_overlap_timeout.py --eos-vm-oc-enable none ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) +set_property(TEST read-only-trx-overlap-timeout-no-oc-test PROPERTY LABELS nonparallelizable_tests) add_test(NAME read-only-trx-basic-test COMMAND tests/read_only_trx_test.py -p 2 -n 3 --read-only-threads 1 ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) set_property(TEST read-only-trx-basic-test PROPERTY LABELS nonparallelizable_tests) add_test(NAME read-only-trx-parallel-test COMMAND tests/read_only_trx_test.py -p 2 -n 3 --read-only-threads 16 --num-test-runs 3 ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) diff --git a/tests/read_only_trx_overlap_timeout.py b/tests/read_only_trx_overlap_timeout.py new file mode 100755 index 0000000000..9fea585b33 --- /dev/null +++ b/tests/read_only_trx_overlap_timeout.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python3 + +import random +import time +import signal +import threading +import os +import platform +import traceback + +from TestHarness import Account, Cluster, ReturnType, TestHelper, Utils, WalletMgr +from TestHarness.TestHelper import AppArgs + +############################################################### +# read_only_trx_overlap_timeout tests that a ROtrx which hits the deadline timer on one thread doesn't +# disturb ROtrx on other threads +############################################################### + +Print=Utils.Print +errorExit=Utils.errorExit + +appArgs=AppArgs() +appArgs.add(flag="--test-length-seconds", type=int, help="number of seconds to search for a failure", default=10) +appArgs.add(flag="--eos-vm-oc-enable", type=str, help="specify eos-vm-oc-enable option", default="auto") +appArgs.add(flag="--wasm-runtime", type=str, help="if set to eos-vm-oc, must compile with EOSIO_EOS_VM_OC_DEVELOPER", default="eos-vm-jit") + +args=TestHelper.parse_args({"-d","-s","--nodes-file","--seed" + ,"--activate-if","--dump-error-details","-v","--leave-running" + ,"--keep-logs","--unshared"}, applicationSpecificArgs=appArgs) + +pnodes=1 +topo=args.s +delay=args.d +total_nodes=2 +debug=args.v +nodesFile=args.nodes_file +dontLaunch=nodesFile is not None +seed=args.seed +activateIF=args.activate_if +dumpErrorDetails=args.dump_error_details +testLengthSeconds=args.test_length_seconds + +Utils.Debug=debug +testSuccessful=False +stopThread=False + +random.seed(seed) # Use a fixed seed for repeatability. +cluster=Cluster(loggingLevel="all", unshared=args.unshared, keepRunning=True if nodesFile is not None else args.leave_running, keepLogs=args.keep_logs) + +walletMgr=WalletMgr(True) +EOSIO_ACCT_PRIVATE_DEFAULT_KEY = "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3" +EOSIO_ACCT_PUBLIC_DEFAULT_KEY = "EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV" + +producerNode = None +apiNode = None +tightloopAccountName = "tightloop" + +def startCluster(): + global total_nodes + global producerNode + global apiNode + + TestHelper.printSystemInfo("BEGIN") + cluster.setWalletMgr(walletMgr) + + if dontLaunch: # run test against remote cluster + jsonStr=None + with open(nodesFile, "r") as f: + jsonStr=f.read() + if not cluster.initializeNodesFromJson(jsonStr): + errorExit("Failed to initilize nodes from Json string.") + total_nodes=len(cluster.getNodes()) + + print("Stand up walletd") + if walletMgr.launch() is False: + errorExit("Failed to stand up keosd.") + + Print("producing nodes: %d, non-producing nodes: %d, topology: %s, delay between nodes launch(seconds): %d" % (pnodes, total_nodes-pnodes, topo, delay)) + + Print("Stand up cluster") + # set up read-only options for API node + specificExtraNodeosArgs={} + # producer nodes will be mapped to 0 through pnodes-1, so the number pnodes is the no-producing API node + specificExtraNodeosArgs[pnodes]="--read-only-threads " + specificExtraNodeosArgs[pnodes]+=" 2 " + specificExtraNodeosArgs[pnodes]+=" --max-transaction-time " + specificExtraNodeosArgs[pnodes]+=" 10 " + if args.eos_vm_oc_enable: + if platform.system() != "Linux": + Print("OC not run on Linux. Skip the test") + exit(True) # Do not fail the test + specificExtraNodeosArgs[pnodes]+=" --eos-vm-oc-enable " + specificExtraNodeosArgs[pnodes]+=args.eos_vm_oc_enable + if args.wasm_runtime: + specificExtraNodeosArgs[pnodes]+=" --wasm-runtime " + specificExtraNodeosArgs[pnodes]+=args.wasm_runtime + + if cluster.launch(pnodes=pnodes, totalNodes=total_nodes, topo=topo, delay=delay, activateIF=activateIF, specificExtraNodeosArgs=specificExtraNodeosArgs, extraNodeosArgs="--resource-monitor-not-shutdown-on-threshold-exceeded" ) is False: + errorExit("Failed to stand up eos cluster.") + + Print ("Wait for Cluster stabilization") + # wait for cluster to start producing blocks + if not cluster.waitOnClusterBlockNumSync(3): + errorExit("Cluster never stabilized") + + producerNode = cluster.getNode() + apiNode = cluster.nodes[-1] + + +def deployTestContracts(): + Utils.Print("create test accounts") + tightloopAccount = Account(tightloopAccountName) + tightloopAccount.ownerPublicKey = EOSIO_ACCT_PUBLIC_DEFAULT_KEY + tightloopAccount.activePublicKey = EOSIO_ACCT_PUBLIC_DEFAULT_KEY + cluster.createAccountAndVerify(tightloopAccount, cluster.eosioAccount, buyRAM=1000000) + + tightloopContractDir="unittests/test-contracts/tightloop" + tightloopWasmFile="tightloop.wasm" + tightloopAbiFile="tightloop.abi" + producerNode.publishContract(tightloopAccount, tightloopContractDir, tightloopWasmFile, tightloopAbiFile, waitForTransBlock=True) + + +def sendTransaction(account, action, data, auth=[], opts=None): + trx = { + "actions": [{ + "account": account, + "name": action, + "authorization": auth, + "data": data + }] + } + return apiNode.pushTransaction(trx, opts) + +def longROtrxThread(): + Print("start longROtrxThread") + + while stopThread==False: + sendTransaction(tightloopAccountName, 'doit', {"count": 50000000}, opts='--read') #50 million is a good number to always take >10ms + +try: + startCluster() + deployTestContracts() + + # start a background thread that constatly runs a ROtrx that is never expected to complete in max-transaction-time + thr = threading.Thread(target = longROtrxThread) + thr.start() + + endTime = time.time() + testLengthSeconds + # and then run some other ROtrx that should complete successfully + while time.time() < endTime: + results = sendTransaction(tightloopAccountName, 'doit', {"count": 1000000}, opts='--read') #1 million is a good number to always take <10ms + assert(results[0]) + + testSuccessful = True +finally: + stopThread = True; + thr.join() + TestHelper.shutdown(cluster, walletMgr, testSuccessful, dumpErrorDetails) + +errorCode = 0 if testSuccessful else 1 +exit(errorCode) From f1e6f6b16a81432f096f5b6e2ca53a04fb8d9354 Mon Sep 17 00:00:00 2001 From: Matt Witherspoon <32485495+spoonincode@users.noreply.github.com> Date: Fri, 17 Jan 2025 17:38:23 -0500 Subject: [PATCH 4/9] remove ..not-shutdown-on-threshold.. & spelling fixes --- tests/read_only_trx_overlap_timeout.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/read_only_trx_overlap_timeout.py b/tests/read_only_trx_overlap_timeout.py index 9fea585b33..e5dc15c814 100755 --- a/tests/read_only_trx_overlap_timeout.py +++ b/tests/read_only_trx_overlap_timeout.py @@ -68,7 +68,7 @@ def startCluster(): with open(nodesFile, "r") as f: jsonStr=f.read() if not cluster.initializeNodesFromJson(jsonStr): - errorExit("Failed to initilize nodes from Json string.") + errorExit("Failed to initialize nodes from Json string.") total_nodes=len(cluster.getNodes()) print("Stand up walletd") @@ -95,7 +95,7 @@ def startCluster(): specificExtraNodeosArgs[pnodes]+=" --wasm-runtime " specificExtraNodeosArgs[pnodes]+=args.wasm_runtime - if cluster.launch(pnodes=pnodes, totalNodes=total_nodes, topo=topo, delay=delay, activateIF=activateIF, specificExtraNodeosArgs=specificExtraNodeosArgs, extraNodeosArgs="--resource-monitor-not-shutdown-on-threshold-exceeded" ) is False: + if cluster.launch(pnodes=pnodes, totalNodes=total_nodes, topo=topo, delay=delay, activateIF=activateIF, specificExtraNodeosArgs=specificExtraNodeosArgs) is False: errorExit("Failed to stand up eos cluster.") Print ("Wait for Cluster stabilization") @@ -141,7 +141,7 @@ def longROtrxThread(): startCluster() deployTestContracts() - # start a background thread that constatly runs a ROtrx that is never expected to complete in max-transaction-time + # start a background thread that constantly runs a ROtrx that is never expected to complete in max-transaction-time thr = threading.Thread(target = longROtrxThread) thr.start() From aef3a65a4cf4ae794b17c1be9fee6ee95d28dd68 Mon Sep 17 00:00:00 2001 From: Matt Witherspoon <32485495+spoonincode@users.noreply.github.com> Date: Fri, 17 Jan 2025 17:40:00 -0500 Subject: [PATCH 5/9] (hopefully) improve test on slower hardware; make OC test use OC --- tests/read_only_trx_overlap_timeout.py | 45 ++++++++++++++++++++------ 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/tests/read_only_trx_overlap_timeout.py b/tests/read_only_trx_overlap_timeout.py index e5dc15c814..a5118f58d0 100755 --- a/tests/read_only_trx_overlap_timeout.py +++ b/tests/read_only_trx_overlap_timeout.py @@ -20,6 +20,7 @@ errorExit=Utils.errorExit appArgs=AppArgs() +appArgs.add(flag="--read-only-threads", type=int, help="number of read-only threads", default=2) appArgs.add(flag="--test-length-seconds", type=int, help="number of seconds to search for a failure", default=10) appArgs.add(flag="--eos-vm-oc-enable", type=str, help="specify eos-vm-oc-enable option", default="auto") appArgs.add(flag="--wasm-runtime", type=str, help="if set to eos-vm-oc, must compile with EOSIO_EOS_VM_OC_DEVELOPER", default="eos-vm-jit") @@ -41,7 +42,9 @@ testLengthSeconds=args.test_length_seconds Utils.Debug=debug -testSuccessful=False +threadLock=threading.Lock() +allResponsesGood=True +numShortResponses=0 stopThread=False random.seed(seed) # Use a fixed seed for repeatability. @@ -82,7 +85,7 @@ def startCluster(): specificExtraNodeosArgs={} # producer nodes will be mapped to 0 through pnodes-1, so the number pnodes is the no-producing API node specificExtraNodeosArgs[pnodes]="--read-only-threads " - specificExtraNodeosArgs[pnodes]+=" 2 " + specificExtraNodeosArgs[pnodes]+=str(args.read_only_threads) specificExtraNodeosArgs[pnodes]+=" --max-transaction-time " specificExtraNodeosArgs[pnodes]+=" 10 " if args.eos_vm_oc_enable: @@ -137,25 +140,47 @@ def longROtrxThread(): while stopThread==False: sendTransaction(tightloopAccountName, 'doit', {"count": 50000000}, opts='--read') #50 million is a good number to always take >10ms +def shortROtrxThread(): + Print("start a shortROtrxThread") + + global numShortResponses + global allResponsesGood + + while stopThread==False: + results = sendTransaction(tightloopAccountName, 'doit', {"count": 250000}, opts='--read') #250k will hopefully complete within 10ms, even on slower hardware + with threadLock: + allResponsesGood &= results[0] + numShortResponses += 1 + +def testPassed(): + return allResponsesGood and numShortResponses > 0 + try: startCluster() deployTestContracts() + # ROtrx won't pump OC's cache so prime OC's cache of this contract with some actions + for i in range(2): + trans = apiNode.pushMessage(tightloopAccountName, 'doit', '{"count": 1}', "-p {}@active".format(tightloopAccountName)) + assert(trans[0]) + apiNode.waitForTransactionInBlock(trans[1]['transaction_id'], exitOnError=True) # start a background thread that constantly runs a ROtrx that is never expected to complete in max-transaction-time thr = threading.Thread(target = longROtrxThread) thr.start() - endTime = time.time() + testLengthSeconds - # and then run some other ROtrx that should complete successfully - while time.time() < endTime: - results = sendTransaction(tightloopAccountName, 'doit', {"count": 1000000}, opts='--read') #1 million is a good number to always take <10ms - assert(results[0]) + # start twice the number of configured nodeos RO threads to spam short ROtrx; the 2x is hopefully to keep nodeos RO threads as busy as possible + shortThreadList = [] + for i in range(args.read_only_threads*2): + shortThreadList.append(threading.Thread(target = shortROtrxThread)) + shortThreadList[i].start() - testSuccessful = True + time.sleep(testLengthSeconds) finally: stopThread = True; thr.join() - TestHelper.shutdown(cluster, walletMgr, testSuccessful, dumpErrorDetails) + for sthr in shortThreadList: + sthr.join() + TestHelper.shutdown(cluster, walletMgr, testPassed(), dumpErrorDetails) -errorCode = 0 if testSuccessful else 1 +errorCode = 0 if testPassed() else 1 exit(errorCode) From cec196e3cf39f9b201d8fbb272d98cb7c344d456 Mon Sep 17 00:00:00 2001 From: Matt Witherspoon <32485495+spoonincode@users.noreply.github.com> Date: Mon, 20 Jan 2025 20:32:00 -0500 Subject: [PATCH 6/9] use a separate node to populate initial non-ROtrx --- tests/read_only_trx_overlap_timeout.py | 34 ++++++++++++++------------ 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/tests/read_only_trx_overlap_timeout.py b/tests/read_only_trx_overlap_timeout.py index a5118f58d0..48ffa43390 100755 --- a/tests/read_only_trx_overlap_timeout.py +++ b/tests/read_only_trx_overlap_timeout.py @@ -32,7 +32,7 @@ pnodes=1 topo=args.s delay=args.d -total_nodes=2 +total_nodes=3 debug=args.v nodesFile=args.nodes_file dontLaunch=nodesFile is not None @@ -56,12 +56,14 @@ producerNode = None apiNode = None +roTrxNode = None tightloopAccountName = "tightloop" def startCluster(): global total_nodes global producerNode global apiNode + global roTrxNode TestHelper.printSystemInfo("BEGIN") cluster.setWalletMgr(walletMgr) @@ -83,20 +85,21 @@ def startCluster(): Print("Stand up cluster") # set up read-only options for API node specificExtraNodeosArgs={} - # producer nodes will be mapped to 0 through pnodes-1, so the number pnodes is the no-producing API node - specificExtraNodeosArgs[pnodes]="--read-only-threads " - specificExtraNodeosArgs[pnodes]+=str(args.read_only_threads) - specificExtraNodeosArgs[pnodes]+=" --max-transaction-time " - specificExtraNodeosArgs[pnodes]+=" 10 " + # last node will be the roTrx node + roTrxNodeIndex = total_nodes-1 + specificExtraNodeosArgs[roTrxNodeIndex]="--read-only-threads " + specificExtraNodeosArgs[roTrxNodeIndex]+=str(args.read_only_threads) + specificExtraNodeosArgs[roTrxNodeIndex]+=" --max-transaction-time " + specificExtraNodeosArgs[roTrxNodeIndex]+=" 10 " if args.eos_vm_oc_enable: if platform.system() != "Linux": Print("OC not run on Linux. Skip the test") exit(True) # Do not fail the test - specificExtraNodeosArgs[pnodes]+=" --eos-vm-oc-enable " - specificExtraNodeosArgs[pnodes]+=args.eos_vm_oc_enable + specificExtraNodeosArgs[roTrxNodeIndex]+=" --eos-vm-oc-enable " + specificExtraNodeosArgs[roTrxNodeIndex]+=args.eos_vm_oc_enable if args.wasm_runtime: - specificExtraNodeosArgs[pnodes]+=" --wasm-runtime " - specificExtraNodeosArgs[pnodes]+=args.wasm_runtime + specificExtraNodeosArgs[roTrxNodeIndex]+=" --wasm-runtime " + specificExtraNodeosArgs[roTrxNodeIndex]+=args.wasm_runtime if cluster.launch(pnodes=pnodes, totalNodes=total_nodes, topo=topo, delay=delay, activateIF=activateIF, specificExtraNodeosArgs=specificExtraNodeosArgs) is False: errorExit("Failed to stand up eos cluster.") @@ -107,7 +110,8 @@ def startCluster(): errorExit("Cluster never stabilized") producerNode = cluster.getNode() - apiNode = cluster.nodes[-1] + roTrxNode = cluster.nodes[-1] + apiNode = cluster.nodes[-2] def deployTestContracts(): @@ -123,7 +127,7 @@ def deployTestContracts(): producerNode.publishContract(tightloopAccount, tightloopContractDir, tightloopWasmFile, tightloopAbiFile, waitForTransBlock=True) -def sendTransaction(account, action, data, auth=[], opts=None): +def sendROTransaction(account, action, data, auth=[], opts=None): trx = { "actions": [{ "account": account, @@ -132,13 +136,13 @@ def sendTransaction(account, action, data, auth=[], opts=None): "data": data }] } - return apiNode.pushTransaction(trx, opts) + return roTrxNode.pushTransaction(trx, opts) def longROtrxThread(): Print("start longROtrxThread") while stopThread==False: - sendTransaction(tightloopAccountName, 'doit', {"count": 50000000}, opts='--read') #50 million is a good number to always take >10ms + sendROTransaction(tightloopAccountName, 'doit', {"count": 50000000}, opts='--read') #50 million is a good number to always take >10ms def shortROtrxThread(): Print("start a shortROtrxThread") @@ -147,7 +151,7 @@ def shortROtrxThread(): global allResponsesGood while stopThread==False: - results = sendTransaction(tightloopAccountName, 'doit', {"count": 250000}, opts='--read') #250k will hopefully complete within 10ms, even on slower hardware + results = sendROTransaction(tightloopAccountName, 'doit', {"count": 250000}, opts='--read') #250k will hopefully complete within 10ms, even on slower hardware with threadLock: allResponsesGood &= results[0] numShortResponses += 1 From 8a09b5da05ac263e52e35adb8fdc458ffb674aba Mon Sep 17 00:00:00 2001 From: Matt Witherspoon <32485495+spoonincode@users.noreply.github.com> Date: Tue, 21 Jan 2025 15:13:17 -0500 Subject: [PATCH 7/9] tweaks to make the test work right on mac or arm --- tests/read_only_trx_overlap_timeout.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/read_only_trx_overlap_timeout.py b/tests/read_only_trx_overlap_timeout.py index 48ffa43390..29e64f7b26 100755 --- a/tests/read_only_trx_overlap_timeout.py +++ b/tests/read_only_trx_overlap_timeout.py @@ -23,7 +23,7 @@ appArgs.add(flag="--read-only-threads", type=int, help="number of read-only threads", default=2) appArgs.add(flag="--test-length-seconds", type=int, help="number of seconds to search for a failure", default=10) appArgs.add(flag="--eos-vm-oc-enable", type=str, help="specify eos-vm-oc-enable option", default="auto") -appArgs.add(flag="--wasm-runtime", type=str, help="if set to eos-vm-oc, must compile with EOSIO_EOS_VM_OC_DEVELOPER", default="eos-vm-jit") +appArgs.add(flag="--wasm-runtime", type=str, help="if set to eos-vm-oc, must compile with EOSIO_EOS_VM_OC_DEVELOPER", default="") args=TestHelper.parse_args({"-d","-s","--nodes-file","--seed" ,"--activate-if","--dump-error-details","-v","--leave-running" @@ -91,10 +91,10 @@ def startCluster(): specificExtraNodeosArgs[roTrxNodeIndex]+=str(args.read_only_threads) specificExtraNodeosArgs[roTrxNodeIndex]+=" --max-transaction-time " specificExtraNodeosArgs[roTrxNodeIndex]+=" 10 " - if args.eos_vm_oc_enable: + if args.eos_vm_oc_enable != "off": if platform.system() != "Linux": Print("OC not run on Linux. Skip the test") - exit(True) # Do not fail the test + exit(0) # Do not fail the test specificExtraNodeosArgs[roTrxNodeIndex]+=" --eos-vm-oc-enable " specificExtraNodeosArgs[roTrxNodeIndex]+=args.eos_vm_oc_enable if args.wasm_runtime: From 092d23a17dd171929bdc913696a17f2635957345 Mon Sep 17 00:00:00 2001 From: Matt Witherspoon <32485495+spoonincode@users.noreply.github.com> Date: Tue, 21 Jan 2025 15:14:34 -0500 Subject: [PATCH 8/9] bump eos-vm submod to spring1.0 release head w/ overlap fix --- libraries/eos-vm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/eos-vm b/libraries/eos-vm index d2a81f3951..02751e008e 160000 --- a/libraries/eos-vm +++ b/libraries/eos-vm @@ -1 +1 @@ -Subproject commit d2a81f3951c91533624561950acfcb993189c70f +Subproject commit 02751e008e1648741764b5db378579a777841729 From 90c11c3812f26c74573e25ab6a650b8c21c3e4fe Mon Sep 17 00:00:00 2001 From: Matt Witherspoon <32485495+spoonincode@users.noreply.github.com> Date: Tue, 21 Jan 2025 15:17:42 -0500 Subject: [PATCH 9/9] remove overlap timeout test since it's too sensitive to timing in CI --- tests/CMakeLists.txt | 5 - tests/read_only_trx_overlap_timeout.py | 190 ------------------ unittests/test-contracts/CMakeLists.txt | 1 - .../test-contracts/tightloop/CMakeLists.txt | 9 - .../test-contracts/tightloop/tightloop.abi | 21 -- .../test-contracts/tightloop/tightloop.wasm | Bin 116 -> 0 bytes .../test-contracts/tightloop/tightloop.wast | 17 -- 7 files changed, 243 deletions(-) delete mode 100755 tests/read_only_trx_overlap_timeout.py delete mode 100644 unittests/test-contracts/tightloop/CMakeLists.txt delete mode 100644 unittests/test-contracts/tightloop/tightloop.abi delete mode 100644 unittests/test-contracts/tightloop/tightloop.wasm delete mode 100644 unittests/test-contracts/tightloop/tightloop.wast diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b47ba8a9f4..a6194c13f1 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -85,7 +85,6 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/trx_finality_status_forked_test.py ${ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/plugin_http_api_test.py ${CMAKE_CURRENT_BINARY_DIR}/plugin_http_api_test.py COPYONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/plugin_http_api_test_savanna.py ${CMAKE_CURRENT_BINARY_DIR}/plugin_http_api_test_savanna.py COPYONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/nodeos_contrl_c_test.py ${CMAKE_CURRENT_BINARY_DIR}/nodeos_contrl_c_test.py COPYONLY) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/read_only_trx_overlap_timeout.py ${CMAKE_CURRENT_BINARY_DIR}/read_only_trx_overlap_timeout.py COPYONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/read_only_trx_test.py ${CMAKE_CURRENT_BINARY_DIR}/read_only_trx_test.py COPYONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/resource_monitor_plugin_test.py ${CMAKE_CURRENT_BINARY_DIR}/resource_monitor_plugin_test.py COPYONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/light_validation_sync_test.py ${CMAKE_CURRENT_BINARY_DIR}/light_validation_sync_test.py COPYONLY) @@ -205,10 +204,6 @@ add_test(NAME nodeos_protocol_feature_test COMMAND tests/nodeos_protocol_feature set_property(TEST nodeos_protocol_feature_test PROPERTY LABELS nonparallelizable_tests) add_test(NAME compute_transaction_test COMMAND tests/compute_transaction_test.py -v -p 2 -n 3 ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) set_property(TEST compute_transaction_test PROPERTY LABELS nonparallelizable_tests) -add_test(NAME read-only-trx-overlap-timeout-test COMMAND tests/read_only_trx_overlap_timeout.py ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) -set_property(TEST read-only-trx-overlap-timeout-test PROPERTY LABELS nonparallelizable_tests) -add_test(NAME read-only-trx-overlap-timeout-no-oc-test COMMAND tests/read_only_trx_overlap_timeout.py --eos-vm-oc-enable none ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) -set_property(TEST read-only-trx-overlap-timeout-no-oc-test PROPERTY LABELS nonparallelizable_tests) add_test(NAME read-only-trx-basic-test COMMAND tests/read_only_trx_test.py -p 2 -n 3 --read-only-threads 1 ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) set_property(TEST read-only-trx-basic-test PROPERTY LABELS nonparallelizable_tests) add_test(NAME read-only-trx-parallel-test COMMAND tests/read_only_trx_test.py -p 2 -n 3 --read-only-threads 16 --num-test-runs 3 ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) diff --git a/tests/read_only_trx_overlap_timeout.py b/tests/read_only_trx_overlap_timeout.py deleted file mode 100755 index 29e64f7b26..0000000000 --- a/tests/read_only_trx_overlap_timeout.py +++ /dev/null @@ -1,190 +0,0 @@ -#!/usr/bin/env python3 - -import random -import time -import signal -import threading -import os -import platform -import traceback - -from TestHarness import Account, Cluster, ReturnType, TestHelper, Utils, WalletMgr -from TestHarness.TestHelper import AppArgs - -############################################################### -# read_only_trx_overlap_timeout tests that a ROtrx which hits the deadline timer on one thread doesn't -# disturb ROtrx on other threads -############################################################### - -Print=Utils.Print -errorExit=Utils.errorExit - -appArgs=AppArgs() -appArgs.add(flag="--read-only-threads", type=int, help="number of read-only threads", default=2) -appArgs.add(flag="--test-length-seconds", type=int, help="number of seconds to search for a failure", default=10) -appArgs.add(flag="--eos-vm-oc-enable", type=str, help="specify eos-vm-oc-enable option", default="auto") -appArgs.add(flag="--wasm-runtime", type=str, help="if set to eos-vm-oc, must compile with EOSIO_EOS_VM_OC_DEVELOPER", default="") - -args=TestHelper.parse_args({"-d","-s","--nodes-file","--seed" - ,"--activate-if","--dump-error-details","-v","--leave-running" - ,"--keep-logs","--unshared"}, applicationSpecificArgs=appArgs) - -pnodes=1 -topo=args.s -delay=args.d -total_nodes=3 -debug=args.v -nodesFile=args.nodes_file -dontLaunch=nodesFile is not None -seed=args.seed -activateIF=args.activate_if -dumpErrorDetails=args.dump_error_details -testLengthSeconds=args.test_length_seconds - -Utils.Debug=debug -threadLock=threading.Lock() -allResponsesGood=True -numShortResponses=0 -stopThread=False - -random.seed(seed) # Use a fixed seed for repeatability. -cluster=Cluster(loggingLevel="all", unshared=args.unshared, keepRunning=True if nodesFile is not None else args.leave_running, keepLogs=args.keep_logs) - -walletMgr=WalletMgr(True) -EOSIO_ACCT_PRIVATE_DEFAULT_KEY = "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3" -EOSIO_ACCT_PUBLIC_DEFAULT_KEY = "EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV" - -producerNode = None -apiNode = None -roTrxNode = None -tightloopAccountName = "tightloop" - -def startCluster(): - global total_nodes - global producerNode - global apiNode - global roTrxNode - - TestHelper.printSystemInfo("BEGIN") - cluster.setWalletMgr(walletMgr) - - if dontLaunch: # run test against remote cluster - jsonStr=None - with open(nodesFile, "r") as f: - jsonStr=f.read() - if not cluster.initializeNodesFromJson(jsonStr): - errorExit("Failed to initialize nodes from Json string.") - total_nodes=len(cluster.getNodes()) - - print("Stand up walletd") - if walletMgr.launch() is False: - errorExit("Failed to stand up keosd.") - - Print("producing nodes: %d, non-producing nodes: %d, topology: %s, delay between nodes launch(seconds): %d" % (pnodes, total_nodes-pnodes, topo, delay)) - - Print("Stand up cluster") - # set up read-only options for API node - specificExtraNodeosArgs={} - # last node will be the roTrx node - roTrxNodeIndex = total_nodes-1 - specificExtraNodeosArgs[roTrxNodeIndex]="--read-only-threads " - specificExtraNodeosArgs[roTrxNodeIndex]+=str(args.read_only_threads) - specificExtraNodeosArgs[roTrxNodeIndex]+=" --max-transaction-time " - specificExtraNodeosArgs[roTrxNodeIndex]+=" 10 " - if args.eos_vm_oc_enable != "off": - if platform.system() != "Linux": - Print("OC not run on Linux. Skip the test") - exit(0) # Do not fail the test - specificExtraNodeosArgs[roTrxNodeIndex]+=" --eos-vm-oc-enable " - specificExtraNodeosArgs[roTrxNodeIndex]+=args.eos_vm_oc_enable - if args.wasm_runtime: - specificExtraNodeosArgs[roTrxNodeIndex]+=" --wasm-runtime " - specificExtraNodeosArgs[roTrxNodeIndex]+=args.wasm_runtime - - if cluster.launch(pnodes=pnodes, totalNodes=total_nodes, topo=topo, delay=delay, activateIF=activateIF, specificExtraNodeosArgs=specificExtraNodeosArgs) is False: - errorExit("Failed to stand up eos cluster.") - - Print ("Wait for Cluster stabilization") - # wait for cluster to start producing blocks - if not cluster.waitOnClusterBlockNumSync(3): - errorExit("Cluster never stabilized") - - producerNode = cluster.getNode() - roTrxNode = cluster.nodes[-1] - apiNode = cluster.nodes[-2] - - -def deployTestContracts(): - Utils.Print("create test accounts") - tightloopAccount = Account(tightloopAccountName) - tightloopAccount.ownerPublicKey = EOSIO_ACCT_PUBLIC_DEFAULT_KEY - tightloopAccount.activePublicKey = EOSIO_ACCT_PUBLIC_DEFAULT_KEY - cluster.createAccountAndVerify(tightloopAccount, cluster.eosioAccount, buyRAM=1000000) - - tightloopContractDir="unittests/test-contracts/tightloop" - tightloopWasmFile="tightloop.wasm" - tightloopAbiFile="tightloop.abi" - producerNode.publishContract(tightloopAccount, tightloopContractDir, tightloopWasmFile, tightloopAbiFile, waitForTransBlock=True) - - -def sendROTransaction(account, action, data, auth=[], opts=None): - trx = { - "actions": [{ - "account": account, - "name": action, - "authorization": auth, - "data": data - }] - } - return roTrxNode.pushTransaction(trx, opts) - -def longROtrxThread(): - Print("start longROtrxThread") - - while stopThread==False: - sendROTransaction(tightloopAccountName, 'doit', {"count": 50000000}, opts='--read') #50 million is a good number to always take >10ms - -def shortROtrxThread(): - Print("start a shortROtrxThread") - - global numShortResponses - global allResponsesGood - - while stopThread==False: - results = sendROTransaction(tightloopAccountName, 'doit', {"count": 250000}, opts='--read') #250k will hopefully complete within 10ms, even on slower hardware - with threadLock: - allResponsesGood &= results[0] - numShortResponses += 1 - -def testPassed(): - return allResponsesGood and numShortResponses > 0 - -try: - startCluster() - deployTestContracts() - # ROtrx won't pump OC's cache so prime OC's cache of this contract with some actions - for i in range(2): - trans = apiNode.pushMessage(tightloopAccountName, 'doit', '{"count": 1}', "-p {}@active".format(tightloopAccountName)) - assert(trans[0]) - apiNode.waitForTransactionInBlock(trans[1]['transaction_id'], exitOnError=True) - - # start a background thread that constantly runs a ROtrx that is never expected to complete in max-transaction-time - thr = threading.Thread(target = longROtrxThread) - thr.start() - - # start twice the number of configured nodeos RO threads to spam short ROtrx; the 2x is hopefully to keep nodeos RO threads as busy as possible - shortThreadList = [] - for i in range(args.read_only_threads*2): - shortThreadList.append(threading.Thread(target = shortROtrxThread)) - shortThreadList[i].start() - - time.sleep(testLengthSeconds) -finally: - stopThread = True; - thr.join() - for sthr in shortThreadList: - sthr.join() - TestHelper.shutdown(cluster, walletMgr, testPassed(), dumpErrorDetails) - -errorCode = 0 if testPassed() else 1 -exit(errorCode) diff --git a/unittests/test-contracts/CMakeLists.txt b/unittests/test-contracts/CMakeLists.txt index 451481faa1..32195be224 100644 --- a/unittests/test-contracts/CMakeLists.txt +++ b/unittests/test-contracts/CMakeLists.txt @@ -38,7 +38,6 @@ add_subdirectory( test_api ) add_subdirectory( test_api_db ) add_subdirectory( test_api_multi_index ) add_subdirectory( test_ram_limit ) -add_subdirectory( tightloop ) add_subdirectory( action_results ) add_subdirectory( wasm_config_bios ) add_subdirectory( params_test ) diff --git a/unittests/test-contracts/tightloop/CMakeLists.txt b/unittests/test-contracts/tightloop/CMakeLists.txt deleted file mode 100644 index 03d418a763..0000000000 --- a/unittests/test-contracts/tightloop/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -if(EOSIO_COMPILE_TEST_CONTRACTS) - add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/tightloop.wasm" - COMMAND "${CDT_ROOT}/bin/eosio-wast2wasm" "${CMAKE_CURRENT_SOURCE_DIR}/tightloop.wast" -o "${CMAKE_CURRENT_BINARY_DIR}/tightloop.wasm" - DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/tightloop.wast") - add_custom_target(gen_tightloop_wasm ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/tightloop.wasm") -else() - configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/tightloop.wasm ${CMAKE_CURRENT_BINARY_DIR}/tightloop.wasm COPYONLY ) -endif() -configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/tightloop.abi ${CMAKE_CURRENT_BINARY_DIR}/tightloop.abi COPYONLY ) diff --git a/unittests/test-contracts/tightloop/tightloop.abi b/unittests/test-contracts/tightloop/tightloop.abi deleted file mode 100644 index 17a6f9394b..0000000000 --- a/unittests/test-contracts/tightloop/tightloop.abi +++ /dev/null @@ -1,21 +0,0 @@ -{ - "version": "eosio::abi/1.0", - "types": [], - "structs": [{ - "name": "doit", - "base": "", - "fields": [{"name":"count","type":"uint64"}] - }], - "actions": [{ - "name": "doit", - "type": "doit", - "ricardian_contract": "" - } - ], - "tables": [], - "ricardian_clauses": [], - "error_messages": [], - "abi_extensions": [], - "variants": [], - "action_results": [] -} \ No newline at end of file diff --git a/unittests/test-contracts/tightloop/tightloop.wasm b/unittests/test-contracts/tightloop/tightloop.wasm deleted file mode 100644 index 4722cb239139d24b3af4bc1346c63e4d10c940da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 116 zcmV~$OA5k35Cy>Zrt?vUK$hYvGP>{