From 56fee0ee2e862f2c6d5b978a2f1956ec58ff80b3 Mon Sep 17 00:00:00 2001 From: Stefan Iliev <46542846+StefanIliev545@users.noreply.github.com> Date: Tue, 3 Sep 2024 16:20:40 +0300 Subject: [PATCH] Cross chain testing scripts. (#2033) * Rework to use config endpoint. * Returning sim test. * Revert IDE flag. * Adding l2 message bus. * Dumping progress. * Dumping debug scripts. * UAT version. * Potential fix. * Fix for rpc error. * Committing generated contracts. * UAT changes. * Added dev testnet to the network config. * Adding back skip flag. * Addressed PR comments. * PR comment. --------- Co-authored-by: StefanIliev545 --- contracts/config/networks.json | 94 +++++++++++++- .../bridge/001_deploy_bridge.ts | 2 +- .../testing/001_gas_testing_deployment.ts | 1 - .../testing/002_access_list_test.ts | 36 ++++++ .../testing/003_simple_withdrawal.ts | 119 ++++++++++++++++++ .../GasConsumerBalance/GasConsumerBalance.go | 25 +++- contracts/package.json | 3 +- contracts/src/testing/GasConsumerBalance.sol | 4 + go/common/gethencoding/geth_encoding.go | 3 + go/host/l1/publisher.go | 2 + go/host/l1/statemachine.go | 7 ++ integration/eth2network/pos_eth2_network.go | 2 + .../simulation/devnetwork/dev_network.go | 1 + 13 files changed, 293 insertions(+), 6 deletions(-) create mode 100644 contracts/deployment_scripts/testing/002_access_list_test.ts create mode 100644 contracts/deployment_scripts/testing/003_simple_withdrawal.ts diff --git a/contracts/config/networks.json b/contracts/config/networks.json index 6b63a7eade..de384dfead 100644 --- a/contracts/config/networks.json +++ b/contracts/config/networks.json @@ -1,6 +1,6 @@ { "localGeth": { - "url": "http://127.0.0.1:17000", + "url": "http://127.0.0.1:25400", "deploy": [ "deployment_scripts/core" ], @@ -30,6 +30,22 @@ "4bfe14725e685901c062ccd4e220c61cf9c189897b6c78bd18d7f51291b2b8f8" ] }, + "testObscuro": { + "chainId": 443, + "url": "http://127.0.0.1:11180/v1/", + "useGateway": true, + "companionNetworks" : { + "layer1" : "localGeth" + }, + "deploy": [ + "deployment_scripts/testing" + ], + "accounts": [ + "8dfb8083da6275ae3e4f41e3e8a8c19d028d32c9247e24530933782f2a05035b", + "6e384a07a01263518a09a5424c7b6bbfc3604ba7d93f47e3a455cbdd7f9f0682", + "4bfe14725e685901c062ccd4e220c61cf9c189897b6c78bd18d7f51291b2b8f8" + ] + }, "hardhat": { "deploy": [ "deployment_scripts/core/layer1/", @@ -47,5 +63,81 @@ "balance": "174165200000000000" } ] + }, + "uatGeth": { + "url": "http://uat-testnet-eth2network.uksouth.cloudapp.azure.com:8025", + "deploy": [ + "deployment_scripts/core" + ], + "accounts": [ + "4bfe14725e685901c062ccd4e220c61cf9c189897b6c78bd18d7f51291b2b8f1" + ] + }, + "testUatObscuro": { + "chainId": 443, + "url": "https://uat-testnet.ten.xyz/v1/", + "useGateway": true, + "companionNetworks" : { + "layer1" : "uatGeth" + }, + "deploy": [ + "deployment_scripts/testing" + ], + "accounts": [ + "8dfb8083da6275ae3e4f41e3e8a8c19d028d32c9247e24530933782f2a05035b", + "6e384a07a01263518a09a5424c7b6bbfc3604ba7d93f47e3a455cbdd7f9f0682", + "4bfe14725e685901c062ccd4e220c61cf9c189897b6c78bd18d7f51291b2b8f8" + ] + }, + "devGeth": { + "url": "http://dev-testnet-eth2network.uksouth.cloudapp.azure.com:8025", + "deploy": [ + "deployment_scripts/core" + ], + "accounts": [ + "4bfe14725e685901c062ccd4e220c61cf9c189897b6c78bd18d7f51291b2b8f1" + ] + }, + "testDevObscuro": { + "chainId": 443, + "url": "https://dev-testnet.ten.xyz/v1/", + "useGateway": true, + "companionNetworks" : { + "layer1" : "uatGeth" + }, + "deploy": [ + "deployment_scripts/testing" + ], + "accounts": [ + "8dfb8083da6275ae3e4f41e3e8a8c19d028d32c9247e24530933782f2a05035b", + "6e384a07a01263518a09a5424c7b6bbfc3604ba7d93f47e3a455cbdd7f9f0682", + "4bfe14725e685901c062ccd4e220c61cf9c189897b6c78bd18d7f51291b2b8f8" + ] + }, + "localTestnetGeth": { + "url": "http://127.0.0.1:8025", + "deploy": [ + "deployment_scripts/core" + ], + "accounts": [ + "f52e5418e349dccdda29b6ac8b0abe6576bb7713886aa85abea6181ba731f9bb", + "4bfe14725e685901c062ccd4e220c61cf9c189897b6c78bd18d7f51291b2b8f1" + ] + }, + "localTestnetTen": { + "chainId": 443, + "url": "http://127.0.0.1:3000/v1/", + "useGateway": true, + "companionNetworks" : { + "layer1" : "localTestnetGeth" + }, + "deploy": [ + "deployment_scripts/testing" + ], + "accounts": [ + "8dfb8083da6275ae3e4f41e3e8a8c19d028d32c9247e24530933782f2a05035b", + "6e384a07a01263518a09a5424c7b6bbfc3604ba7d93f47e3a455cbdd7f9f0682", + "4bfe14725e685901c062ccd4e220c61cf9c189897b6c78bd18d7f51291b2b8f8" + ] } } \ No newline at end of file diff --git a/contracts/deployment_scripts/bridge/001_deploy_bridge.ts b/contracts/deployment_scripts/bridge/001_deploy_bridge.ts index 056bc2a6e4..07198386e0 100644 --- a/contracts/deployment_scripts/bridge/001_deploy_bridge.ts +++ b/contracts/deployment_scripts/bridge/001_deploy_bridge.ts @@ -8,7 +8,7 @@ import {DeployFunction} from 'hardhat-deploy/types'; */ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { - const { + const { deployments, getNamedAccounts } = hre; diff --git a/contracts/deployment_scripts/testing/001_gas_testing_deployment.ts b/contracts/deployment_scripts/testing/001_gas_testing_deployment.ts index d28d409bc2..d6062a8e60 100644 --- a/contracts/deployment_scripts/testing/001_gas_testing_deployment.ts +++ b/contracts/deployment_scripts/testing/001_gas_testing_deployment.ts @@ -16,7 +16,6 @@ const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { const gasConsumerBalance = await hre.ethers.getContractAt("GasConsumerBalance", gcb.address) const gasEstimation = await gasConsumerBalance.getFunction('get_balance').estimateGas({ from: deployer, - gasPrice: 2, }); await hre.deployments.execute("GasConsumerBalance", { diff --git a/contracts/deployment_scripts/testing/002_access_list_test.ts b/contracts/deployment_scripts/testing/002_access_list_test.ts new file mode 100644 index 0000000000..f2b65fd0e5 --- /dev/null +++ b/contracts/deployment_scripts/testing/002_access_list_test.ts @@ -0,0 +1,36 @@ +import {HardhatRuntimeEnvironment} from 'hardhat/types'; +import {DeployFunction} from 'hardhat-deploy/types'; +import { Receipt } from 'hardhat-deploy/dist/types'; + + +const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { + return; + const l2Network = hre; + const {deployer} = await hre.getNamedAccounts(); + + const gcb = await l2Network.deployments.deploy("GasConsumerBalance", { + from: deployer, + log: true + }) + + const signer = await hre.ethers.getSigner(deployer); + + const gasConsumerBalance = await hre.ethers.getContractAt("GasConsumerBalance", gcb.address) + const gasConsumer = await gasConsumerBalance.connect(signer) + + + const tx = await gasConsumer.getFunction("resetOwner").populateTransaction(deployer); + tx.accessList = [ + { + address: gcb.address, + storageKeys: [] + }, + ]; + const resp = await signer.sendTransaction(tx); + const receipt = await resp.wait(); + console.log(`Receipt.Status=${receipt.status}`); +}; + + +export default func; +func.tags = ['GasDebug']; diff --git a/contracts/deployment_scripts/testing/003_simple_withdrawal.ts b/contracts/deployment_scripts/testing/003_simple_withdrawal.ts new file mode 100644 index 0000000000..c7f19ccad6 --- /dev/null +++ b/contracts/deployment_scripts/testing/003_simple_withdrawal.ts @@ -0,0 +1,119 @@ +import {EthereumProvider, HardhatRuntimeEnvironment} from 'hardhat/types'; +import {DeployFunction} from 'hardhat-deploy/types'; +import { Receipt } from 'hardhat-deploy/dist/types'; +import { StandardMerkleTree } from "@openzeppelin/merkle-tree"; +import { keccak256 } from 'ethers'; +import { HardhatEthersProvider } from '@nomicfoundation/hardhat-ethers/internal/hardhat-ethers-provider'; + +function process_value_transfer(ethers, value_transfer) { + const abiTypes = ['address', 'address', 'uint256', 'uint64']; + const msg = [ + value_transfer['args'].sender, value_transfer['args'].receiver, + value_transfer['args'].amount.toString(), value_transfer['args'].sequence.toString() + ] + + const abiCoder = ethers.AbiCoder.defaultAbiCoder(); + const encodedMsg = abiCoder.encode(abiTypes, msg); + return [msg, ethers.keccak256(encodedMsg)]; + } + + + function decode_base64(base64String) { + let jsonString = atob(base64String); + return JSON.parse(jsonString); + } + + +async function sleep(ms: number) { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); +} +async function waitForRootPublished(management, msg, proof, root, provider: EthereumProvider, interval = 5000, timeout = 120000) { + var gas_estimate = null + const l1Ethers = new HardhatEthersProvider(provider, "layer1") + + const startTime = Date.now(); + while (gas_estimate === null) { + try { + console.log(`Extracting native value from cross chain message for root ${root}`) + const tx = await management.getFunction('ExtractNativeValue').populateTransaction(msg, proof, root, {} ) + console.log(`Tx to = ${tx.to}`) + gas_estimate = await l1Ethers.estimateGas(tx) + } catch (error) { + console.log(`Estimate gas threw error : ${error}`) + } + if (Date.now() - startTime >= timeout) { + console.log(`Timed out waiting for the estimate gas to return`) + break + } + await sleep(interval) + } + console.log(`Estimation took ${Date.now() - startTime} ms`) + return gas_estimate +} + + +const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { + const l2Network = hre; + const {deployer} = await hre.getNamedAccounts(); + + var mbusBase = await hre.ethers.getContractAt("MessageBus", "0x526c84529b2b8c11f57d93d3f5537aca3aecef9b"); + const mbus = mbusBase.connect(await hre.ethers.provider.getSigner(deployer)); + const tx = await mbus.getFunction("sendValueToL2").send(deployer, 1000, { value: 1000}); + const receipt = await tx.wait() + console.log(`003_simple_withdrawal: Cross Chain send receipt status = ${receipt.status}`); + + const block = await hre.ethers.provider.send('eth_getBlockByHash', [receipt.blockHash, true]); + console.log(`Block received: ${block.number}`) + + + const value_transfer = mbus.interface.parseLog(receipt.logs[0]); + const _processed_value_transfer = process_value_transfer(hre.ethers, value_transfer) + const msg = _processed_value_transfer[0] + const msgHash = _processed_value_transfer[1] + const decoded = decode_base64(block.crossChainTree) + + console.log(` Sender: ${value_transfer['args'].sender}`) + console.log(` Receiver: ${value_transfer['args'].receiver}`) + console.log(` Amount: ${value_transfer['args'].amount}`) + console.log(` Sequence: ${value_transfer['args'].sequence}`) + console.log(` VTrans Hash: ${msgHash}`) + console.log(` XChain tree: ${decoded}`) + + if (decoded[0][1] != msgHash) { + console.error('Value transfer hash is not in the xchain tree!'); + return; + } + + const tree = StandardMerkleTree.of(decoded, ["string", "bytes32"]); + const proof = tree.getProof(['v',msgHash]) + console.log(` Merkle root: ${tree.root}`) + console.log(` Merkle proof: ${JSON.stringify(proof, null,2)}`) + + if (block.crossChainTreeHash != tree.root) { + console.error('Constructed merkle root matches block crossChainTreeHash'); + return + } + + + //const networkConfig : any = await hre.network.provider.request({method: 'net_config'}); + const mgmtContractAddress = "0x946600AF6893Ee818CC7CC2dEC4D0A0bF91C9817" // networkConfig.ManagementContractAddress; + const messageBusAddress = "0x68e95924f22Be35386A8aE0240f8885967d452D6" //networkConfig.MessageBusAddress; + + const l1Accounts = await hre.companionNetworks.layer1.getNamedAccounts() + const fundTx = await hre.companionNetworks.layer1.deployments.rawTx({ + from: l1Accounts.deployer, + to: messageBusAddress, + value: "1000", + }) + console.log(`Message bus funding status = ${fundTx.status}`) + + var managementContract = await hre.ethers.getContractAt("ManagementContract", mgmtContractAddress); + const estimation = await waitForRootPublished(managementContract, msg, proof, tree.root, hre.companionNetworks.layer1.provider) + console.log(`Estimation for native value extraction = ${estimation}`) +}; + + +export default func; +func.tags = ['GasDebug']; diff --git a/contracts/generated/GasConsumerBalance/GasConsumerBalance.go b/contracts/generated/GasConsumerBalance/GasConsumerBalance.go index 2444b2e2fa..724c46f8fe 100644 --- a/contracts/generated/GasConsumerBalance/GasConsumerBalance.go +++ b/contracts/generated/GasConsumerBalance/GasConsumerBalance.go @@ -31,8 +31,8 @@ var ( // GasConsumerBalanceMetaData contains all meta data concerning the GasConsumerBalance contract. var GasConsumerBalanceMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"destroy\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"get_balance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", - Bin: "0x608060405234801561001057600080fd5b50600080546001600160a01b03191633179055610157806100326000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806383197ef0146100465780638da5cb5b14610050578063c1cfb99a1461004e575b600080fd5b61004e610099565b005b6000546100709073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200160405180910390f35b60005473ffffffffffffffffffffffffffffffffffffffff16331461011e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f596f7520617265206e6f7420746865206f776e65720000000000000000000000604482015260640160405180910390fd5b30fffea26469706673582212205d989c8df853591d61707dd349944634844c4a7cbbae383852bd046ba70f7ab464736f6c63430008140033", + ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"destroy\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"get_balance\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_owner\",\"type\":\"address\"}],\"name\":\"resetOwner\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b50600080546001600160a01b031916331790556101b3806100326000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c806373cc802a1461005157806383197ef01461009b5780638da5cb5b146100a3578063c1cfb99a14610099575b600080fd5b61009961005f36600461014d565b600080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b005b6100996100d2565b6000546100b6906001600160a01b031681565b6040516001600160a01b03909116815260200160405180910390f35b6000546001600160a01b0316331461014a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f596f7520617265206e6f7420746865206f776e65720000000000000000000000604482015260640160405180910390fd5b30ff5b60006020828403121561015f57600080fd5b81356001600160a01b038116811461017657600080fd5b939250505056fea264697066735822122028e65924c674b360c7e0fc6c4d9fb70e74b16d18a3a462b15e37d5b51b9b929b64736f6c63430008140033", } // GasConsumerBalanceABI is the input ABI used to generate the binding from. @@ -274,3 +274,24 @@ func (_GasConsumerBalance *GasConsumerBalanceSession) GetBalance() (*types.Trans func (_GasConsumerBalance *GasConsumerBalanceTransactorSession) GetBalance() (*types.Transaction, error) { return _GasConsumerBalance.Contract.GetBalance(&_GasConsumerBalance.TransactOpts) } + +// ResetOwner is a paid mutator transaction binding the contract method 0x73cc802a. +// +// Solidity: function resetOwner(address _owner) returns() +func (_GasConsumerBalance *GasConsumerBalanceTransactor) ResetOwner(opts *bind.TransactOpts, _owner common.Address) (*types.Transaction, error) { + return _GasConsumerBalance.contract.Transact(opts, "resetOwner", _owner) +} + +// ResetOwner is a paid mutator transaction binding the contract method 0x73cc802a. +// +// Solidity: function resetOwner(address _owner) returns() +func (_GasConsumerBalance *GasConsumerBalanceSession) ResetOwner(_owner common.Address) (*types.Transaction, error) { + return _GasConsumerBalance.Contract.ResetOwner(&_GasConsumerBalance.TransactOpts, _owner) +} + +// ResetOwner is a paid mutator transaction binding the contract method 0x73cc802a. +// +// Solidity: function resetOwner(address _owner) returns() +func (_GasConsumerBalance *GasConsumerBalanceTransactorSession) ResetOwner(_owner common.Address) (*types.Transaction, error) { + return _GasConsumerBalance.Contract.ResetOwner(&_GasConsumerBalance.TransactOpts, _owner) +} diff --git a/contracts/package.json b/contracts/package.json index 648e64074c..11faaa9eeb 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -23,12 +23,13 @@ "dependencies": { "@openzeppelin/contracts": "^5.0.1", "@openzeppelin/contracts-upgradeable": "^5.0.1", + "@openzeppelin/merkle-tree": "^1.0.7", "ethers": "^6.6.0", "hardhat-ignore-warnings": "^0.2.6", "ten-hardhat-plugin": "^0.0.9" }, "peerDependencies": { "@nomicfoundation/hardhat-verify" : "2.0.8", - "@nomicfoundation/hardhat-ethers":"3.0.6" + "@nomicfoundation/hardhat-ethers":"3.0.5" } } \ No newline at end of file diff --git a/contracts/src/testing/GasConsumerBalance.sol b/contracts/src/testing/GasConsumerBalance.sol index 3b42c3334e..f1ddc46d88 100644 --- a/contracts/src/testing/GasConsumerBalance.sol +++ b/contracts/src/testing/GasConsumerBalance.sol @@ -15,4 +15,8 @@ contract GasConsumerBalance { require(msg.sender == owner, "You are not the owner"); selfdestruct(payable(address(this))); } + + function resetOwner(address _owner) public { + owner = _owner; + } } \ No newline at end of file diff --git a/go/common/gethencoding/geth_encoding.go b/go/common/gethencoding/geth_encoding.go index 33f819591a..9974b6a97f 100644 --- a/go/common/gethencoding/geth_encoding.go +++ b/go/common/gethencoding/geth_encoding.go @@ -39,6 +39,7 @@ const ( callFieldGasPrice = "gasprice" callFieldMaxFeePerGas = "maxfeepergas" callFieldMaxPriorityFeePerGas = "maxpriorityfeepergas" + callFieldAccessList = "accesslist" ) // EncodingService handles conversion to Geth data structures @@ -235,6 +236,8 @@ func ExtractEthCall(param interface{}) (*gethapi.TransactionArgs, error) { return nil, fmt.Errorf("could not decode value in CallMsg - %w", err) } maxPriorityFeePerGas = (*hexutil.Big)(maxPriorityFeePerGasVal) + case callFieldAccessList: + // ignore access list for now } } diff --git a/go/host/l1/publisher.go b/go/host/l1/publisher.go index cf52af0d84..3ad66d65a5 100644 --- a/go/host/l1/publisher.go +++ b/go/host/l1/publisher.go @@ -313,6 +313,8 @@ func (p *Publisher) PublishCrossChainBundle(bundle *common.ExtCrossChainBundle, transactor.Nonce = big.NewInt(0).SetUint64(nonce) + p.logger.Debug("Adding cross chain roots to management contract", log.BundleHashKey, bundle.CrossChainRootHashes) + tx, err := managementCtr.AddCrossChainMessagesRoot(transactor, [32]byte(bundle.LastBatchHash.Bytes()), bundle.L1BlockHash, bundle.L1BlockNum, bundle.CrossChainRootHashes, bundle.Signature, rollupNum, forkID) if err != nil { if !errors.Is(err, errutil.ErrCrossChainBundleRepublished) { diff --git a/go/host/l1/statemachine.go b/go/host/l1/statemachine.go index f3ac218eda..438336a594 100644 --- a/go/host/l1/statemachine.go +++ b/go/host/l1/statemachine.go @@ -15,6 +15,7 @@ import ( "github.com/ten-protocol/go-ten/go/common/stopcontrol" "github.com/ten-protocol/go-ten/go/ethadapter" "github.com/ten-protocol/go-ten/go/ethadapter/mgmtcontractlib" + "google.golang.org/grpc/status" ) type ( @@ -122,6 +123,11 @@ func (c *crossChainStateMachine) PublishNextBundle() error { bundle, err := c.enclaveClient.ExportCrossChainData(context.Background(), begin.Uint64(), end.Uint64()) if err != nil { + s, ok := status.FromError(err) + if ok && errors.Is(s.Err(), errutil.ErrCrossChainBundleNoBatches) { + c.currentRollup++ + return nil + } return err } @@ -178,6 +184,7 @@ func (c *crossChainStateMachine) Synchronize() error { Number: c.latestRollup.Number + 1, } + c.logger.Info("Synchronized rollup state machine", "latestRollup", c.latestRollup.Number, "forkUID", c.latestRollup.ForkUID.String()) return nil } diff --git a/integration/eth2network/pos_eth2_network.go b/integration/eth2network/pos_eth2_network.go index b2625e905b..e3b811d1d2 100644 --- a/integration/eth2network/pos_eth2_network.go +++ b/integration/eth2network/pos_eth2_network.go @@ -293,6 +293,8 @@ func startNetworkScript(gethNetworkPort, beaconP2PPort, gethRPCPort, gethHTTPPor "--beacondata-dir", beacondataDir, "--validatordata-dir", validatordataDir, ) + fmt.Println(cmd.String()) + var out bytes.Buffer cmd.Stdout = &out cmd.Stderr = &out diff --git a/integration/simulation/devnetwork/dev_network.go b/integration/simulation/devnetwork/dev_network.go index 562235314b..18239760ec 100644 --- a/integration/simulation/devnetwork/dev_network.go +++ b/integration/simulation/devnetwork/dev_network.go @@ -150,6 +150,7 @@ func (s *InMemDevNetwork) Start() { // this is a new network, deploy the contracts to the L1 fmt.Println("Deploying Ten contracts to L1") s.deployTenNetworkContracts() + fmt.Printf("L1 Port - %d\n", integration.StartPortNetworkTests) } fmt.Println("Starting Ten nodes") s.startNodes()