Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3957a55641 | |||
| 01cdc2423d | |||
| 99e4c4e669 | |||
| 422251466c | |||
| 18bfc53964 | |||
| 11fe3fb262 | |||
| e6f84d85d2 | |||
| fa635449fb | |||
| e682c99cec | |||
| 80d75b7ecc | |||
| feb14eee57 | |||
| 9f0e9d9eb8 | |||
| ce6ce28931 | |||
| d9ad5a3bf3 | |||
| ae853b4586 | |||
| f80e29ecec | |||
| fa4e5ef0c2 | |||
| ed1d8dafcc | |||
| 926cc4e509 | |||
| 6eeb2d15ac |
@@ -0,0 +1,686 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from datetime import datetime
|
||||
from datetime import timedelta
|
||||
import time
|
||||
import json
|
||||
import signal
|
||||
import rlp
|
||||
import re
|
||||
from ethereum import transactions
|
||||
from binascii import unhexlify
|
||||
|
||||
from TestHarness import Cluster, Node, TestHelper, Utils, WalletMgr
|
||||
from TestHarness.TestHelper import AppArgs
|
||||
from threading import Thread
|
||||
|
||||
###############################################################
|
||||
# nodeos_trust_evm_forked_chain_test
|
||||
#
|
||||
# This test sets up 2 producing nodes and one "bridge" node using test_control_api_plugin.
|
||||
# One producing node has 11 of the elected producers and the other has 10 of the elected producers.
|
||||
# All the producers are named in alphabetical order, so that the 11 producers, in the one production node, are
|
||||
# scheduled first, followed by the 10 producers in the other producer node. Each producing node is only connected
|
||||
# to the other producing node via the "bridge" node.
|
||||
# The bridge node has the test_control_api_plugin, which exposes a restful interface that the test script uses to kill
|
||||
# the "bridge" node at a specific producer in the production cycle. This is used to fork the producer network
|
||||
# precisely when the 11 producer node has finished producing and the other producing node is about to produce.
|
||||
# The fork in the producer network results in one fork of the block chain that advances with 10 producers with a LIB
|
||||
# that has advanced, since all of the previous blocks were confirmed and the producer that was scheduled for that
|
||||
# slot produced it, and one with 11 producers with a LIB that has not advanced. This situation is validated by
|
||||
# the test script.
|
||||
# After both chains are allowed to produce, the "bridge" node is turned back on.
|
||||
# Time is allowed to progress so that the "bridge" node can catchup and both producer nodes to come to consensus
|
||||
# The block log is then checked for both producer nodes to verify that the 10 producer fork is selected and that
|
||||
# both nodes are in agreement on the block log.
|
||||
#
|
||||
###############################################################
|
||||
|
||||
Print=Utils.Print
|
||||
|
||||
from core_symbol import CORE_SYMBOL
|
||||
|
||||
def analyzeBPs(bps0, bps1, expectDivergence):
|
||||
start=0
|
||||
index=None
|
||||
length=len(bps0)
|
||||
firstDivergence=None
|
||||
errorInDivergence=False
|
||||
analysysPass=0
|
||||
bpsStr=None
|
||||
bpsStr0=None
|
||||
bpsStr1=None
|
||||
while start < length:
|
||||
analysysPass+=1
|
||||
bpsStr=None
|
||||
for i in range(start,length):
|
||||
bp0=bps0[i]
|
||||
bp1=bps1[i]
|
||||
if bpsStr is None:
|
||||
bpsStr=""
|
||||
else:
|
||||
bpsStr+=", "
|
||||
blockNum0=bp0["blockNum"]
|
||||
prod0=bp0["prod"]
|
||||
blockNum1=bp1["blockNum"]
|
||||
prod1=bp1["prod"]
|
||||
numDiff=True if blockNum0!=blockNum1 else False
|
||||
prodDiff=True if prod0!=prod1 else False
|
||||
if numDiff or prodDiff:
|
||||
index=i
|
||||
if firstDivergence is None:
|
||||
firstDivergence=min(blockNum0, blockNum1)
|
||||
if not expectDivergence:
|
||||
errorInDivergence=True
|
||||
break
|
||||
bpsStr+=str(blockNum0)+"->"+prod0
|
||||
|
||||
if index is None:
|
||||
if expectDivergence:
|
||||
errorInDivergence=True
|
||||
break
|
||||
return None
|
||||
|
||||
bpsStr0=None
|
||||
bpsStr2=None
|
||||
start=length
|
||||
for i in range(index,length):
|
||||
if bpsStr0 is None:
|
||||
bpsStr0=""
|
||||
bpsStr1=""
|
||||
else:
|
||||
bpsStr0+=", "
|
||||
bpsStr1+=", "
|
||||
bp0=bps0[i]
|
||||
bp1=bps1[i]
|
||||
blockNum0=bp0["blockNum"]
|
||||
prod0=bp0["prod"]
|
||||
blockNum1=bp1["blockNum"]
|
||||
prod1=bp1["prod"]
|
||||
numDiff="*" if blockNum0!=blockNum1 else ""
|
||||
prodDiff="*" if prod0!=prod1 else ""
|
||||
if not numDiff and not prodDiff:
|
||||
start=i
|
||||
index=None
|
||||
if expectDivergence:
|
||||
errorInDivergence=True
|
||||
break
|
||||
bpsStr0+=str(blockNum0)+numDiff+"->"+prod0+prodDiff
|
||||
bpsStr1+=str(blockNum1)+numDiff+"->"+prod1+prodDiff
|
||||
if errorInDivergence:
|
||||
break
|
||||
|
||||
if errorInDivergence:
|
||||
msg="Failed analyzing block producers - "
|
||||
if expectDivergence:
|
||||
msg+="nodes do not indicate different block producers for the same blocks, but they are expected to diverge at some point."
|
||||
else:
|
||||
msg+="did not expect nodes to indicate different block producers for the same blocks."
|
||||
msg+="\n Matching Blocks= %s \n Diverging branch node0= %s \n Diverging branch node1= %s" % (bpsStr,bpsStr0,bpsStr1)
|
||||
Utils.errorExit(msg)
|
||||
|
||||
return firstDivergence
|
||||
|
||||
def getMinHeadAndLib(prodNodes):
|
||||
info0=prodNodes[0].getInfo(exitOnError=True)
|
||||
info1=prodNodes[1].getInfo(exitOnError=True)
|
||||
headBlockNum=min(int(info0["head_block_num"]),int(info1["head_block_num"]))
|
||||
libNum=min(int(info0["last_irreversible_block_num"]), int(info1["last_irreversible_block_num"]))
|
||||
return (headBlockNum, libNum)
|
||||
|
||||
def generate_evm_transactions(nonce, prodNode):
|
||||
while True:
|
||||
Utils.Print("Execute ETH contract")
|
||||
nonce += 1
|
||||
toAdd = "2787b98fc4e731d0456b3941f0b3fe2e01430000"
|
||||
amount = 0
|
||||
unsignedTrx = transactions.Transaction(
|
||||
nonce,
|
||||
150000000000, #150 GWei
|
||||
100000, #100k Gas
|
||||
toAdd,
|
||||
amount,
|
||||
unhexlify("6057361d000000000000000000000000000000000000000000000000000000000000007b")
|
||||
)
|
||||
rlptx = rlp.encode(unsignedTrx.sign(evmSendKey, evmChainId), transactions.Transaction)
|
||||
actData = {"ram_payer":"evmevmevmevm", "rlptx":rlptx.hex()}
|
||||
evmRetValue = prodNode.pushMessage(evmAcc.name, "pushtx", json.dumps(actData), '-p evmevmevmevm', expiration=300)
|
||||
if not evmRetValue[0]:
|
||||
try:
|
||||
Utils.Print("*** Invalid nonce ***")
|
||||
Utils.Print(f"evmRetValue: {evmRetValue}")
|
||||
found = re.search('"console": "nonce:([0-9]+)', str(evmRetValue[1])).group(1)
|
||||
Utils.Print(f"*** Invalid nonce of {nonce} should be {found} ***")
|
||||
nonce = int(found)
|
||||
nonce -= 1 # for the +1 at top of loop
|
||||
continue
|
||||
except AttributeError as e:
|
||||
Utils.Print(f"Exception {e}")
|
||||
pass
|
||||
assert evmRetValue[0], "pushtx to ETH contract failed."
|
||||
Utils.Print("\tReturn value:", evmRetValue[1]["processed"]["action_traces"][0]["return_value_data"])
|
||||
Utils.Print("\tBlock#", evmRetValue[1]["processed"]["block_num"])
|
||||
row0=prodNode.getTableRow(evmAcc.name, 3, "storage", 0)
|
||||
Utils.Print("\tTable row:", row0)
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
appArgs=AppArgs()
|
||||
extraArgs = appArgs.add(flag="--trust-evm-contract-root", type=str, help="TrustEVM contract build dir", default=None)
|
||||
|
||||
args = TestHelper.parse_args({"--prod-count","--dump-error-details","--keep-logs","-v","--leave-running","--clean-run",
|
||||
"--wallet-port"}, applicationSpecificArgs=appArgs)
|
||||
Utils.Debug=args.v
|
||||
totalProducerNodes=2
|
||||
totalNonProducerNodes=1
|
||||
totalNodes=totalProducerNodes+totalNonProducerNodes
|
||||
maxActiveProducers=21
|
||||
totalProducers=maxActiveProducers
|
||||
cluster=Cluster(walletd=True)
|
||||
dumpErrorDetails=args.dump_error_details
|
||||
keepLogs=args.keep_logs
|
||||
dontKill=args.leave_running
|
||||
prodCount=args.prod_count
|
||||
killAll=args.clean_run
|
||||
walletPort=args.wallet_port
|
||||
trustEvmContractRoot=args.trust_evm_contract_root
|
||||
|
||||
walletMgr=WalletMgr(True, port=walletPort)
|
||||
testSuccessful=False
|
||||
killEosInstances=not dontKill
|
||||
killWallet=not dontKill
|
||||
|
||||
WalletdName=Utils.EosWalletName
|
||||
ClientName="cleos"
|
||||
|
||||
assert trustEvmContractRoot is not None, "--trust-evm-contract-root is required"
|
||||
|
||||
try:
|
||||
TestHelper.printSystemInfo("BEGIN")
|
||||
|
||||
cluster.setWalletMgr(walletMgr)
|
||||
cluster.killall(allInstances=killAll)
|
||||
cluster.cleanup()
|
||||
Print("Stand up cluster")
|
||||
specificExtraNodeosArgs={}
|
||||
# producer nodes will be mapped to 0 through totalProducerNodes-1, so the number totalProducerNodes will be the non-producing node
|
||||
specificExtraNodeosArgs[totalProducerNodes]="--plugin eosio::test_control_api_plugin --plugin eosio::state_history_plugin --state-history-endpoint 127.0.0.1:8999 --trace-history --chain-state-history --disable-replay-opts "
|
||||
extraNodeosArgs="--contracts-console"
|
||||
|
||||
|
||||
# *** setup topogrophy ***
|
||||
|
||||
# "bridge" shape connects defprocera through defproducerk (in node0) to each other and defproducerl through defproduceru (in node01)
|
||||
# and the only connection between those 2 groups is through the bridge node
|
||||
|
||||
if cluster.launch(prodCount=prodCount, topo="bridge", pnodes=totalProducerNodes,
|
||||
totalNodes=totalNodes, totalProducers=totalProducers,
|
||||
useBiosBootFile=False, extraNodeosArgs=extraNodeosArgs, specificExtraNodeosArgs=specificExtraNodeosArgs) is False:
|
||||
Utils.cmdError("launcher")
|
||||
Utils.errorExit("Failed to stand up eos cluster.")
|
||||
Print("Validating system accounts after bootstrap")
|
||||
cluster.validateAccounts(None)
|
||||
|
||||
|
||||
# *** create accounts to vote in desired producers ***
|
||||
|
||||
accounts=cluster.createAccountKeys(6)
|
||||
if accounts is None:
|
||||
Utils.errorExit("FAILURE - create keys")
|
||||
accounts[0].name="tester111111"
|
||||
accounts[1].name="tester222222"
|
||||
accounts[2].name="tester333333"
|
||||
accounts[3].name="tester444444"
|
||||
accounts[4].name="tester555555"
|
||||
accounts[5].name="evmevmevmevm"
|
||||
|
||||
evmAcc = accounts[5]
|
||||
|
||||
testWalletName="test"
|
||||
|
||||
Print("Creating wallet \"%s\"." % (testWalletName))
|
||||
testWallet=walletMgr.create(testWalletName, [cluster.eosioAccount,accounts[0],accounts[1],accounts[2],accounts[3],accounts[4],accounts[5]])
|
||||
|
||||
for _, account in cluster.defProducerAccounts.items():
|
||||
walletMgr.importKey(account, testWallet, ignoreDupKeyWarning=True)
|
||||
|
||||
Print("Wallet \"%s\" password=%s." % (testWalletName, testWallet.password.encode("utf-8")))
|
||||
|
||||
|
||||
# *** identify each node (producers and non-producing node) ***
|
||||
|
||||
nonProdNode=None
|
||||
prodNodes=[]
|
||||
producers=[]
|
||||
for i in range(0, totalNodes):
|
||||
node=cluster.getNode(i)
|
||||
node.producers=Cluster.parseProducers(i)
|
||||
numProducers=len(node.producers)
|
||||
Print("node has producers=%s" % (node.producers))
|
||||
if numProducers==0:
|
||||
if nonProdNode is None:
|
||||
nonProdNode=node
|
||||
nonProdNode.nodeNum=i
|
||||
else:
|
||||
Utils.errorExit("More than one non-producing nodes")
|
||||
else:
|
||||
for prod in node.producers:
|
||||
trans=node.regproducer(cluster.defProducerAccounts[prod], "http::/mysite.com", 0, waitForTransBlock=False, exitOnError=True)
|
||||
|
||||
prodNodes.append(node)
|
||||
producers.extend(node.producers)
|
||||
|
||||
|
||||
# *** delegate bandwidth to accounts ***
|
||||
|
||||
node=prodNodes[0]
|
||||
prodNode = node
|
||||
# create accounts via eosio as otherwise a bid is needed
|
||||
for account in accounts:
|
||||
Print("Create new account %s via %s" % (account.name, cluster.eosioAccount.name))
|
||||
trans=node.createInitializeAccount(account, cluster.eosioAccount, stakedDeposit=0, waitForTransBlock=True, stakeNet=10000, stakeCPU=10000, buyRAM=10000000, exitOnError=True)
|
||||
transferAmount="100000000.0000 {0}".format(CORE_SYMBOL)
|
||||
Print("Transfer funds %s from account %s to %s" % (transferAmount, cluster.eosioAccount.name, account.name))
|
||||
node.transferFunds(cluster.eosioAccount, account, transferAmount, "test transfer", waitForTransBlock=True)
|
||||
trans=node.delegatebw(account, 20000000.0000, 20000000.0000, waitForTransBlock=True, exitOnError=True)
|
||||
|
||||
|
||||
# *** vote using accounts ***
|
||||
|
||||
#verify nodes are in sync and advancing
|
||||
cluster.waitOnClusterSync(blockAdvancing=5)
|
||||
index=0
|
||||
for account in accounts:
|
||||
Print("Vote for producers=%s" % (producers))
|
||||
trans=prodNodes[index % len(prodNodes)].vote(account, producers, waitForTransBlock=True)
|
||||
index+=1
|
||||
|
||||
# *** Setup EVM contract ***
|
||||
|
||||
contractDir=trustEvmContractRoot + "/evm_runtime"
|
||||
wasmFile="evm_runtime.wasm"
|
||||
abiFile="evm_runtime.abi"
|
||||
Utils.Print("Publish evm_runtime contract")
|
||||
evmTrans = prodNode.publishContract(evmAcc, contractDir, wasmFile, abiFile, waitForTransBlock=True)
|
||||
transId=prodNode.getTransId(evmTrans)
|
||||
blockNum = prodNode.getBlockNumByTransId(transId)
|
||||
block = prodNode.getBlock(blockNum)
|
||||
Utils.Print("Block Id: ", block["id"])
|
||||
Utils.Print("Block timestamp: ", block["timestamp"])
|
||||
|
||||
Utils.Print("Set balance")
|
||||
evmTrans = prodNode.pushMessage(evmAcc.name, "setbal", '{"addy":"2787b98fc4e731d0456b3941f0b3fe2e01439961", "bal":"0000000000000000000000000000000100000000000000000000000000000000"}', '-p evmevmevmevm')
|
||||
prodNode.waitForTransBlockIfNeeded(evmTrans[1], True)
|
||||
|
||||
Utils.Print("Send balance")
|
||||
evmChainId = 15555
|
||||
fromAdd = "2787b98fc4e731d0456b3941f0b3fe2e01439961"
|
||||
toAdd = '0x9edf022004846bc987799d552d1b8485b317b7ed'
|
||||
amount = 100
|
||||
nonce = 0
|
||||
evmSendKey = "a3f1b69da92a0233ce29485d3049a4ace39e8d384bbc2557e3fc60940ce4e954"
|
||||
unsignedTrx = transactions.Transaction(
|
||||
nonce,
|
||||
150000000000, #150 GWei
|
||||
100000, #100k Gas
|
||||
toAdd,
|
||||
amount,
|
||||
b''
|
||||
)
|
||||
rlptx = rlp.encode(unsignedTrx.sign(evmSendKey, evmChainId), transactions.Transaction)
|
||||
actData = {"ram_payer":"evmevmevmevm", "rlptx":rlptx.hex()}
|
||||
evmTrans = prodNode.pushMessage(evmAcc.name, "pushtx", json.dumps(actData), '-p evmevmevmevm')
|
||||
prodNode.waitForTransBlockIfNeeded(evmTrans[1], True)
|
||||
|
||||
Utils.Print("Send balance again, should fail with wrong nonce")
|
||||
retValue = prodNode.pushMessage(evmAcc.name, "pushtx", json.dumps(actData), '-p evmevmevmevm', silentErrors=True, force=True)
|
||||
assert not retValue[0], f"push trx should have failed: {retValue}"
|
||||
|
||||
Utils.Print("Simple Solidity contract")
|
||||
# // SPDX-License-Identifier: GPL-3.0
|
||||
# pragma solidity >=0.7.0 <0.9.0;
|
||||
# contract Storage {
|
||||
# uint256 number;
|
||||
# function store(uint256 num) public {
|
||||
# number = num;
|
||||
# }
|
||||
# function retrieve() public view returns (uint256){
|
||||
# return number;
|
||||
# }
|
||||
# }
|
||||
retValue = prodNode.pushMessage(evmAcc.name, "updatecode", '{"address":"2787b98fc4e731d0456b3941f0b3fe2e01430000","incarnation":0,"code_hash":"286e3d498e2178afc91275f11d446e62a0d85b060ce66d6ca75f6ec9d874d560","code":"608060405234801561001057600080fd5b50600436106100365760003560e01c80632e64cec11461003b5780636057361d14610059575b600080fd5b610043610075565b60405161005091906100d9565b60405180910390f35b610073600480360381019061006e919061009d565b61007e565b005b60008054905090565b8060008190555050565b60008135905061009781610103565b92915050565b6000602082840312156100b3576100b26100fe565b5b60006100c184828501610088565b91505092915050565b6100d3816100f4565b82525050565b60006020820190506100ee60008301846100ca565b92915050565b6000819050919050565b600080fd5b61010c816100f4565b811461011757600080fd5b5056fea26469706673582212209a159a4f3847890f10bfb87871a61eba91c5dbf5ee3cf6398207e292eee22a1664736f6c63430008070033"}', '-p evmevmevmevm')
|
||||
|
||||
Utils.Print("Pausing for setup of trustevm node")
|
||||
input("Press Enter to continue...")
|
||||
|
||||
# start generating evm transactions
|
||||
thread = Thread(target=generate_evm_transactions, args=(nonce, prodNode))
|
||||
thread.start()
|
||||
|
||||
Print("Thread spawned")
|
||||
|
||||
# *** Identify a block where production is stable ***
|
||||
|
||||
#verify nodes are in sync and advancing
|
||||
cluster.waitOnClusterSync(blockAdvancing=5)
|
||||
blockNum=node.getNextCleanProductionCycle(trans)
|
||||
blockProducer=node.getBlockProducerByNum(blockNum)
|
||||
Print("Validating blockNum=%s, producer=%s" % (blockNum, blockProducer))
|
||||
cluster.biosNode.kill(signal.SIGTERM)
|
||||
|
||||
class HeadWaiter:
|
||||
def __init__(self, node):
|
||||
self.node=node
|
||||
self.cachedHeadBlockNum=node.getBlockNum()
|
||||
|
||||
def waitIfNeeded(self, blockNum):
|
||||
delta=self.cachedHeadBlockNum-blockNum
|
||||
if delta >= 0:
|
||||
return
|
||||
previousHeadBlockNum=self.cachedHeadBlockNum
|
||||
delta=-1*delta
|
||||
timeout=(delta+1)/2 + 3 # round up to nearest second and 3 second extra leeway
|
||||
self.node.waitForBlock(blockNum, timeout=timeout)
|
||||
self.cachedHeadBlockNum=node.getBlockNum()
|
||||
if blockNum > self.cachedHeadBlockNum:
|
||||
Utils.errorExit("Failed to advance from block number %d to %d in %d seconds. Only got to block number %d" % (previousHeadBlockNum, blockNum, timeout, self.cachedHeadBlockNum))
|
||||
|
||||
def getBlock(self, blockNum):
|
||||
self.waitIfNeeded(blockNum)
|
||||
return self.node.getBlock(blockNum)
|
||||
|
||||
#advance to the next block of 12
|
||||
lastBlockProducer=blockProducer
|
||||
|
||||
waiter=HeadWaiter(node)
|
||||
|
||||
while blockProducer==lastBlockProducer:
|
||||
blockNum+=1
|
||||
block=waiter.getBlock(blockNum)
|
||||
Utils.Print("Block num: %d, block: %s" % (blockNum, json.dumps(block, indent=4, sort_keys=True)))
|
||||
blockProducer=Node.getBlockAttribute(block, "producer", blockNum)
|
||||
|
||||
timestampStr=Node.getBlockAttribute(block, "timestamp", blockNum)
|
||||
timestamp=datetime.strptime(timestampStr, Utils.TimeFmt)
|
||||
|
||||
|
||||
# *** Identify what the production cycle is ***
|
||||
|
||||
productionCycle=[]
|
||||
producerToSlot={}
|
||||
slot=-1
|
||||
inRowCountPerProducer=12
|
||||
minNumBlocksPerProducer=10
|
||||
lastTimestamp=timestamp
|
||||
headBlockNum=node.getBlockNum()
|
||||
firstBlockForWindowMissedSlot=None
|
||||
while True:
|
||||
if blockProducer not in producers:
|
||||
Utils.errorExit("Producer %s was not one of the voted on producers" % blockProducer)
|
||||
|
||||
productionCycle.append(blockProducer)
|
||||
slot+=1
|
||||
if blockProducer in producerToSlot:
|
||||
Utils.errorExit("Producer %s was first seen in slot %d, but is repeated in slot %d" % (blockProducer, producerToSlot[blockProducer], slot))
|
||||
|
||||
producerToSlot[blockProducer]={"slot":slot, "count":0}
|
||||
lastBlockProducer=blockProducer
|
||||
blockSkip=[]
|
||||
roundSkips=0
|
||||
missedSlotAfter=[]
|
||||
if firstBlockForWindowMissedSlot is not None:
|
||||
missedSlotAfter.append(firstBlockForWindowMissedSlot)
|
||||
firstBlockForWindowMissedSlot=None
|
||||
|
||||
while blockProducer==lastBlockProducer:
|
||||
producerToSlot[blockProducer]["count"]+=1
|
||||
blockNum+=1
|
||||
block=waiter.getBlock(blockNum)
|
||||
blockProducer=Node.getBlockAttribute(block, "producer", blockNum)
|
||||
timestampStr=Node.getBlockAttribute(block, "timestamp", blockNum)
|
||||
timestamp=datetime.strptime(timestampStr, Utils.TimeFmt)
|
||||
timediff=timestamp-lastTimestamp
|
||||
slotTime=0.5
|
||||
slotsDiff=int(timediff.total_seconds() / slotTime)
|
||||
if slotsDiff != 1:
|
||||
slotTimeDelta=timedelta(slotTime)
|
||||
first=lastTimestamp + slotTimeDelta
|
||||
missed=first.strftime(Utils.TimeFmt)
|
||||
if slotsDiff > 2:
|
||||
last=timestamp - slotTimeDelta
|
||||
missed+= " thru " + last.strftime(Utils.TimeFmt)
|
||||
missedSlotAfter.append("%d (%s)" % (blockNum-1, missed))
|
||||
lastTimestamp=timestamp
|
||||
|
||||
if producerToSlot[lastBlockProducer]["count"] < minNumBlocksPerProducer or producerToSlot[lastBlockProducer]["count"] > inRowCountPerProducer:
|
||||
Utils.errorExit("Producer %s, in slot %d, expected to produce %d blocks but produced %d blocks. At block number %d. " %
|
||||
(lastBlockProducer, slot, inRowCountPerProducer, producerToSlot[lastBlockProducer]["count"], blockNum-1) +
|
||||
"Slots were missed after the following blocks: %s" % (", ".join(missedSlotAfter)))
|
||||
|
||||
if len(missedSlotAfter) > 0:
|
||||
# it may be the most recent producer missed a slot
|
||||
possibleMissed=missedSlotAfter[-1]
|
||||
if possibleMissed == blockNum - 1:
|
||||
firstBlockForWindowMissedSlot=possibleMissed
|
||||
|
||||
if blockProducer==productionCycle[0]:
|
||||
break
|
||||
|
||||
output=None
|
||||
for blockProducer in productionCycle:
|
||||
if output is None:
|
||||
output=""
|
||||
else:
|
||||
output+=", "
|
||||
output+=blockProducer+":"+str(producerToSlot[blockProducer]["count"])
|
||||
Print("ProductionCycle ->> {\n%s\n}" % output)
|
||||
|
||||
#retrieve the info for all the nodes to report the status for each
|
||||
for node in cluster.getNodes():
|
||||
node.getInfo()
|
||||
cluster.reportStatus()
|
||||
|
||||
|
||||
# *** Killing the "bridge" node ***
|
||||
|
||||
Print("Sending command to kill \"bridge\" node to separate the 2 producer groups.")
|
||||
# block number to start expecting node killed after
|
||||
preKillBlockNum=nonProdNode.getBlockNum()
|
||||
preKillBlockProducer=nonProdNode.getBlockProducerByNum(preKillBlockNum)
|
||||
Print("preKillBlockProducer = {}".format(preKillBlockProducer))
|
||||
# kill at last block before defproducerl, since the block it is killed on will get propagated
|
||||
killAtProducer="defproducerk"
|
||||
nonProdNode.killNodeOnProducer(producer=killAtProducer, whereInSequence=(inRowCountPerProducer-1))
|
||||
|
||||
|
||||
# *** Identify a highest block number to check while we are trying to identify where the divergence will occur ***
|
||||
|
||||
# will search full cycle after the current block, since we don't know how many blocks were produced since retrieving
|
||||
# block number and issuing kill command
|
||||
postKillBlockNum=prodNodes[1].getBlockNum()
|
||||
blockProducers0=[]
|
||||
blockProducers1=[]
|
||||
libs0=[]
|
||||
libs1=[]
|
||||
lastBlockNum=max([preKillBlockNum,postKillBlockNum])+2*maxActiveProducers*inRowCountPerProducer
|
||||
actualLastBlockNum=None
|
||||
prodChanged=False
|
||||
nextProdChange=False
|
||||
#identify the earliest LIB to start identify the earliest block to check if divergent branches eventually reach concensus
|
||||
(headBlockNum, libNumAroundDivergence)=getMinHeadAndLib(prodNodes)
|
||||
Print("Tracking block producers from %d till divergence or %d. Head block is %d and lowest LIB is %d" % (preKillBlockNum, lastBlockNum, headBlockNum, libNumAroundDivergence))
|
||||
transitionCount=0
|
||||
missedTransitionBlock=None
|
||||
for blockNum in range(preKillBlockNum,lastBlockNum + 1):
|
||||
#avoiding getting LIB until my current block passes the head from the last time I checked
|
||||
if blockNum>headBlockNum:
|
||||
(headBlockNum, libNumAroundDivergence)=getMinHeadAndLib(prodNodes)
|
||||
|
||||
# track the block number and producer from each producing node
|
||||
# we use timeout 70 here because of case when chain break, call to getBlockProducerByNum
|
||||
# and call of producer_plugin::schedule_delayed_production_loop happens nearly immediately
|
||||
# for 10 producers wait cycle is 10 * (12*0.5) = 60 seconds.
|
||||
# for 11 producers wait cycle is 11 * (12*0.5) = 66 seconds.
|
||||
blockProducer0=prodNodes[0].getBlockProducerByNum(blockNum, timeout=70)
|
||||
blockProducer1=prodNodes[1].getBlockProducerByNum(blockNum, timeout=70)
|
||||
Print("blockNum = {} blockProducer0 = {} blockProducer1 = {}".format(blockNum, blockProducer0, blockProducer1))
|
||||
blockProducers0.append({"blockNum":blockNum, "prod":blockProducer0})
|
||||
blockProducers1.append({"blockNum":blockNum, "prod":blockProducer1})
|
||||
|
||||
#in the case that the preKillBlockNum was also produced by killAtProducer, ensure that we have
|
||||
#at least one producer transition before checking for killAtProducer
|
||||
if not prodChanged:
|
||||
if preKillBlockProducer!=blockProducer0:
|
||||
prodChanged=True
|
||||
Print("prodChanged = True")
|
||||
|
||||
#since it is killing for the last block of killAtProducer, we look for the next producer change
|
||||
if not nextProdChange and prodChanged and blockProducer1==killAtProducer:
|
||||
nextProdChange=True
|
||||
Print("nextProdChange = True")
|
||||
elif nextProdChange and blockProducer1!=killAtProducer:
|
||||
Print("nextProdChange = False")
|
||||
if blockProducer0!=blockProducer1:
|
||||
Print("Divergence identified at block %s, node_00 producer: %s, node_01 producer: %s" % (blockNum, blockProducer0, blockProducer1))
|
||||
actualLastBlockNum=blockNum
|
||||
break
|
||||
else:
|
||||
missedTransitionBlock=blockNum
|
||||
transitionCount+=1
|
||||
Print("missedTransitionBlock = {} transitionCount = {}".format(missedTransitionBlock, transitionCount))
|
||||
# allow this to transition twice, in case the script was identifying an earlier transition than the bridge node received the kill command
|
||||
if transitionCount>1:
|
||||
Print("At block %d and have passed producer: %s %d times and we have not diverged, stopping looking and letting errors report" % (blockNum, killAtProducer, transitionCount))
|
||||
actualLastBlockNum=blockNum
|
||||
break
|
||||
|
||||
#if we diverge before identifying the actualLastBlockNum, then there is an ERROR
|
||||
if blockProducer0!=blockProducer1:
|
||||
extra="" if transitionCount==0 else " Diverged after expected killAtProducer transition at block %d." % (missedTransitionBlock)
|
||||
Utils.errorExit("Groups reported different block producers for block number %d.%s %s != %s." % (blockNum,extra,blockProducer0,blockProducer1))
|
||||
|
||||
#verify that the non producing node is not alive (and populate the producer nodes with current getInfo data to report if
|
||||
#an error occurs)
|
||||
if nonProdNode.verifyAlive():
|
||||
Utils.errorExit("Expected the non-producing node to have shutdown.")
|
||||
|
||||
Print("Analyzing the producers leading up to the block after killing the non-producing node, expecting divergence at %d" % (blockNum))
|
||||
|
||||
firstDivergence=analyzeBPs(blockProducers0, blockProducers1, expectDivergence=True)
|
||||
# Nodes should not have diverged till the last block
|
||||
if firstDivergence!=blockNum:
|
||||
Utils.errorExit("Expected to diverge at %s, but diverged at %s." % (firstDivergence, blockNum))
|
||||
blockProducers0=[]
|
||||
blockProducers1=[]
|
||||
|
||||
for prodNode in prodNodes:
|
||||
info=prodNode.getInfo()
|
||||
Print("node info: %s" % (info))
|
||||
|
||||
killBlockNum=blockNum
|
||||
lastBlockNum=killBlockNum+(maxActiveProducers - 1)*inRowCountPerProducer+1 # allow 1st testnet group to produce just 1 more block than the 2nd
|
||||
|
||||
Print("Tracking the blocks from the divergence till there are 10*12 blocks on one chain and 10*12+1 on the other, from block %d to %d" % (killBlockNum, lastBlockNum))
|
||||
|
||||
for blockNum in range(killBlockNum,lastBlockNum):
|
||||
blockProducer0=prodNodes[0].getBlockProducerByNum(blockNum)
|
||||
blockProducer1=prodNodes[1].getBlockProducerByNum(blockNum)
|
||||
blockProducers0.append({"blockNum":blockNum, "prod":blockProducer0})
|
||||
blockProducers1.append({"blockNum":blockNum, "prod":blockProducer1})
|
||||
|
||||
|
||||
Print("Analyzing the producers from the divergence to the lastBlockNum and verify they stay diverged, expecting divergence at block %d" % (killBlockNum))
|
||||
|
||||
firstDivergence=analyzeBPs(blockProducers0, blockProducers1, expectDivergence=True)
|
||||
if firstDivergence!=killBlockNum:
|
||||
Utils.errorExit("Expected to diverge at %s, but diverged at %s." % (firstDivergence, killBlockNum))
|
||||
blockProducers0=[]
|
||||
blockProducers1=[]
|
||||
|
||||
for prodNode in prodNodes:
|
||||
info=prodNode.getInfo()
|
||||
Print("node info: %s" % (info))
|
||||
|
||||
Print("Relaunching the non-producing bridge node to connect the producing nodes again")
|
||||
|
||||
if not nonProdNode.relaunch():
|
||||
Utils.errorExit("Failure - (non-production) node %d should have restarted" % (nonProdNode.nodeNum))
|
||||
|
||||
|
||||
Print("Waiting to allow forks to resolve")
|
||||
|
||||
for prodNode in prodNodes:
|
||||
info=prodNode.getInfo()
|
||||
Print("node info: %s" % (info))
|
||||
|
||||
#ensure that the nodes have enough time to get in concensus, so wait for 3 producers to produce their complete round
|
||||
time.sleep(inRowCountPerProducer * 3 / 2)
|
||||
remainingChecks=60
|
||||
match=False
|
||||
checkHead=False
|
||||
checkMatchBlock=killBlockNum
|
||||
forkResolved=False
|
||||
while remainingChecks>0:
|
||||
if checkMatchBlock == killBlockNum and checkHead:
|
||||
checkMatchBlock = prodNodes[0].getBlockNum()
|
||||
blockProducer0=prodNodes[0].getBlockProducerByNum(checkMatchBlock)
|
||||
blockProducer1=prodNodes[1].getBlockProducerByNum(checkMatchBlock)
|
||||
match=blockProducer0==blockProducer1
|
||||
if match:
|
||||
if checkHead:
|
||||
forkResolved=True
|
||||
break
|
||||
else:
|
||||
checkHead=True
|
||||
continue
|
||||
Print("Fork has not resolved yet, wait a little more. Block %s has producer %s for node_00 and %s for node_01. Original divergence was at block %s. Wait time remaining: %d" % (checkMatchBlock, blockProducer0, blockProducer1, killBlockNum, remainingChecks))
|
||||
time.sleep(1)
|
||||
remainingChecks-=1
|
||||
|
||||
assert forkResolved, "fork was not resolved in a reasonable time. node_00 lib {} head {} node_01 lib {} head {}".format(
|
||||
prodNodes[0].getIrreversibleBlockNum(),
|
||||
prodNodes[0].getHeadBlockNum(),
|
||||
prodNodes[1].getIrreversibleBlockNum(),
|
||||
prodNodes[1].getHeadBlockNum())
|
||||
|
||||
for prodNode in prodNodes:
|
||||
info=prodNode.getInfo()
|
||||
Print("node info: %s" % (info))
|
||||
|
||||
# ensure all blocks from the lib before divergence till the current head are now in consensus
|
||||
endBlockNum=max(prodNodes[0].getBlockNum(), prodNodes[1].getBlockNum())
|
||||
|
||||
Print("Identifying the producers from the saved LIB to the current highest head, from block %d to %d" % (libNumAroundDivergence, endBlockNum))
|
||||
|
||||
for blockNum in range(libNumAroundDivergence,endBlockNum):
|
||||
blockProducer0=prodNodes[0].getBlockProducerByNum(blockNum)
|
||||
blockProducer1=prodNodes[1].getBlockProducerByNum(blockNum)
|
||||
blockProducers0.append({"blockNum":blockNum, "prod":blockProducer0})
|
||||
blockProducers1.append({"blockNum":blockNum, "prod":blockProducer1})
|
||||
|
||||
|
||||
Print("Analyzing the producers from the saved LIB to the current highest head and verify they match now")
|
||||
|
||||
analyzeBPs(blockProducers0, blockProducers1, expectDivergence=False)
|
||||
|
||||
resolvedKillBlockProducer=None
|
||||
for prod in blockProducers0:
|
||||
if prod["blockNum"]==killBlockNum:
|
||||
resolvedKillBlockProducer = prod["prod"]
|
||||
if resolvedKillBlockProducer is None:
|
||||
Utils.errorExit("Did not find find block %s (the original divergent block) in blockProducers0, test setup is wrong. blockProducers0: %s" % (killBlockNum, ", ".join(blockProducers0)))
|
||||
Print("Fork resolved and determined producer %s for block %s" % (resolvedKillBlockProducer, killBlockNum))
|
||||
|
||||
blockProducers0=[]
|
||||
blockProducers1=[]
|
||||
|
||||
Utils.Print("Press enter to end test")
|
||||
input("Press Enter...")
|
||||
|
||||
|
||||
testSuccessful=True
|
||||
finally:
|
||||
TestHelper.shutdown(cluster, walletMgr, testSuccessful=testSuccessful, killEosInstances=killEosInstances, killWallet=killWallet, keepLogs=keepLogs, cleanRun=killAll, dumpErrorDetails=dumpErrorDetails)
|
||||
|
||||
exitCode = 0 if testSuccessful else 1
|
||||
exit(exitCode)
|
||||
@@ -36,6 +36,9 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/nodeos_chainbase_allocation_test.py $
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/nodeos_protocol_feature_test.py ${CMAKE_CURRENT_BINARY_DIR}/nodeos_protocol_feature_test.py COPYONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/nodeos_multiple_version_protocol_feature_test.py ${CMAKE_CURRENT_BINARY_DIR}/nodeos_multiple_version_protocol_feature_test.py COPYONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/nodeos_extra_packed_data_test.py ${CMAKE_CURRENT_BINARY_DIR}/nodeos_extra_packed_data_test.py COPYONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/nodeos_trust_evm_test.py ${CMAKE_CURRENT_BINARY_DIR}/nodeos_trust_evm_test.py COPYONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/nodeos_trust_evm_server.py ${CMAKE_CURRENT_BINARY_DIR}/nodeos_trust_evm_server.py COPYONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/nodeos_trust_evm_fork_server.py ${CMAKE_CURRENT_BINARY_DIR}/nodeos_trust_evm_fork_server.py COPYONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/validate-dirty-db.py ${CMAKE_CURRENT_BINARY_DIR}/validate-dirty-db.py COPYONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/launcher_test.py ${CMAKE_CURRENT_BINARY_DIR}/launcher_test.py COPYONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/keosd_auto_launch_test.py ${CMAKE_CURRENT_BINARY_DIR}/keosd_auto_launch_test.py COPYONLY)
|
||||
@@ -78,6 +81,9 @@ add_test(NAME block_log_util_test COMMAND tests/block_log_util_test.py -v --clea
|
||||
set_property(TEST block_log_util_test PROPERTY LABELS nonparallelizable_tests)
|
||||
add_test(NAME block_log_retain_blocks_test COMMAND tests/block_log_retain_blocks_test.py -v --clean-run --dump-error-detail WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
set_property(TEST block_log_retain_blocks_test PROPERTY LABELS nonparallelizable_tests)
|
||||
# comment out for now since prerequisites are not available
|
||||
#add_test(NAME nodeos_trust_evm_test COMMAND tests/nodeos_trust_evm_test.py -v --clean-run --dump-error-detail WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
#set_property(TEST nodeos_trust_evm_test PROPERTY LABELS nonparallelizable_tests)
|
||||
|
||||
option(ABIEOS_ONLY_LIBRARY "define and build the ABIEOS library" ON)
|
||||
set(ABIEOS_INSTALL_COMPONENT "dev")
|
||||
|
||||
@@ -790,8 +790,9 @@ class Node(object):
|
||||
except subprocess.CalledProcessError as ex:
|
||||
if not shouldFail:
|
||||
end=time.perf_counter()
|
||||
out=ex.output.decode("utf-8")
|
||||
msg=ex.stderr.decode("utf-8")
|
||||
Utils.Print("ERROR: Exception during set contract. cmd Duration: %.3f sec. %s" % (end-start, msg))
|
||||
Utils.Print("ERROR: Exception during set contract. stderr: %s. stdout: %s. cmd Duration: %.3f sec." % (msg, out, end-start))
|
||||
return None
|
||||
else:
|
||||
retMap={}
|
||||
@@ -874,13 +875,19 @@ class Node(object):
|
||||
return (False, msg)
|
||||
|
||||
# returns tuple with transaction execution status and transaction
|
||||
def pushMessage(self, account, action, data, opts, silentErrors=False, signatures=None, expectTrxTrace=True):
|
||||
cmd="%s %s push action -j %s %s" % (Utils.EosClientPath, self.eosClientArgs(), account, action)
|
||||
def pushMessage(self, account, action, data, opts, silentErrors=False, signatures=None, expectTrxTrace=True, force=False, expiration=None):
|
||||
expirationStr = ""
|
||||
if expiration is not None:
|
||||
expirationStr = "--expiration %d " % (expiration)
|
||||
|
||||
cmd="%s %s push action -j %s %s %s" % (Utils.EosClientPath, self.eosClientArgs(), expirationStr, account, action)
|
||||
cmdArr=cmd.split()
|
||||
# not using __sign_str, since cmdArr messes up the string
|
||||
if signatures is not None:
|
||||
cmdArr.append("--sign-with")
|
||||
cmdArr.append("[ \"%s\" ]" % ("\", \"".join(signatures)))
|
||||
if force:
|
||||
cmdArr.append("-f")
|
||||
if data is not None:
|
||||
cmdArr.append(data)
|
||||
if opts is not None:
|
||||
@@ -896,10 +903,11 @@ class Node(object):
|
||||
return (Node.getTransStatus(trans) == 'executed' if expectTrxTrace else True, trans)
|
||||
except subprocess.CalledProcessError as ex:
|
||||
msg=ex.stderr.decode("utf-8")
|
||||
output=ex.output.decode("utf-8")
|
||||
if not silentErrors:
|
||||
end=time.perf_counter()
|
||||
Utils.Print("ERROR: Exception during push message. cmd Duration=%.3f sec. %s" % (end - start, msg))
|
||||
return (False, msg)
|
||||
Utils.Print("ERROR: Exception during push message. stderr: %s. stdout: %s. cmd Duration=%.3f sec." % (msg, output, end - start))
|
||||
return (False, output)
|
||||
|
||||
@staticmethod
|
||||
def __sign_str(sign, keys):
|
||||
@@ -996,8 +1004,9 @@ class Node(object):
|
||||
except subprocess.CalledProcessError as ex:
|
||||
if not silentErrors:
|
||||
end=time.perf_counter()
|
||||
out=ex.output.decode("utf-8")
|
||||
msg=ex.stderr.decode("utf-8")
|
||||
errorMsg="Exception during \"%s\". Exception message: %s. cmd Duration=%.3f sec. %s" % (cmdDesc, msg, end-start, exitMsg)
|
||||
errorMsg="Exception during \"%s\". Exception message: %s. stdout: %s. cmd Duration=%.3f sec. %s" % (cmdDesc, msg, out, end-start, exitMsg)
|
||||
if exitOnError:
|
||||
Utils.cmdError(errorMsg)
|
||||
Utils.errorExit(errorMsg)
|
||||
|
||||
@@ -80,7 +80,7 @@ class WalletMgr(object):
|
||||
statusMsg+=" port %d is NOT available." % (self.port)
|
||||
Utils.Print("Launching %s, note similar processes running. %s" % (Utils.EosWalletName, statusMsg))
|
||||
|
||||
cmd="%s --data-dir %s --config-dir %s --http-server-address=%s:%d --http-max-response-time-ms 99999 --verbose-http-errors" % (
|
||||
cmd="%s --data-dir %s --config-dir %s --unlock-timeout=999999 --http-server-address=%s:%d --http-max-response-time-ms 99999 --verbose-http-errors" % (
|
||||
Utils.EosWalletPath, WalletMgr.__walletDataDir, WalletMgr.__walletDataDir, self.host, self.port)
|
||||
if Utils.Debug: Utils.Print("cmd: %s" % (cmd))
|
||||
with open(WalletMgr.__walletLogOutFile, 'w') as sout, open(WalletMgr.__walletLogErrFile, 'w') as serr:
|
||||
|
||||
Executable
+342
@@ -0,0 +1,342 @@
|
||||
#!/usr/bin/env python3
|
||||
import random
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
import signal
|
||||
import calendar
|
||||
from datetime import datetime
|
||||
|
||||
from flask import Flask, request, jsonify
|
||||
from flask_cors import CORS
|
||||
from eth_hash.auto import keccak
|
||||
import requests
|
||||
import json
|
||||
|
||||
from binascii import unhexlify
|
||||
from TestHarness import Cluster, TestHelper, Utils, WalletMgr
|
||||
from TestHarness.TestHelper import AppArgs
|
||||
from core_symbol import CORE_SYMBOL
|
||||
|
||||
|
||||
###############################################################
|
||||
# nodeos_trust_evm_fork_server
|
||||
#
|
||||
# Set up a TrustEVM env
|
||||
#
|
||||
# This test sets up 2 producing nodes and one "bridge" node using test_control_api_plugin.
|
||||
# One producing node has 3 of the elected producers and the other has 1 of the elected producers.
|
||||
# All the producers are named in alphabetical order, so that the 3 producers, in the one production node, are
|
||||
# scheduled first, followed by the 1 producer in the other producer node. Each producing node is only connected
|
||||
# to the other producing node via the "bridge" node.
|
||||
# The bridge node has the test_control_api_plugin, which exposes a restful interface that the test script uses to kill
|
||||
# the "bridge" node when /fork endpoint called.
|
||||
#
|
||||
# --trust-evm-contract-root should point to root of TrustEVM contract build dir
|
||||
# --genesis-json file to save generated EVM genesis json
|
||||
# --read-endpoint trustnode-rpc endpoint (read endpoint)
|
||||
#
|
||||
# Example:
|
||||
# ./tests/nodeos_trust_fork_server.py --trust-evm-contract-root ~/ext/TrustEVM/contract/build --leave-running
|
||||
#
|
||||
# Launches wallet at port: 9899
|
||||
# Example: bin/cleos --wallet-url http://127.0.0.1:9899 ...
|
||||
#
|
||||
# Sets up endpoint on port 5000
|
||||
# / - for req['method'] == "eth_sendRawTransaction"
|
||||
# /fork - create forked chain, does not return until a fork has started
|
||||
# /restore - resolve fork and stabilize chain
|
||||
#
|
||||
# Dependencies:
|
||||
# pip install eth-hash requests flask flask-cors
|
||||
###############################################################
|
||||
|
||||
Print=Utils.Print
|
||||
errorExit=Utils.errorExit
|
||||
|
||||
appArgs=AppArgs()
|
||||
appArgs.add(flag="--trust-evm-contract-root", type=str, help="TrustEVM contract build dir", default=None)
|
||||
appArgs.add(flag="--genesis-json", type=str, help="File to save generated genesis json", default="trust-evm-genesis.json")
|
||||
appArgs.add(flag="--read-endpoint", type=str, help="EVM read enpoint (trustevm-rpc)", default="http://localhost:8881")
|
||||
|
||||
args=TestHelper.parse_args({"--keep-logs","--dump-error-details","-v","--leave-running","--clean-run" }, applicationSpecificArgs=appArgs)
|
||||
debug=args.v
|
||||
killEosInstances= not args.leave_running
|
||||
dumpErrorDetails=args.dump_error_details
|
||||
keepLogs=args.keep_logs
|
||||
killAll=args.clean_run
|
||||
trustEvmContractRoot=args.trust_evm_contract_root
|
||||
gensisJson=args.genesis_json
|
||||
readEndpoint=args.read_endpoint
|
||||
|
||||
assert trustEvmContractRoot is not None, "--trust-evm-contract-root is required"
|
||||
|
||||
totalProducerNodes=2
|
||||
totalNonProducerNodes=1
|
||||
totalNodes=totalProducerNodes+totalNonProducerNodes
|
||||
maxActiveProducers=21
|
||||
totalProducers=maxActiveProducers
|
||||
|
||||
seed=1
|
||||
Utils.Debug=debug
|
||||
testSuccessful=False
|
||||
|
||||
random.seed(seed) # Use a fixed seed for repeatability.
|
||||
cluster=Cluster(walletd=True)
|
||||
walletMgr=WalletMgr(True)
|
||||
|
||||
|
||||
try:
|
||||
TestHelper.printSystemInfo("BEGIN")
|
||||
|
||||
cluster.setWalletMgr(walletMgr)
|
||||
cluster.killall(allInstances=killAll)
|
||||
cluster.cleanup()
|
||||
walletMgr.killall(allInstances=killAll)
|
||||
walletMgr.cleanup()
|
||||
|
||||
# *** setup topogrophy ***
|
||||
|
||||
# "bridge" shape connects defprocera through defproducerc (3 in node0) to each other and defproduceru (1 in node01)
|
||||
# and the only connection between those 2 groups is through the bridge node
|
||||
|
||||
specificExtraNodeosArgs={}
|
||||
# Connect SHiP to node01 so it will switch forks as they are resolved
|
||||
specificExtraNodeosArgs[1]="--plugin eosio::state_history_plugin --state-history-endpoint 127.0.0.1:8999 --trace-history --chain-state-history --disable-replay-opts "
|
||||
# producer nodes will be mapped to 0 through totalProducerNodes-1, so the number totalProducerNodes will be the non-producing node
|
||||
specificExtraNodeosArgs[totalProducerNodes]="--plugin eosio::test_control_api_plugin "
|
||||
extraNodeosArgs="--contracts-console"
|
||||
|
||||
Print("Stand up cluster")
|
||||
if cluster.launch(topo="bridge", pnodes=totalProducerNodes,
|
||||
totalNodes=totalNodes, totalProducers=totalProducers,
|
||||
useBiosBootFile=False, extraNodeosArgs=extraNodeosArgs, specificExtraNodeosArgs=specificExtraNodeosArgs) is False:
|
||||
Utils.cmdError("launcher")
|
||||
Utils.errorExit("Failed to stand up eos cluster.")
|
||||
|
||||
Print("Validating system accounts after bootstrap")
|
||||
cluster.validateAccounts(None)
|
||||
|
||||
Print ("Wait for Cluster stabilization")
|
||||
# wait for cluster to start producing blocks
|
||||
if not cluster.waitOnClusterBlockNumSync(3):
|
||||
errorExit("Cluster never stabilized")
|
||||
Print ("Cluster stabilized")
|
||||
|
||||
prodNode = cluster.getNode(0)
|
||||
prodNode0 = prodNode
|
||||
prodNode1 = cluster.getNode(1)
|
||||
nonProdNode = cluster.getNode(2)
|
||||
|
||||
accounts=cluster.createAccountKeys(6)
|
||||
if accounts is None:
|
||||
Utils.errorExit("FAILURE - create keys")
|
||||
|
||||
evmAcc = accounts[0]
|
||||
evmAcc.name = "evmevmevmevm"
|
||||
evmAcc.activePrivateKey="5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"
|
||||
evmAcc.activePublicKey="EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV"
|
||||
accounts[1].name="tester111111" # needed for voting
|
||||
accounts[2].name="tester222222" # needed for voting
|
||||
accounts[3].name="tester333333" # needed for voting
|
||||
accounts[4].name="tester444444" # needed for voting
|
||||
accounts[5].name="tester555555" # needed for voting
|
||||
|
||||
testWalletName="test"
|
||||
|
||||
Print("Creating wallet \"%s\"." % (testWalletName))
|
||||
testWallet=walletMgr.create(testWalletName, [cluster.eosioAccount,accounts[0],accounts[1],accounts[2],accounts[3],accounts[4],accounts[5]])
|
||||
|
||||
for _, account in cluster.defProducerAccounts.items():
|
||||
walletMgr.importKey(account, testWallet, ignoreDupKeyWarning=True)
|
||||
|
||||
for i in range(0, totalNodes):
|
||||
node=cluster.getNode(i)
|
||||
node.producers=Cluster.parseProducers(i)
|
||||
numProducers=len(node.producers)
|
||||
for prod in node.producers:
|
||||
prodName = cluster.defProducerAccounts[prod].name
|
||||
if prodName == "defproducera" or prodName == "defproducerb" or prodName == "defproducerc" or prodName == "defproduceru":
|
||||
Print("Register producer %s" % cluster.defProducerAccounts[prod].name)
|
||||
trans=node.regproducer(cluster.defProducerAccounts[prod], "http::/mysite.com", 0, waitForTransBlock=False, exitOnError=True)
|
||||
|
||||
# create accounts via eosio as otherwise a bid is needed
|
||||
for account in accounts:
|
||||
Print("Create new account %s via %s with private key: %s" % (account.name, cluster.eosioAccount.name, account.activePrivateKey))
|
||||
trans=nonProdNode.createInitializeAccount(account, cluster.eosioAccount, stakedDeposit=0, waitForTransBlock=True, stakeNet=10000, stakeCPU=10000, buyRAM=10000000, exitOnError=True)
|
||||
transferAmount="100000000.0000 {0}".format(CORE_SYMBOL)
|
||||
Print("Transfer funds %s from account %s to %s" % (transferAmount, cluster.eosioAccount.name, account.name))
|
||||
nonProdNode.transferFunds(cluster.eosioAccount, account, transferAmount, "test transfer", waitForTransBlock=True)
|
||||
trans=nonProdNode.delegatebw(account, 20000000.0000, 20000000.0000, waitForTransBlock=False, exitOnError=True)
|
||||
|
||||
# *** vote using accounts ***
|
||||
|
||||
cluster.waitOnClusterSync(blockAdvancing=3)
|
||||
|
||||
# vote a,b,c u
|
||||
voteProducers=[]
|
||||
voteProducers.append("defproducera")
|
||||
voteProducers.append("defproducerb")
|
||||
voteProducers.append("defproducerc")
|
||||
voteProducers.append("defproduceru")
|
||||
for account in accounts:
|
||||
Print("Account %s vote for producers=%s" % (account.name, voteProducers))
|
||||
trans=prodNode.vote(account, voteProducers, exitOnError=True, waitForTransBlock=False)
|
||||
|
||||
#verify nodes are in sync and advancing
|
||||
cluster.waitOnClusterSync(blockAdvancing=3)
|
||||
Print("Shutdown unneeded bios node")
|
||||
cluster.biosNode.kill(signal.SIGTERM)
|
||||
|
||||
# setup evm
|
||||
|
||||
contractDir=trustEvmContractRoot + "/evm_runtime"
|
||||
wasmFile="evm_runtime.wasm"
|
||||
abiFile="evm_runtime.abi"
|
||||
Utils.Print("Publish evm_runtime contract")
|
||||
trans = prodNode.publishContract(evmAcc, contractDir, wasmFile, abiFile, waitForTransBlock=True)
|
||||
transId=prodNode.getTransId(trans)
|
||||
blockNum = prodNode.getBlockNumByTransId(transId)
|
||||
block = prodNode.getBlock(blockNum)
|
||||
Utils.Print("Block Id: ", block["id"])
|
||||
Utils.Print("Block timestamp: ", block["timestamp"])
|
||||
|
||||
genesis_info = {
|
||||
"alloc": {},
|
||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||
"config": {
|
||||
"chainId": 15555,
|
||||
"homesteadBlock": 0,
|
||||
"eip150Block": 0,
|
||||
"eip155Block": 0,
|
||||
"byzantiumBlock": 0,
|
||||
"constantinopleBlock": 0,
|
||||
"petersburgBlock": 0,
|
||||
"istanbulBlock": 0,
|
||||
"noproof": {}
|
||||
},
|
||||
"difficulty": "0x01",
|
||||
"extraData": "TrustEVM",
|
||||
"gasLimit": "0x7ffffffffff",
|
||||
"mixHash": "0x"+block["id"],
|
||||
"nonce": hex(1000),
|
||||
"timestamp": hex(int(calendar.timegm(datetime.strptime(block["timestamp"].split(".")[0], '%Y-%m-%dT%H:%M:%S').timetuple())))
|
||||
}
|
||||
|
||||
# accounts: {
|
||||
# mnemonic: "test test test test test test test test test test test junk",
|
||||
# path: "m/44'/60'/0'/0",
|
||||
# initialIndex: 0,
|
||||
# count: 20,
|
||||
# passphrase: "",
|
||||
# }
|
||||
|
||||
addys = {
|
||||
"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266":"0x038318535b54105d4a7aae60c08fc45f9687181b4fdfc625bd1a753fa7397fed75,0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
|
||||
"0x70997970C51812dc3A010C7d01b50e0d17dc79C8":"0x02ba5734d8f7091719471e7f7ed6b9df170dc70cc661ca05e688601ad984f068b0,0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d",
|
||||
"0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC":"0x039d9031e97dd78ff8c15aa86939de9b1e791066a0224e331bc962a2099a7b1f04,0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a",
|
||||
"0x90F79bf6EB2c4f870365E785982E1f101E93b906":"0x0220b871f3ced029e14472ec4ebc3c0448164942b123aa6af91a3386c1c403e0eb,0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6",
|
||||
"0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65":"0x03bf6ee64a8d2fdc551ec8bb9ef862ef6b4bcb1805cdc520c3aa5866c0575fd3b5,0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a",
|
||||
"0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc":"0x0337b84de6947b243626cc8b977bb1f1632610614842468dfa8f35dcbbc55a515e,0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba",
|
||||
"0x976EA74026E726554dB657fA54763abd0C3a0aa9":"0x029a4ab212cb92775d227af4237c20b81f4221e9361d29007dfc16c79186b577cb,0x92db14e403b83dfe3df233f83dfa3a0d7096f21ca9b0d6d6b8d88b2b4ec1564e",
|
||||
"0x14dC79964da2C08b23698B3D3cc7Ca32193d9955":"0x0201f2bf1fa920e77a43c7aec2587d0b3814093420cc59a9b3ad66dd5734dda7be,0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356",
|
||||
"0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f":"0x03931e7fda8da226f799f791eefc9afebcd7ae2b1b19a03c5eaa8d72122d9fe74d,0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97",
|
||||
"0xa0Ee7A142d267C1f36714E4a8F75612F20a79720":"0x023255458e24278e31d5940f304b16300fdff3f6efd3e2a030b5818310ac67af45,0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6",
|
||||
"0xBcd4042DE499D14e55001CcbB24a551F3b954096":"0x030bb316cf4dbaeff8df7c6a8f3c55a11c72f7f2f8d79c274f27cdce2220f36371,0xf214f2b2cd398c806f84e317254e0f0b801d0643303237d97a22a48e01628897",
|
||||
"0x71bE63f3384f5fb98995898A86B02Fb2426c5788":"0x02e393c954d127d79d56b594a46df6b2e053f49446759eac612dbe12ade3095c67,0x701b615bbdfb9de65240bc28bd21bbc0d996645a3dd57e7b12bc2bdf6f192c82",
|
||||
"0xFABB0ac9d68B0B445fB7357272Ff202C5651694a":"0x02463e7db0f9c35ba7ae68a8098f1024019b90191281276eeef294acb3f1354b0a,0xa267530f49f8280200edf313ee7af6b827f2a8bce2897751d06a843f644967b1",
|
||||
"0x1CBd3b2770909D4e10f157cABC84C7264073C9Ec":"0x037805be0fc5186c4437306b531c8e981d3922e5fc81d72d527931b995445fb78e,0x47c99abed3324a2707c28affff1267e45918ec8c3f20b8aa892e8b065d2942dd",
|
||||
"0xdF3e18d64BC6A983f673Ab319CCaE4f1a57C7097":"0x03407a862c69cbc66dceea40079757697abcf931043f5a5f128a56ae6e51bdbce7,0xc526ee95bf44d8fc405a158bb884d9d1238d99f0612e9f33d006bb0789009aaa",
|
||||
"0xcd3B766CCDd6AE721141F452C550Ca635964ce71":"0x02d5ab34891bfe989bfa557f983cb5d031c4acc845ad990baaefb58ca1db0e7716,0x8166f546bab6da521a8369cab06c5d2b9e46670292d85c875ee9ec20e84ffb61",
|
||||
"0x2546BcD3c84621e976D8185a91A922aE77ECEc30":"0x0364c1c85d9aa8081a8ef94c35379fa7532942b2d8cbbd1e3ea71c0e3609b96cc0,0xea6c44ac03bff858b476bba40716402b03e41b8e97e276d1baec7c37d42484a0",
|
||||
"0xbDA5747bFD65F08deb54cb465eB87D40e51B197E":"0x02c216848622dfc38a2ad2a921f524103cf654a22b8679736ecedc4901453ea3f7,0x689af8efa8c651a91ad287602527f3af2fe9f6501a7ac4b061667b5a93e037fd",
|
||||
"0xdD2FD4581271e230360230F9337D5c0430Bf44C0":"0x02302a94bd084ff317493db7c2fe07e0935c0f6d3e6772d6af3c58e92abebfb402,0xde9be858da4a475276426320d5e9262ecfc3ba460bfac56360bfa6c4c28b4ee0",
|
||||
"0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199":"0x027bf824b28c4bf11ce553fa746a18754949ab4959e2ea73465778d14179211f8c,0xdf57089febbacf7ba0bc227dafbffa9fc08a93fdc68e1e42411a14efcf23656e"
|
||||
}
|
||||
|
||||
for k in addys:
|
||||
trans = prodNode.pushMessage(evmAcc.name, "setbal", '{"addy":"' + k[2:].lower() + '", "bal":"0000000000000000000000000000000010000000000000000000000000000000"}', '-p evmevmevmevm')
|
||||
genesis_info["alloc"][k.lower()] = {"balance":"0x10000000000000000000000000000000"}
|
||||
prodNode.waitForTransBlockIfNeeded(trans[1], True)
|
||||
|
||||
if gensisJson[0] != '/': gensisJson = os.path.realpath(gensisJson)
|
||||
f=open(gensisJson,"w")
|
||||
f.write(json.dumps(genesis_info))
|
||||
f.close()
|
||||
Utils.Print("#####################################################")
|
||||
Utils.Print("Generated EVM json genesis file in: %s" % gensisJson)
|
||||
Utils.Print("")
|
||||
Utils.Print("You can now run:")
|
||||
Utils.Print(" trustevm-node --plugin=blockchain_plugin --ship-endpoint=127.0.0.1:8999 --genesis-json=%s --chain-data=/tmp --verbosity=4" % gensisJson)
|
||||
Utils.Print(" trustevm-rpc --trust-evm-node=127.0.0.1:8080 --http-port=0.0.0.0:8881 --chaindata=/tmp --api-spec=eth,debug,net,trace")
|
||||
Utils.Print("")
|
||||
Utils.Print("Web3 endpoint:")
|
||||
Utils.Print(" http://localhost:5000")
|
||||
|
||||
app = Flask(__name__)
|
||||
CORS(app)
|
||||
|
||||
@app.route("/fork", methods=["POST"])
|
||||
def fork():
|
||||
Print("Sending command to kill bridge node to separate the 2 producer groups.")
|
||||
forkAtProducer="defproducera"
|
||||
prodNode1Prod="defproduceru"
|
||||
preKillBlockNum=nonProdNode.getBlockNum()
|
||||
preKillBlockProducer=nonProdNode.getBlockProducerByNum(preKillBlockNum)
|
||||
nonProdNode.killNodeOnProducer(producer=forkAtProducer, whereInSequence=1)
|
||||
Print("Current block producer %s fork will be at producer %s" % (preKillBlockProducer, forkAtProducer))
|
||||
prodNode0.waitForProducer(forkAtProducer)
|
||||
prodNode1.waitForProducer(prodNode1Prod)
|
||||
if nonProdNode.verifyAlive(): # if on defproducera, need to wait again
|
||||
prodNode0.waitForProducer(forkAtProducer)
|
||||
prodNode1.waitForProducer(prodNode1Prod)
|
||||
|
||||
if nonProdNode.verifyAlive():
|
||||
Print("Bridge did not shutdown")
|
||||
return "Bridge did not shutdown"
|
||||
|
||||
Print("Fork started")
|
||||
return "Fork started"
|
||||
|
||||
@app.route("/restore", methods=["POST"])
|
||||
def restore():
|
||||
Print("Relaunching the non-producing bridge node to connect the producing nodes again")
|
||||
|
||||
if nonProdNode.verifyAlive():
|
||||
return "bridge is already running"
|
||||
|
||||
if not nonProdNode.relaunch():
|
||||
Utils.errorExit("Failure - (non-production) node %d should have restarted" % (nonProdNode.nodeNum))
|
||||
|
||||
return "restored fork should resolve"
|
||||
|
||||
@app.route("/", methods=["POST"])
|
||||
def default():
|
||||
def forward_request(req):
|
||||
if req['method'] == "eth_sendRawTransaction":
|
||||
actData = {"ram_payer":"evmevmevmevm", "rlptx":req['params'][0][2:]}
|
||||
prodNode.pushMessage(evmAcc.name, "pushtx", json.dumps(actData), '-p evmevmevmevm')
|
||||
return {
|
||||
"id": req['id'],
|
||||
"jsonrpc": "2.0",
|
||||
"result": '0x'+keccak(unhexlify(req['params'][0][2:])).hex()
|
||||
}
|
||||
return requests.post(readEndpoint, json.dumps(req), headers={"Content-Type":"application/json"}).json()
|
||||
|
||||
request_data = request.get_json()
|
||||
if type(request_data) == dict:
|
||||
return jsonify(forward_request(request_data))
|
||||
|
||||
res = []
|
||||
for r in request_data:
|
||||
res.append(forward_request(r))
|
||||
|
||||
return jsonify(res)
|
||||
|
||||
app.run(host='0.0.0.0', port=5000)
|
||||
|
||||
finally:
|
||||
TestHelper.shutdown(cluster, walletMgr, testSuccessful=testSuccessful, killEosInstances=killEosInstances, killWallet=killEosInstances, keepLogs=keepLogs, cleanRun=killAll, dumpErrorDetails=dumpErrorDetails)
|
||||
|
||||
exitCode = 0 if testSuccessful else 1
|
||||
exit(exitCode)
|
||||
Executable
+240
@@ -0,0 +1,240 @@
|
||||
#!/usr/bin/env python3
|
||||
import random
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
import calendar
|
||||
from datetime import datetime
|
||||
|
||||
from flask import Flask, request, jsonify
|
||||
from flask_cors import CORS
|
||||
from eth_hash.auto import keccak
|
||||
import requests
|
||||
import json
|
||||
|
||||
from binascii import unhexlify
|
||||
from TestHarness import Cluster, TestHelper, Utils, WalletMgr
|
||||
from TestHarness.TestHelper import AppArgs
|
||||
from core_symbol import CORE_SYMBOL
|
||||
|
||||
|
||||
###############################################################
|
||||
# nodeos_trust_evm_server
|
||||
#
|
||||
# Set up a TrustEVM env
|
||||
#
|
||||
# --trust-evm-contract-root should point to root of TrustEVM contract build dir
|
||||
# --genesis-json file to save generated EVM genesis json
|
||||
# --read-endpoint trustnode-rpc endpoint (read endpoint)
|
||||
#
|
||||
# Example:
|
||||
# ./tests/nodeos_trust_evm_no_txs.py --trust-evm-contract-root ~/ext/TrustEVM/contract/build --leave-running
|
||||
#
|
||||
# Launches wallet at port: 9899
|
||||
# Example: bin/cleos --wallet-url http://127.0.0.1:9899 ...
|
||||
#
|
||||
# Dependencies:
|
||||
# pip install eth-hash requests flask flask-cors
|
||||
###############################################################
|
||||
|
||||
Print=Utils.Print
|
||||
errorExit=Utils.errorExit
|
||||
|
||||
appArgs=AppArgs()
|
||||
appArgs.add(flag="--trust-evm-contract-root", type=str, help="TrustEVM contract build dir", default=None)
|
||||
appArgs.add(flag="--genesis-json", type=str, help="File to save generated genesis json", default="trust-evm-genesis.json")
|
||||
appArgs.add(flag="--read-endpoint", type=str, help="EVM read enpoint (trustevm-rpc)", default="http://localhost:8881")
|
||||
|
||||
args=TestHelper.parse_args({"--keep-logs","--dump-error-details","-v","--leave-running","--clean-run" }, applicationSpecificArgs=appArgs)
|
||||
debug=args.v
|
||||
killEosInstances= not args.leave_running
|
||||
dumpErrorDetails=args.dump_error_details
|
||||
keepLogs=args.keep_logs
|
||||
killAll=args.clean_run
|
||||
trustEvmContractRoot=args.trust_evm_contract_root
|
||||
gensisJson=args.genesis_json
|
||||
readEndpoint=args.read_endpoint
|
||||
|
||||
assert trustEvmContractRoot is not None, "--trust-evm-contract-root is required"
|
||||
|
||||
seed=1
|
||||
Utils.Debug=debug
|
||||
testSuccessful=False
|
||||
|
||||
random.seed(seed) # Use a fixed seed for repeatability.
|
||||
cluster=Cluster(walletd=True)
|
||||
walletMgr=WalletMgr(True)
|
||||
|
||||
pnodes=1
|
||||
total_nodes=pnodes + 2
|
||||
|
||||
try:
|
||||
TestHelper.printSystemInfo("BEGIN")
|
||||
|
||||
cluster.setWalletMgr(walletMgr)
|
||||
|
||||
cluster.killall(allInstances=killAll)
|
||||
cluster.cleanup()
|
||||
walletMgr.killall(allInstances=killAll)
|
||||
walletMgr.cleanup()
|
||||
|
||||
specificExtraNodeosArgs={}
|
||||
shipNodeNum = total_nodes - 1
|
||||
specificExtraNodeosArgs[shipNodeNum]="--plugin eosio::state_history_plugin --state-history-endpoint 127.0.0.1:8999 --trace-history --chain-state-history --disable-replay-opts "
|
||||
|
||||
extraNodeosArgs=""
|
||||
if not Utils.Debug: # added by launch when debug set
|
||||
extraNodeosArgs="--contracts-console"
|
||||
|
||||
Print("Stand up cluster")
|
||||
if cluster.launch(pnodes=pnodes, totalNodes=total_nodes, extraNodeosArgs=extraNodeosArgs, 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")
|
||||
Print ("Cluster stabilized")
|
||||
|
||||
prodNode = cluster.getNode(0)
|
||||
nonProdNode = cluster.getNode(1)
|
||||
|
||||
accounts=cluster.createAccountKeys(1)
|
||||
if accounts is None:
|
||||
Utils.errorExit("FAILURE - create keys")
|
||||
|
||||
evmAcc = accounts[0]
|
||||
evmAcc.name = "evmevmevmevm"
|
||||
evmAcc.activePrivateKey="5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"
|
||||
evmAcc.activePublicKey="EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV"
|
||||
|
||||
testWalletName="test"
|
||||
|
||||
Print("Creating wallet \"%s\"." % (testWalletName))
|
||||
testWallet=walletMgr.create(testWalletName, [cluster.eosioAccount,accounts[0]])
|
||||
|
||||
# create accounts via eosio as otherwise a bid is needed
|
||||
for account in accounts:
|
||||
Print("Create new account %s via %s with private key: %s" % (account.name, cluster.eosioAccount.name, account.activePrivateKey))
|
||||
trans=nonProdNode.createInitializeAccount(account, cluster.eosioAccount, stakedDeposit=0, waitForTransBlock=True, stakeNet=10000, stakeCPU=10000, buyRAM=10000000, exitOnError=True)
|
||||
transferAmount="100000000.0000 {0}".format(CORE_SYMBOL)
|
||||
Print("Transfer funds %s from account %s to %s" % (transferAmount, cluster.eosioAccount.name, account.name))
|
||||
nonProdNode.transferFunds(cluster.eosioAccount, account, transferAmount, "test transfer", waitForTransBlock=True)
|
||||
trans=nonProdNode.delegatebw(account, 20000000.0000, 20000000.0000, waitForTransBlock=True, exitOnError=True)
|
||||
|
||||
contractDir=trustEvmContractRoot + "/evm_runtime"
|
||||
wasmFile="evm_runtime.wasm"
|
||||
abiFile="evm_runtime.abi"
|
||||
Utils.Print("Publish evm_runtime contract")
|
||||
trans = prodNode.publishContract(evmAcc, contractDir, wasmFile, abiFile, waitForTransBlock=True)
|
||||
transId=prodNode.getTransId(trans)
|
||||
blockNum = prodNode.getBlockNumByTransId(transId)
|
||||
block = prodNode.getBlock(blockNum)
|
||||
Utils.Print("Block Id: ", block["id"])
|
||||
Utils.Print("Block timestamp: ", block["timestamp"])
|
||||
|
||||
genesis_info = {
|
||||
"alloc": {},
|
||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||
"config": {
|
||||
"chainId": 15555,
|
||||
"homesteadBlock": 0,
|
||||
"eip150Block": 0,
|
||||
"eip155Block": 0,
|
||||
"byzantiumBlock": 0,
|
||||
"constantinopleBlock": 0,
|
||||
"petersburgBlock": 0,
|
||||
"istanbulBlock": 0,
|
||||
"noproof": {}
|
||||
},
|
||||
"difficulty": "0x01",
|
||||
"extraData": "TrustEVM",
|
||||
"gasLimit": "0x7ffffffffff",
|
||||
"mixHash": "0x"+block["id"],
|
||||
"nonce": hex(1000),
|
||||
"timestamp": hex(int(calendar.timegm(datetime.strptime(block["timestamp"].split(".")[0], '%Y-%m-%dT%H:%M:%S').timetuple())))
|
||||
}
|
||||
|
||||
# accounts: {
|
||||
# mnemonic: "test test test test test test test test test test test junk",
|
||||
# path: "m/44'/60'/0'/0",
|
||||
# initialIndex: 0,
|
||||
# count: 20,
|
||||
# passphrase: "",
|
||||
# }
|
||||
|
||||
addys = {
|
||||
"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266":"0x038318535b54105d4a7aae60c08fc45f9687181b4fdfc625bd1a753fa7397fed75,0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
|
||||
"0x70997970C51812dc3A010C7d01b50e0d17dc79C8":"0x02ba5734d8f7091719471e7f7ed6b9df170dc70cc661ca05e688601ad984f068b0,0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d",
|
||||
"0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC":"0x039d9031e97dd78ff8c15aa86939de9b1e791066a0224e331bc962a2099a7b1f04,0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a",
|
||||
"0x90F79bf6EB2c4f870365E785982E1f101E93b906":"0x0220b871f3ced029e14472ec4ebc3c0448164942b123aa6af91a3386c1c403e0eb,0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6",
|
||||
"0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65":"0x03bf6ee64a8d2fdc551ec8bb9ef862ef6b4bcb1805cdc520c3aa5866c0575fd3b5,0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a",
|
||||
"0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc":"0x0337b84de6947b243626cc8b977bb1f1632610614842468dfa8f35dcbbc55a515e,0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba",
|
||||
"0x976EA74026E726554dB657fA54763abd0C3a0aa9":"0x029a4ab212cb92775d227af4237c20b81f4221e9361d29007dfc16c79186b577cb,0x92db14e403b83dfe3df233f83dfa3a0d7096f21ca9b0d6d6b8d88b2b4ec1564e",
|
||||
"0x14dC79964da2C08b23698B3D3cc7Ca32193d9955":"0x0201f2bf1fa920e77a43c7aec2587d0b3814093420cc59a9b3ad66dd5734dda7be,0x4bbbf85ce3377467afe5d46f804f221813b2bb87f24d81f60f1fcdbf7cbf4356",
|
||||
"0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f":"0x03931e7fda8da226f799f791eefc9afebcd7ae2b1b19a03c5eaa8d72122d9fe74d,0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97",
|
||||
"0xa0Ee7A142d267C1f36714E4a8F75612F20a79720":"0x023255458e24278e31d5940f304b16300fdff3f6efd3e2a030b5818310ac67af45,0x2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6",
|
||||
"0xBcd4042DE499D14e55001CcbB24a551F3b954096":"0x030bb316cf4dbaeff8df7c6a8f3c55a11c72f7f2f8d79c274f27cdce2220f36371,0xf214f2b2cd398c806f84e317254e0f0b801d0643303237d97a22a48e01628897",
|
||||
"0x71bE63f3384f5fb98995898A86B02Fb2426c5788":"0x02e393c954d127d79d56b594a46df6b2e053f49446759eac612dbe12ade3095c67,0x701b615bbdfb9de65240bc28bd21bbc0d996645a3dd57e7b12bc2bdf6f192c82",
|
||||
"0xFABB0ac9d68B0B445fB7357272Ff202C5651694a":"0x02463e7db0f9c35ba7ae68a8098f1024019b90191281276eeef294acb3f1354b0a,0xa267530f49f8280200edf313ee7af6b827f2a8bce2897751d06a843f644967b1",
|
||||
"0x1CBd3b2770909D4e10f157cABC84C7264073C9Ec":"0x037805be0fc5186c4437306b531c8e981d3922e5fc81d72d527931b995445fb78e,0x47c99abed3324a2707c28affff1267e45918ec8c3f20b8aa892e8b065d2942dd",
|
||||
"0xdF3e18d64BC6A983f673Ab319CCaE4f1a57C7097":"0x03407a862c69cbc66dceea40079757697abcf931043f5a5f128a56ae6e51bdbce7,0xc526ee95bf44d8fc405a158bb884d9d1238d99f0612e9f33d006bb0789009aaa",
|
||||
"0xcd3B766CCDd6AE721141F452C550Ca635964ce71":"0x02d5ab34891bfe989bfa557f983cb5d031c4acc845ad990baaefb58ca1db0e7716,0x8166f546bab6da521a8369cab06c5d2b9e46670292d85c875ee9ec20e84ffb61",
|
||||
"0x2546BcD3c84621e976D8185a91A922aE77ECEc30":"0x0364c1c85d9aa8081a8ef94c35379fa7532942b2d8cbbd1e3ea71c0e3609b96cc0,0xea6c44ac03bff858b476bba40716402b03e41b8e97e276d1baec7c37d42484a0",
|
||||
"0xbDA5747bFD65F08deb54cb465eB87D40e51B197E":"0x02c216848622dfc38a2ad2a921f524103cf654a22b8679736ecedc4901453ea3f7,0x689af8efa8c651a91ad287602527f3af2fe9f6501a7ac4b061667b5a93e037fd",
|
||||
"0xdD2FD4581271e230360230F9337D5c0430Bf44C0":"0x02302a94bd084ff317493db7c2fe07e0935c0f6d3e6772d6af3c58e92abebfb402,0xde9be858da4a475276426320d5e9262ecfc3ba460bfac56360bfa6c4c28b4ee0",
|
||||
"0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199":"0x027bf824b28c4bf11ce553fa746a18754949ab4959e2ea73465778d14179211f8c,0xdf57089febbacf7ba0bc227dafbffa9fc08a93fdc68e1e42411a14efcf23656e"
|
||||
}
|
||||
|
||||
for k in addys:
|
||||
trans = prodNode.pushMessage(evmAcc.name, "setbal", '{"addy":"' + k[2:].lower() + '", "bal":"0000000000000000000000000000000010000000000000000000000000000000"}', '-p evmevmevmevm')
|
||||
genesis_info["alloc"][k.lower()] = {"balance":"0x10000000000000000000000000000000"}
|
||||
prodNode.waitForTransBlockIfNeeded(trans[1], True)
|
||||
|
||||
if gensisJson[0] != '/': gensisJson = os.path.realpath(gensisJson)
|
||||
f=open(gensisJson,"w")
|
||||
f.write(json.dumps(genesis_info))
|
||||
f.close()
|
||||
Utils.Print("#####################################################")
|
||||
Utils.Print("Generated EVM json genesis file in: %s" % gensisJson)
|
||||
Utils.Print("")
|
||||
Utils.Print("You can now run:")
|
||||
Utils.Print(" trustevm-node --plugin=blockchain_plugin --ship-endpoint=127.0.0.1:8999 --genesis-json=%s --chain-data=/tmp --verbosity=4" % gensisJson)
|
||||
Utils.Print(" trustevm-rpc --trust-evm-node=127.0.0.1:8080 --http-port=0.0.0.0:8881 --chaindata=/tmp --api-spec=eth,debug,net,trace")
|
||||
Utils.Print("")
|
||||
Utils.Print("Web3 endpoint:")
|
||||
Utils.Print(" http://localhost:5000")
|
||||
|
||||
app = Flask(__name__)
|
||||
CORS(app)
|
||||
|
||||
@app.route("/", methods=["POST"])
|
||||
def default():
|
||||
def forward_request(req):
|
||||
if req['method'] == "eth_sendRawTransaction":
|
||||
actData = {"ram_payer":"evmevmevmevm", "rlptx":req['params'][0][2:]}
|
||||
prodNode.pushMessage(evmAcc.name, "pushtx", json.dumps(actData), '-p evmevmevmevm')
|
||||
return {
|
||||
"id": req['id'],
|
||||
"jsonrpc": "2.0",
|
||||
"result": '0x'+keccak(unhexlify(req['params'][0][2:])).hex()
|
||||
}
|
||||
return requests.post(readEndpoint, json.dumps(req), headers={"Content-Type":"application/json"}).json()
|
||||
|
||||
request_data = request.get_json()
|
||||
if type(request_data) == dict:
|
||||
return jsonify(forward_request(request_data))
|
||||
|
||||
res = []
|
||||
for r in request_data:
|
||||
res.append(forward_request(r))
|
||||
|
||||
return jsonify(res)
|
||||
|
||||
app.run(host='0.0.0.0', port=5000)
|
||||
|
||||
finally:
|
||||
TestHelper.shutdown(cluster, walletMgr, testSuccessful=testSuccessful, killEosInstances=killEosInstances, killWallet=killEosInstances, keepLogs=keepLogs, cleanRun=killAll, dumpErrorDetails=dumpErrorDetails)
|
||||
|
||||
exitCode = 0 if testSuccessful else 1
|
||||
exit(exitCode)
|
||||
@@ -0,0 +1,9 @@
|
||||
# Sample Hardhat Project
|
||||
|
||||
This project demonstrates a basic Hardhat use case. It comes with a sample contract, a test for that contract, and a script that deploys that contract.
|
||||
|
||||
Try running some of the following tasks:
|
||||
|
||||
```shell
|
||||
npx hardhat run scripts/deploy.js
|
||||
```
|
||||
@@ -0,0 +1,34 @@
|
||||
// SPDX-License-Identifier: UNLICENSED
|
||||
pragma solidity ^0.8.9;
|
||||
|
||||
// Uncomment this line to use console.log
|
||||
// import "hardhat/console.sol";
|
||||
|
||||
contract Lock {
|
||||
uint public unlockTime;
|
||||
address payable public owner;
|
||||
|
||||
event Withdrawal(uint amount, uint when);
|
||||
|
||||
constructor(uint _unlockTime) payable {
|
||||
require(
|
||||
block.timestamp < _unlockTime,
|
||||
"Unlock time should be in the future"
|
||||
);
|
||||
|
||||
unlockTime = _unlockTime;
|
||||
owner = payable(msg.sender);
|
||||
}
|
||||
|
||||
function withdraw() public {
|
||||
// Uncomment this line, and the import of "hardhat/console.sol", to print a log in your terminal
|
||||
// console.log("Unlock time is %o and block timestamp is %o", unlockTime, block.timestamp);
|
||||
|
||||
require(block.timestamp >= unlockTime, "You can't withdraw yet");
|
||||
require(msg.sender == owner, "You aren't the owner");
|
||||
|
||||
emit Withdrawal(address(this).balance, block.timestamp);
|
||||
|
||||
owner.transfer(address(this).balance);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
|
||||
/**
|
||||
* @title Storage
|
||||
* @dev Store & retrieve value in a variable
|
||||
* @custom:dev-run-script ./scripts/deploy_with_ethers.ts
|
||||
*/
|
||||
contract Storage {
|
||||
|
||||
uint256 number;
|
||||
|
||||
/**
|
||||
* @dev Store value in variable
|
||||
* @param num value to store
|
||||
*/
|
||||
function store(uint256 num) public {
|
||||
number = num;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dev Return value
|
||||
* @return value of 'number'
|
||||
*/
|
||||
function retrieve() public view returns (uint256){
|
||||
return number;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
|
||||
pragma solidity >=0.7.0 <0.9.0;
|
||||
|
||||
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||||
|
||||
contract Token is ERC20 {
|
||||
|
||||
constructor () ERC20("Yuniper", "YUN") {
|
||||
_mint(msg.sender, 1000000 * (10 ** uint256(decimals())));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
require("@nomicfoundation/hardhat-toolbox");
|
||||
require("@nomiclabs/hardhat-web3");
|
||||
|
||||
// task action function receives the Hardhat Runtime Environment as second argument
|
||||
task("accounts", "Prints accounts", async (_, { web3 }) => {
|
||||
console.log(await web3.eth.getAccounts());
|
||||
});
|
||||
|
||||
task("blockNumber", "Prints the current block number", async (_, { web3 }) => {
|
||||
console.log(await web3.eth.getBlockNumber());
|
||||
});
|
||||
|
||||
task("block", "Prints block")
|
||||
.addParam("blocknum", "The block number")
|
||||
.setAction(async (taskArgs) => {
|
||||
const block = await ethers.provider.getBlockWithTransactions(taskArgs.blocknum);
|
||||
console.log(block);
|
||||
});
|
||||
|
||||
task("balance", "Prints an account's balance")
|
||||
.addParam("account", "The account's address")
|
||||
.setAction(async (taskArgs) => {
|
||||
const balance = await ethers.provider.getBalance(taskArgs.account);
|
||||
console.log(ethers.utils.formatEther(balance), "ETH");
|
||||
});
|
||||
|
||||
task("nonce", "Prints an account's nonce")
|
||||
.addParam("account", "The account's address")
|
||||
.setAction(async (taskArgs) => {
|
||||
const nonce = await ethers.provider.getTransactionCount(taskArgs.account);
|
||||
console.log(nonce);
|
||||
});
|
||||
|
||||
task("transfer", "Send ERC20 tokens")
|
||||
.addParam("from", "from account")
|
||||
.addParam("to", "to account")
|
||||
.addParam("contract", "ERC20 token address")
|
||||
.addParam("amount", "amount to trasfer")
|
||||
.setAction(async (taskArgs) => {
|
||||
const Token = await ethers.getContractFactory('Token')
|
||||
const token = Token.attach(taskArgs.contract)
|
||||
const res = await token.connect(await ethers.getSigner(taskArgs.from)).transfer(taskArgs.to, ethers.utils.parseEther(taskArgs.amount.toString()),{gasLimit:50000});
|
||||
console.log(res);
|
||||
});
|
||||
|
||||
task("send-loop", "Send ERC20 token in a loop")
|
||||
.addParam("contract", "Token contract address")
|
||||
.setAction(async (taskArgs) => {
|
||||
const accounts = await web3.eth.getAccounts();
|
||||
const Token = await ethers.getContractFactory('Token')
|
||||
const token = Token.attach(taskArgs.contract)
|
||||
while(true) {
|
||||
const destination = accounts[parseInt(Math.random()*accounts.length)];
|
||||
const amount = ethers.utils.parseEther((1+Math.random()*3).toString());
|
||||
const res = await token.transfer(destination, amount)
|
||||
console.log(res);
|
||||
console.log("############################################ SENT #######");
|
||||
}
|
||||
});
|
||||
|
||||
task("storage-loop", "Store incremental values to the storage contract")
|
||||
.addParam("contract", "Token contract address")
|
||||
.setAction(async (taskArgs) => {
|
||||
const Storage = await ethers.getContractFactory('Storage')
|
||||
const storage = Storage.attach(taskArgs.contract)
|
||||
let value = 1;
|
||||
while(true) {
|
||||
const res = await storage.store(value, {gasLimit:50000})
|
||||
console.log("############################################ "+ value +" STORED #######");
|
||||
++value;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/** @type import('hardhat/config').HardhatUserConfig */
|
||||
module.exports = {
|
||||
defaultNetwork: "ltrust",
|
||||
networks: {
|
||||
ltrust: {
|
||||
url: "http://localhost:5000",
|
||||
accounts: {
|
||||
mnemonic: "test test test test test test test test test test test junk",
|
||||
path: "m/44'/60'/0'/0",
|
||||
initialIndex: 0,
|
||||
count: 20,
|
||||
passphrase: "",
|
||||
},
|
||||
}
|
||||
},
|
||||
solidity: "0.8.17",
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"devDependencies": {
|
||||
"@ethersproject/abi": "^5.4.7",
|
||||
"@ethersproject/providers": "^5.4.7",
|
||||
"@nomicfoundation/hardhat-chai-matchers": "^1.0.0",
|
||||
"@nomicfoundation/hardhat-network-helpers": "^1.0.0",
|
||||
"@nomicfoundation/hardhat-toolbox": "^2.0.0",
|
||||
"@nomiclabs/hardhat-ethers": "^2.0.0",
|
||||
"@nomiclabs/hardhat-etherscan": "^3.0.0",
|
||||
"@nomiclabs/hardhat-web3": "^2.0.0",
|
||||
"@openzeppelin/contracts": "^4.8.0",
|
||||
"@typechain/ethers-v5": "^10.1.0",
|
||||
"@typechain/hardhat": "^6.1.2",
|
||||
"chai": "^4.2.0",
|
||||
"ethers": "^5.4.7",
|
||||
"hardhat": "^2.12.2",
|
||||
"hardhat-gas-reporter": "^1.0.8",
|
||||
"solidity-coverage": "^0.8.0",
|
||||
"typechain": "^8.1.0",
|
||||
"web3": "^1.8.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// We require the Hardhat Runtime Environment explicitly here. This is optional
|
||||
// but useful for running the script in a standalone fashion through `node <script>`.
|
||||
//
|
||||
// You can also run a script with `npx hardhat run <script>`. If you do that, Hardhat
|
||||
// will compile your contracts, add the Hardhat Runtime Environment's members to the
|
||||
// global scope, and execute the script.
|
||||
const hre = require("hardhat");
|
||||
|
||||
async function main() {
|
||||
const currentTimestampInSeconds = Math.round(Date.now() / 1000);
|
||||
const ONE_YEAR_IN_SECS = 365 * 24 * 60 * 60;
|
||||
const unlockTime = currentTimestampInSeconds + ONE_YEAR_IN_SECS;
|
||||
|
||||
const lockedAmount = hre.ethers.utils.parseEther("1");
|
||||
|
||||
const Lock = await hre.ethers.getContractFactory("Lock");
|
||||
const lock = await Lock.deploy(unlockTime, {value: lockedAmount, gasLimit:1500000});
|
||||
|
||||
await lock.deployed();
|
||||
|
||||
console.log(
|
||||
`Lock with 1 ETH and unlock timestamp ${unlockTime} deployed to ${lock.address}`
|
||||
);
|
||||
|
||||
const Storage = await hre.ethers.getContractFactory("Storage");
|
||||
const storage = await Storage.deploy({gasLimit:1500000});
|
||||
|
||||
await storage.deployed();
|
||||
|
||||
console.log(
|
||||
`Storage deployed to ${storage.address}`
|
||||
);
|
||||
|
||||
const Token = await hre.ethers.getContractFactory("Token");
|
||||
const token = await Token.deploy({gasLimit:1500000});
|
||||
|
||||
await token.deployed();
|
||||
|
||||
console.log(
|
||||
`Token deployed to ${token.address}`
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
// We recommend this pattern to be able to use async/await everywhere
|
||||
// and properly handle errors.
|
||||
main().catch((error) => {
|
||||
console.error(error);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
Executable
+209
@@ -0,0 +1,209 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import random
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
|
||||
import rlp
|
||||
from ethereum import transactions
|
||||
from binascii import unhexlify
|
||||
from TestHarness import Cluster, TestHelper, Utils, WalletMgr
|
||||
from TestHarness.TestHelper import AppArgs
|
||||
from core_symbol import CORE_SYMBOL
|
||||
|
||||
|
||||
###############################################################
|
||||
# nodeos_trust_evm_test
|
||||
#
|
||||
# Set up a TrustEVM env and run simple tests.
|
||||
#
|
||||
# Need to install:
|
||||
# rlp - pip install rlp
|
||||
# ethereum - pip install ethereum
|
||||
# venv/lib/python3.6/site-packages/ethereum/transactions.py
|
||||
# /home/account/.local/lib/python3.8/site-packages/ethereum/transactions.py
|
||||
# Line 135
|
||||
#
|
||||
# 135c135
|
||||
# < self.v += 8 + network_id * 2
|
||||
# ---
|
||||
# > v += 8 + network_id * 2
|
||||
#
|
||||
# --turst-evm-root should point to the root of TrustEVM build dir
|
||||
# --trust-evm-contract-root should point to root of TrustEVM contract build dir
|
||||
#
|
||||
# Example:
|
||||
# ./tests/nodeos_trust_evm_test.py --trust-evm-contract-root ~/ext/TrustEVM/contract/build --leave-running
|
||||
#
|
||||
# Launches wallet at port: 9899
|
||||
# Example: bin/cleos --wallet-url http://127.0.0.1:9899 ...
|
||||
#
|
||||
###############################################################
|
||||
|
||||
Print=Utils.Print
|
||||
errorExit=Utils.errorExit
|
||||
|
||||
appArgs=AppArgs()
|
||||
extraArgs = appArgs.add(flag="--trust-evm-contract-root", type=str, help="TrustEVM contract build dir", default=None)
|
||||
|
||||
args=TestHelper.parse_args({"--keep-logs","--dump-error-details","-v","--leave-running","--clean-run" }, applicationSpecificArgs=appArgs)
|
||||
debug=args.v
|
||||
killEosInstances= not args.leave_running
|
||||
dumpErrorDetails=args.dump_error_details
|
||||
keepLogs=args.keep_logs
|
||||
killAll=args.clean_run
|
||||
trustEvmContractRoot=args.trust_evm_contract_root
|
||||
|
||||
assert trustEvmContractRoot is not None, "--trust-evm-contract-root is required"
|
||||
|
||||
seed=1
|
||||
Utils.Debug=debug
|
||||
testSuccessful=False
|
||||
|
||||
random.seed(seed) # Use a fixed seed for repeatability.
|
||||
cluster=Cluster(walletd=True)
|
||||
walletMgr=WalletMgr(True)
|
||||
|
||||
pnodes=1
|
||||
total_nodes=pnodes + 2
|
||||
|
||||
def generate_evm_transactions(nonce):
|
||||
while True:
|
||||
Utils.Print("Execute ETH contract")
|
||||
nonce += 1
|
||||
toAdd = "2787b98fc4e731d0456b3941f0b3fe2e01430000"
|
||||
amount = 0
|
||||
unsignedTrx = transactions.Transaction(
|
||||
nonce,
|
||||
150000000000, #150 GWei
|
||||
100000, #100k Gas
|
||||
toAdd,
|
||||
amount,
|
||||
unhexlify("6057361d000000000000000000000000000000000000000000000000000000000000007b")
|
||||
)
|
||||
rlptx = rlp.encode(unsignedTrx.sign(evmSendKey, evmChainId), transactions.Transaction)
|
||||
actData = {"ram_payer":"evmevmevmevm", "rlptx":rlptx.hex()}
|
||||
retValue = prodNode.pushMessage(evmAcc.name, "pushtx", json.dumps(actData), '-p evmevmevmevm')
|
||||
assert retValue[0], "pushtx to ETH contract failed."
|
||||
Utils.Print("\tReturn value:", retValue[1]["processed"]["action_traces"][0]["return_value_data"])
|
||||
Utils.Print("\tBlock#", retValue[1]["processed"]["block_num"])
|
||||
row0=prodNode.getTableRow(evmAcc.name, 3, "storage", 0)
|
||||
Utils.Print("\tTable row:", row0)
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
try:
|
||||
TestHelper.printSystemInfo("BEGIN")
|
||||
|
||||
cluster.setWalletMgr(walletMgr)
|
||||
|
||||
cluster.killall(allInstances=killAll)
|
||||
cluster.cleanup()
|
||||
walletMgr.killall(allInstances=killAll)
|
||||
walletMgr.cleanup()
|
||||
|
||||
specificExtraNodeosArgs={}
|
||||
shipNodeNum = total_nodes - 1
|
||||
specificExtraNodeosArgs[shipNodeNum]="--plugin eosio::state_history_plugin --state-history-endpoint 127.0.0.1:8999 --trace-history --chain-state-history --disable-replay-opts "
|
||||
|
||||
extraNodeosArgs="--contracts-console"
|
||||
|
||||
Print("Stand up cluster")
|
||||
if cluster.launch(pnodes=pnodes, totalNodes=total_nodes, extraNodeosArgs=extraNodeosArgs, 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")
|
||||
Print ("Cluster stabilized")
|
||||
|
||||
prodNode = cluster.getNode(0)
|
||||
nonProdNode = cluster.getNode(1)
|
||||
|
||||
accounts=cluster.createAccountKeys(1)
|
||||
if accounts is None:
|
||||
Utils.errorExit("FAILURE - create keys")
|
||||
|
||||
evmAcc = accounts[0]
|
||||
evmAcc.name = "evmevmevmevm"
|
||||
|
||||
testWalletName="test"
|
||||
|
||||
Print("Creating wallet \"%s\"." % (testWalletName))
|
||||
testWallet=walletMgr.create(testWalletName, [cluster.eosioAccount,accounts[0]])
|
||||
|
||||
# create accounts via eosio as otherwise a bid is needed
|
||||
for account in accounts:
|
||||
Print("Create new account %s via %s" % (account.name, cluster.eosioAccount.name))
|
||||
trans=nonProdNode.createInitializeAccount(account, cluster.eosioAccount, stakedDeposit=0, waitForTransBlock=True, stakeNet=10000, stakeCPU=10000, buyRAM=10000000, exitOnError=True)
|
||||
transferAmount="100000000.0000 {0}".format(CORE_SYMBOL)
|
||||
Print("Transfer funds %s from account %s to %s" % (transferAmount, cluster.eosioAccount.name, account.name))
|
||||
nonProdNode.transferFunds(cluster.eosioAccount, account, transferAmount, "test transfer", waitForTransBlock=True)
|
||||
trans=nonProdNode.delegatebw(account, 20000000.0000, 20000000.0000, waitForTransBlock=True, exitOnError=True)
|
||||
|
||||
contractDir=trustEvmContractRoot + "/evm_runtime"
|
||||
wasmFile="evm_runtime.wasm"
|
||||
abiFile="evm_runtime.abi"
|
||||
Utils.Print("Publish evm_runtime contract")
|
||||
trans = prodNode.publishContract(evmAcc, contractDir, wasmFile, abiFile, waitForTransBlock=True)
|
||||
transId=prodNode.getTransId(trans)
|
||||
blockNum = prodNode.getBlockNumByTransId(transId)
|
||||
block = prodNode.getBlock(blockNum)
|
||||
Utils.Print("Block Id: ", block["id"])
|
||||
Utils.Print("Block timestamp: ", block["timestamp"])
|
||||
|
||||
Utils.Print("Set balance")
|
||||
trans = prodNode.pushMessage(evmAcc.name, "setbal", '{"addy":"2787b98fc4e731d0456b3941f0b3fe2e01439961", "bal":"0000000000000000000000000000000100000000000000000000000000000000"}', '-p evmevmevmevm')
|
||||
|
||||
prodNode.waitForTransBlockIfNeeded(trans[1], True)
|
||||
|
||||
Utils.Print("Send balance")
|
||||
evmChainId = 15555
|
||||
fromAdd = "2787b98fc4e731d0456b3941f0b3fe2e01439961"
|
||||
toAdd = '0x9edf022004846bc987799d552d1b8485b317b7ed'
|
||||
amount = 100
|
||||
nonce = 0
|
||||
evmSendKey = "a3f1b69da92a0233ce29485d3049a4ace39e8d384bbc2557e3fc60940ce4e954"
|
||||
unsignedTrx = transactions.Transaction(
|
||||
nonce,
|
||||
150000000000, #150 GWei
|
||||
100000, #100k Gas
|
||||
toAdd,
|
||||
amount,
|
||||
b''
|
||||
)
|
||||
rlptx = rlp.encode(unsignedTrx.sign(evmSendKey, evmChainId), transactions.Transaction)
|
||||
actData = {"ram_payer":"evmevmevmevm", "rlptx":rlptx.hex()}
|
||||
trans = prodNode.pushMessage(evmAcc.name, "pushtx", json.dumps(actData), '-p evmevmevmevm')
|
||||
prodNode.waitForTransBlockIfNeeded(trans[1], True)
|
||||
|
||||
Utils.Print("Send balance again, should fail with wrong nonce")
|
||||
retValue = prodNode.pushMessage(evmAcc.name, "pushtx", json.dumps(actData), '-p evmevmevmevm', silentErrors=True, force=True)
|
||||
assert not retValue[0], f"push trx should have failed: {retValue}"
|
||||
|
||||
Utils.Print("Simple Solidity contract")
|
||||
# // SPDX-License-Identifier: GPL-3.0
|
||||
# pragma solidity >=0.7.0 <0.9.0;
|
||||
# contract Storage {
|
||||
# uint256 number;
|
||||
# function store(uint256 num) public {
|
||||
# number = num;
|
||||
# }
|
||||
# function retrieve() public view returns (uint256){
|
||||
# return number;
|
||||
# }
|
||||
# }
|
||||
retValue = prodNode.pushMessage(evmAcc.name, "updatecode", '{"address":"2787b98fc4e731d0456b3941f0b3fe2e01430000","incarnation":0,"code_hash":"286e3d498e2178afc91275f11d446e62a0d85b060ce66d6ca75f6ec9d874d560","code":"608060405234801561001057600080fd5b50600436106100365760003560e01c80632e64cec11461003b5780636057361d14610059575b600080fd5b610043610075565b60405161005091906100d9565b60405180910390f35b610073600480360381019061006e919061009d565b61007e565b005b60008054905090565b8060008190555050565b60008135905061009781610103565b92915050565b6000602082840312156100b3576100b26100fe565b5b60006100c184828501610088565b91505092915050565b6100d3816100f4565b82525050565b60006020820190506100ee60008301846100ca565b92915050565b6000819050919050565b600080fd5b61010c816100f4565b811461011757600080fd5b5056fea26469706673582212209a159a4f3847890f10bfb87871a61eba91c5dbf5ee3cf6398207e292eee22a1664736f6c63430008070033"}', '-p evmevmevmevm')
|
||||
|
||||
testSuccessful=True
|
||||
|
||||
generate_evm_transactions(nonce)
|
||||
|
||||
testSuccessful=True
|
||||
finally:
|
||||
TestHelper.shutdown(cluster, walletMgr, testSuccessful=testSuccessful, killEosInstances=killEosInstances, killWallet=killEosInstances, keepLogs=keepLogs, cleanRun=killAll, dumpErrorDetails=dumpErrorDetails)
|
||||
|
||||
exitCode = 0 if testSuccessful else 1
|
||||
exit(exitCode)
|
||||
Reference in New Issue
Block a user