From 4c7950035228455043cf3b08fbfe5bb3cbec0986 Mon Sep 17 00:00:00 2001 From: Dimitri Roche Date: Thu, 3 May 2018 14:50:03 -0400 Subject: [PATCH 1/5] echo_server runlog example takes job id in ctor --- examples/echo_server/README.md | 13 ++++---- examples/echo_server/chainlink_deployer.js | 24 +++++++++++++++ examples/echo_server/contracts/RunLog.sol | 6 ++-- examples/echo_server/migrations/5_run_log.js | 11 +++++-- examples/echo_server/package.json | 4 +-- examples/echo_server/test/RunLog_test.js | 2 +- yarn.lock | 32 +++----------------- 7 files changed, 51 insertions(+), 41 deletions(-) create mode 100644 examples/echo_server/chainlink_deployer.js diff --git a/examples/echo_server/README.md b/examples/echo_server/README.md index f808858e68f..af1841a00c2 100644 --- a/examples/echo_server/README.md +++ b/examples/echo_server/README.md @@ -26,13 +26,12 @@ a job anytime a log event occurs. It can optionally be filtered by an `address`. Uses a `runlog` initiator to echo Chainlink log events with the matching job id. 1. Complete the [Run Chainlink Development Environment](../README.md#run-chainlink-development-environment) steps. -2. `./create_runlog_job` to create CL job. Keep track of the returned job id. -3. `yarn install` -4. `node echo.js` -5. Add job id to `contracts/RunLog.sol` where it says `MY_JOB_ID` -5. `./node_modules/.bin/truffle migrate --reset` in another window -6. `node send_runlog_transaction.js` -7. Wait for log to show up in echo server +2. `yarn install` +3. `node echo.js` +4. `./node_modules/.bin/truffle migrate` in another window +5. `node send_runlog_transaction.js` +6. Wait for log to show up in echo server +7. Investigate migrations/5_run_log.js for insight ## Further Reading diff --git a/examples/echo_server/chainlink_deployer.js b/examples/echo_server/chainlink_deployer.js new file mode 100644 index 00000000000..066a7519593 --- /dev/null +++ b/examples/echo_server/chainlink_deployer.js @@ -0,0 +1,24 @@ +let fs = require("fs"); +let request = require("request"); +let url = "http://chainlink:twochains@localhost:6688/v2/specs"; + +module.exports = { + // Deploys chainlink jobs. + job: function(filename, callback, callbackError) { + fs.readFile(filename, 'utf8', (err, file) => { + let data = JSON.parse(file); + console.log(`Posting to ${url}:\n`, data); + request.post(url, {json: data}, + function (error, response, body) { + if (!error && response && response.statusCode == 200) { + callback(error, response, body); + } else { + if (callbackError) { + callbackError(error, response); + } + } + } + ); + }); + } +}; diff --git a/examples/echo_server/contracts/RunLog.sol b/examples/echo_server/contracts/RunLog.sol index 7ba636553c7..0ba6275c23b 100644 --- a/examples/echo_server/contracts/RunLog.sol +++ b/examples/echo_server/contracts/RunLog.sol @@ -4,14 +4,16 @@ import "../../../solidity/contracts/Chainlinked.sol"; contract RunLog is Chainlinked { bytes32 private externalId; + bytes32 private jobId; - constructor(address _link, address _oracle) public { + function RunLog(address _link, address _oracle, bytes32 _jobId) public { setLinkToken(_link); setOracle(_oracle); + jobId = _jobId; } function request() public { - ChainlinkLib.Run memory run = newRun("MY_JOB_ID", this, "fulfill(bytes32,bytes32)"); + ChainlinkLib.Run memory run = newRun(jobId, this, "fulfill(bytes32,bytes32)"); run.add("msg", "hello_chainlink"); externalId = chainlinkRequest(run, 1 szabo); } diff --git a/examples/echo_server/migrations/5_run_log.js b/examples/echo_server/migrations/5_run_log.js index 2625de80cb4..37e8a0f4de2 100644 --- a/examples/echo_server/migrations/5_run_log.js +++ b/examples/echo_server/migrations/5_run_log.js @@ -1,7 +1,14 @@ +let chainlinkDeployer = require("../chainlink_deployer.js"); let LinkToken = artifacts.require("../node_modules/smartcontractkit/chainlink/solidity/contracts/LinkToken.sol"); let Oracle = artifacts.require("../node_modules/smartcontractkit/chainlink/solidity/contracts/Oracle.sol"); let RunLog = artifacts.require("./RunLog.sol"); -module.exports = function(deployer) { - deployer.deploy(RunLog, LinkToken.address, Oracle.address); +module.exports = function(truffleDeployer) { + console.log(`Create Chainlink Job`); + chainlinkDeployer.job("only_jobid_logs_job.json", function(error, response, body) { + console.log(`Deploying Consumer Contract with JobID ${body.id}`); + truffleDeployer.deploy(RunLog, LinkToken.address, Oracle.address, body.id); + }, function(error) { + console.log("chainlink error:", error); + }); }; diff --git a/examples/echo_server/package.json b/examples/echo_server/package.json index f1785a83710..1fabff5dd45 100644 --- a/examples/echo_server/package.json +++ b/examples/echo_server/package.json @@ -2,9 +2,9 @@ "dependencies": { "express": "^4.16.2", "linkToken": "github:smartcontractkit/linktoken", + "openzeppelin-solidity": "^1.6.0", "solidity-cborutils": "github:smartcontractkit/solidity-cborutils", "truffle": "^4.1.6", - "truffle-contract": "^3.0.3", - "openzeppelin-solidity": "^1.6.0" + "truffle-contract": "^3.0.3" } } diff --git a/examples/echo_server/test/RunLog_test.js b/examples/echo_server/test/RunLog_test.js index e1ec1d7848e..8c280461a28 100644 --- a/examples/echo_server/test/RunLog_test.js +++ b/examples/echo_server/test/RunLog_test.js @@ -9,7 +9,7 @@ contract('RunLog', () => { beforeEach(async () => { link = await LinkToken.new(); oc = await Oracle.new(link.address); - logger = await RunLog.new(link.address, oc.address); + logger = await RunLog.new(link.address, oc.address, "SOME_JOB_ID"); await link.transfer(logger.address, web3.toWei(1)); }); diff --git a/yarn.lock b/yarn.lock index 4cc63ab6737..9d3f3d57c36 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1736,7 +1736,7 @@ debug@3.1.0, debug@^3.1.0: dependencies: ms "2.0.0" -debuglog@*, debuglog@^1.0.1: +debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" @@ -3042,7 +3042,7 @@ import-local@^1.0.0: pkg-dir "^2.0.0" resolve-cwd "^2.0.0" -imurmurhash@*, imurmurhash@^0.1.4: +imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" @@ -3790,10 +3790,6 @@ lodash._basecreate@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz#1bc661614daa7fc311b7d03bf16806a0213cf821" -lodash._baseindexof@*: - version "3.1.0" - resolved "https://registry.yarnpkg.com/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz#fe52b53a1c6761e42618d654e4a25789ed61822c" - lodash._baseuniq@~4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8" @@ -3801,25 +3797,11 @@ lodash._baseuniq@~4.6.0: lodash._createset "~4.0.0" lodash._root "~3.0.0" -lodash._bindcallback@*: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e" - -lodash._cacheindexof@*: - version "3.0.2" - resolved "https://registry.yarnpkg.com/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz#3dc69ac82498d2ee5e3ce56091bafd2adc7bde92" - -lodash._createcache@*: - version "3.1.2" - resolved "https://registry.yarnpkg.com/lodash._createcache/-/lodash._createcache-3.1.2.tgz#56d6a064017625e79ebca6b8018e17440bdcf093" - dependencies: - lodash._getnative "^3.0.0" - lodash._createset@~4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26" -lodash._getnative@*, lodash._getnative@^3.0.0: +lodash._getnative@^3.0.0: version "3.9.1" resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" @@ -3863,10 +3845,6 @@ lodash.keys@^3.0.0: lodash.isarguments "^3.0.0" lodash.isarray "^3.0.0" -lodash.restparam@*: - version "3.6.1" - resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" - lodash.union@~4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" @@ -5351,7 +5329,7 @@ readable-stream@~1.1.10: isarray "0.0.1" string_decoder "~0.10.x" -readdir-scoped-modules@*, readdir-scoped-modules@^1.0.0: +readdir-scoped-modules@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.0.2.tgz#9fafa37d286be5d92cbaebdee030dc9b5f406747" dependencies: @@ -6664,7 +6642,7 @@ v8-compile-cache@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-1.1.2.tgz#8d32e4f16974654657e676e0e467a348e89b0dc4" -validate-npm-package-license@*, validate-npm-package-license@^3.0.1: +validate-npm-package-license@^3.0.1: version "3.0.3" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz#81643bcbef1bdfecd4623793dc4648948ba98338" dependencies: From 05db33a516f3a50092ed09a18333903c3c8972dd Mon Sep 17 00:00:00 2001 From: Dimitri Roche Date: Thu, 3 May 2018 16:03:22 -0400 Subject: [PATCH 2/5] Refactor bash ethereum_test to use assert func --- internal/ci/ethereum_test | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/internal/ci/ethereum_test b/internal/ci/ethereum_test index 5a9baa0b665..9d24cc9cfde 100755 --- a/internal/ci/ethereum_test +++ b/internal/ci/ethereum_test @@ -7,6 +7,15 @@ trap "kill -- -$$ || true" SIGINT SIGTERM EXIT PATH=./internal/bin:./node_modules/.bin:$PATH +# Utilities +assert () +{ + echo "$1: expected $2, actual $3." + if [ "$2" -ne "$3" ]; then + exit 1 + fi +} + # Run gethnet time go get -d github.com/ethereum/go-ethereum time go install github.com/ethereum/go-ethereum/cmd/geth @@ -38,34 +47,18 @@ sleep 2 # Check echo count count=`curl -sS localhost:6690/count` -if [ "$count" -ne "1" ]; then - echo "Echo count is $count, not 1." - exit 1 -else - echo "Echo count is correctly $count" -fi +assert "Echo count" 1 $count # Check CL counts cd ../../ ## Check job counts jobs=`cldev -j j | jq length` -echo Retrieved $jobs jobs -if [ "$jobs" -ne "1" ]; then - echo "Jobs count is $jobs, not 1." - exit 1 -else - echo "Jobs count is correctly $jobs." -fi +assert "Jobs count" 1 $jobs ## Check job runs jid=`echo $ethjob | jq .id | tr -d '"'` echo Retrieved job id $jid runs=`cldev -j s $jid | jq '.runs | length'` echo Retrieved $runs runs -if [ "$runs" -ne "1" ]; then - echo "Runs count is $runs, not 1." - exit 1 -else - echo "Runs count is correctly $runs." -fi +assert "Runs count" 1 $runs From 3cfdbcc8098c299a1765ac584f62c4568ab4f61a Mon Sep 17 00:00:00 2001 From: Dimitri Roche Date: Thu, 3 May 2018 16:05:55 -0400 Subject: [PATCH 3/5] Graceful exits with SIGTERM or SIGINT have exit code 0 --- services/application.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/application.go b/services/application.go index e5c2a83b83d..18ccf7ffe8b 100644 --- a/services/application.go +++ b/services/application.go @@ -59,7 +59,7 @@ func (app *ChainlinkApplication) Start() error { go func() { <-sigs app.Stop() - app.Exiter(1) + app.Exiter(0) }() app.attachmentID = app.HeadTracker.Attach(app.EthereumListener) From 9e0cf501e866edd83c5c3e7f71a5750d5dc331ef Mon Sep 17 00:00:00 2001 From: Dimitri Roche Date: Fri, 4 May 2018 08:35:18 -0400 Subject: [PATCH 4/5] Update echo_server RunLog to take jobId in ctor --- examples/echo_server/chainlink_deployer.js | 24 +++++++------------ examples/echo_server/contracts/RunLog.sol | 2 +- examples/echo_server/create_runlog_job | 4 ---- examples/echo_server/migrations/5_run_log.js | 11 ++++----- .../echo_server/migrations/6_transfer_link.js | 6 ++--- examples/echo_server/package.json | 1 + examples/echo_server/yarn.lock | 21 +++++++++++++++- 7 files changed, 38 insertions(+), 31 deletions(-) delete mode 100755 examples/echo_server/create_runlog_job diff --git a/examples/echo_server/chainlink_deployer.js b/examples/echo_server/chainlink_deployer.js index 066a7519593..858c434363e 100644 --- a/examples/echo_server/chainlink_deployer.js +++ b/examples/echo_server/chainlink_deployer.js @@ -1,24 +1,16 @@ -let fs = require("fs"); -let request = require("request"); -let url = "http://chainlink:twochains@localhost:6688/v2/specs"; +const fs = require("fs"); +const request = require("request-promise"); +const util = require('util'); +const url = "http://chainlink:twochains@localhost:6688/v2/specs"; module.exports = { // Deploys chainlink jobs. - job: function(filename, callback, callbackError) { - fs.readFile(filename, 'utf8', (err, file) => { + job: async function(filename) { + let readFile = util.promisify(fs.readFile); + return await readFile(filename, 'utf8').then((file) => { let data = JSON.parse(file); console.log(`Posting to ${url}:\n`, data); - request.post(url, {json: data}, - function (error, response, body) { - if (!error && response && response.statusCode == 200) { - callback(error, response, body); - } else { - if (callbackError) { - callbackError(error, response); - } - } - } - ); + return request.post(url, {json: data}); }); } }; diff --git a/examples/echo_server/contracts/RunLog.sol b/examples/echo_server/contracts/RunLog.sol index 0ba6275c23b..a71caa9256a 100644 --- a/examples/echo_server/contracts/RunLog.sol +++ b/examples/echo_server/contracts/RunLog.sol @@ -6,7 +6,7 @@ contract RunLog is Chainlinked { bytes32 private externalId; bytes32 private jobId; - function RunLog(address _link, address _oracle, bytes32 _jobId) public { + constructor(address _link, address _oracle, bytes32 _jobId) public { setLinkToken(_link); setOracle(_oracle); jobId = _jobId; diff --git a/examples/echo_server/create_runlog_job b/examples/echo_server/create_runlog_job deleted file mode 100755 index 0c79e70fb0e..00000000000 --- a/examples/echo_server/create_runlog_job +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -curl -sS -u chainlink:twochains -X POST -H 'Content-Type: application/json' \ - -d @./only_jobid_logs_job.json http://localhost:6688/v2/specs diff --git a/examples/echo_server/migrations/5_run_log.js b/examples/echo_server/migrations/5_run_log.js index 37e8a0f4de2..62c86af4c11 100644 --- a/examples/echo_server/migrations/5_run_log.js +++ b/examples/echo_server/migrations/5_run_log.js @@ -4,11 +4,10 @@ let Oracle = artifacts.require("../node_modules/smartcontractkit/chainlink/solid let RunLog = artifacts.require("./RunLog.sol"); module.exports = function(truffleDeployer) { - console.log(`Create Chainlink Job`); - chainlinkDeployer.job("only_jobid_logs_job.json", function(error, response, body) { - console.log(`Deploying Consumer Contract with JobID ${body.id}`); - truffleDeployer.deploy(RunLog, LinkToken.address, Oracle.address, body.id); - }, function(error) { - console.log("chainlink error:", error); + truffleDeployer.then(async () => { + await chainlinkDeployer.job("only_jobid_logs_job.json").then(async function(body) { + console.log(`Deploying Consumer Contract with JobID ${body.id}`); + await truffleDeployer.deploy(RunLog, LinkToken.address, Oracle.address, body.id); + }).catch(console.log); }); }; diff --git a/examples/echo_server/migrations/6_transfer_link.js b/examples/echo_server/migrations/6_transfer_link.js index e267a42e72b..dceb9487109 100644 --- a/examples/echo_server/migrations/6_transfer_link.js +++ b/examples/echo_server/migrations/6_transfer_link.js @@ -2,11 +2,11 @@ let LinkToken = artifacts.require("../node_modules/smartcontractkit/chainlink/so let RunLog = artifacts.require("./RunLog.sol"); let devnetAddress = "0x9CA9d2D5E04012C9Ed24C0e513C9bfAa4A2dD77f"; -module.exports = async function(deployer) { - await LinkToken.deployed().then(async function(linkInstance) { +module.exports = function(deployer) { + LinkToken.deployed().then(async function(linkInstance) { await RunLog.deployed().then(async function(runLogInstance) { await linkInstance.transfer(runLogInstance.address, web3.toWei(1000)); await linkInstance.transfer(devnetAddress, web3.toWei(1000)); - }); + }).catch(console.log); }); }; diff --git a/examples/echo_server/package.json b/examples/echo_server/package.json index 1fabff5dd45..1a9e36f7205 100644 --- a/examples/echo_server/package.json +++ b/examples/echo_server/package.json @@ -3,6 +3,7 @@ "express": "^4.16.2", "linkToken": "github:smartcontractkit/linktoken", "openzeppelin-solidity": "^1.6.0", + "request-promise": "^4.2.2", "solidity-cborutils": "github:smartcontractkit/solidity-cborutils", "truffle": "^4.1.6", "truffle-contract": "^3.0.3" diff --git a/examples/echo_server/yarn.lock b/examples/echo_server/yarn.lock index ffa3e716564..c44ec368e86 100644 --- a/examples/echo_server/yarn.lock +++ b/examples/echo_server/yarn.lock @@ -5132,6 +5132,21 @@ replace-ext@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb" +request-promise-core@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.1.tgz#3eee00b2c5aa83239cfb04c5700da36f81cd08b6" + dependencies: + lodash "^4.13.1" + +request-promise@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/request-promise/-/request-promise-4.2.2.tgz#d1ea46d654a6ee4f8ee6a4fea1018c22911904b4" + dependencies: + bluebird "^3.5.0" + request-promise-core "1.1.1" + stealthy-require "^1.1.0" + tough-cookie ">=2.3.3" + request@2, request@^2.74.0: version "2.85.0" resolved "https://registry.yarnpkg.com/request/-/request-2.85.0.tgz#5a03615a47c61420b3eb99b7dba204f83603e1fa" @@ -5676,6 +5691,10 @@ statuses@~1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" +stealthy-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" + stream-browserify@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db" @@ -5940,7 +5959,7 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" -tough-cookie@~2.3.0, tough-cookie@~2.3.3: +tough-cookie@>=2.3.3, tough-cookie@~2.3.0, tough-cookie@~2.3.3: version "2.3.4" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655" dependencies: From 7562fc8921df72d1817df64658e7ce3e1840d486 Mon Sep 17 00:00:00 2001 From: Dimitri Roche Date: Fri, 4 May 2018 08:35:45 -0400 Subject: [PATCH 5/5] Update ci/ethereum_test to check echo_server/RunLog --- internal/bin/cldev | 1 + internal/ci/ethereum_test | 33 ++++++++++++++++++++++++++++----- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/internal/bin/cldev b/internal/bin/cldev index 597e8064b0d..99d16753e39 100755 --- a/internal/bin/cldev +++ b/internal/bin/cldev @@ -12,6 +12,7 @@ export ROOT=./internal/clroot export ETH_URL=ws://localhost:18546 export ETH_CHAIN_ID=17 export TX_MIN_CONFIRMATIONS=2 +export MINIMUM_CONTRACT_PAYMENT=1000000000000 LDFLAGS="-X github.com/smartcontractkit/chainlink/store.Sha=`git rev-parse HEAD`" diff --git a/internal/ci/ethereum_test b/internal/ci/ethereum_test index 9d24cc9cfde..7d9b0ffda13 100755 --- a/internal/ci/ethereum_test +++ b/internal/ci/ethereum_test @@ -36,29 +36,52 @@ cd examples/echo_server yarn install truffle migrate node echo.js & +sleep 1 + +######################## +## runlog +######################## + +./send_runlog_transaction.js +sleep 2 + +# Check echo count +count=`curl -sS localhost:6690/count` +assert "Echo count" 1 $count + +# Check CL counts +cd ../../ + +## Check job counts using jq to parse json: https://stedolan.github.io/jq/ +jobs=`cldev -j j | jq length` +assert "Jobs count" 1 $jobs + +# Check job runs +jid=`cldev -j j | jq 'first | .id' | tr -d '"'` +runs=`cldev -j s $jid | jq '.runs | length'` +assert "RunLog Runs count" 1 $runs ######################## ## ethlog ######################## +cd examples/echo_server ethjob=`./create_ethlog_job` ./send_ethlog_transaction.js sleep 2 # Check echo count count=`curl -sS localhost:6690/count` -assert "Echo count" 1 $count +assert "Echo count" 2 $count # Check CL counts cd ../../ ## Check job counts jobs=`cldev -j j | jq length` -assert "Jobs count" 1 $jobs +assert "Jobs count" 2 $jobs ## Check job runs jid=`echo $ethjob | jq .id | tr -d '"'` -echo Retrieved job id $jid runs=`cldev -j s $jid | jq '.runs | length'` -echo Retrieved $runs runs -assert "Runs count" 1 $runs +assert "EthLog Runs count" 1 $runs