Skip to content

Commit

Permalink
running evm with ganache-cli in travis ci jobs (#301)
Browse files Browse the repository at this point in the history
* adding the whole idea directory to gitignore

* roughing in some testing scripts that run yul code through truffle and ganache. these almost certainly do not work.

* tweaking permissions, per https://stackoverflow.com/questions/42154912/permission-denied-for-build-sh-file

* updating npm so that it's modern enough to work for ganache

* commenting out fabric tests

* double quotes for safety against splitting

* debugging travis build

* debugging travis build

* debugging travis build

* travis_specific/install_ganache.sh

* updating to a more modern ubuntu to maybe solve some node problems

* debugging travis build

* debugging travis build

* going big with version increments

* debugging travis build

* debugging travis build

* debugging travis build xx

* breadcrumbs for myself for tomorrow

* java version

* java11 worked, trying 12. also actually running ganache tests

* moving comments around, removing some directory manipulation commands

* updating sbt version to remove error about log4j

* fixing some Auto-application to () is deprecated errors in the scala

* fixing a scala warning, bumping java version too

* adding a catch all case to the parser to squelch a warning

* 14 seems to be too much, lets try 13

* down to 12

* changes

* some trivial changes to trigger a fresh travis job

* Check to make sure ganache-cli and truffle are installed.
Put the output of solc in EmptyContract.evm.

* Use $TRAVIS_BUILD_DIR to find the repository directory.

* stubs

* Capture return status from individual tests and fail on any failures.

* config file to cause truffle to run evm, possibly

* stepping through emptycontract.sh locally; next thing is to figure out the docker invocation with volumes and maybe a local permission problem for me

* truffle init with fewer Ns

* removing some unused imports as an excuse to kick off a new travis job that might be less cached

* adding cached output of truffle init instead of trying to run it in the script because it is not well-behaved

* small change, new output that i want to run on travis

* scraps

* removing truffle

* progress towards running the evm bytecode directly through curl and ganache-cli

* sudo

* snap

* more changes

* ganache cli returns an error! this is wonderful!

* pulling an account off of the ganache-cli instance works

* adding more gas, bigger default balance

* localhost may not exist by default on travis ci instances

* tidying up output from emptycontract.sh a little bit, making it return an error code so that the travis build fails even if kill succeeds

* add response output

* replicated bad response locally, clearing up some possible causes, pushing to see how it runs on travis. #304

* got past the 400 locally, back to opcodes. pushing to travis to see it there. #304

* this should now (correctly) fail on travis

* pretty printing ganache response, too #304

* #304

* #304

* cleaning up comments and adding references to the tech debt issue

* cleaning up some high churn IDE files that i don't think need to be shared

* tidying up for a PR

* tidying up for a PR

* tidying up for a PR

* tidying up for a PR

* ganache test now correctly fails if one of the tests it runs fails. #302

* reverting install-protobuf

* travis build problems

* i have no idea if this will work or what changed that makes this needed

* progress on #309.

there's something happening here i don't understand. specifically when
translateContract exits, if I print the return value i get

```
YulObject(EmptyContract,Code(Block(List(ExpressionStatement(FunctionCall(Identifier(mstore),List(Literal(number,64,int),
Literal(number,128,int)))),
ExpressionStatement(FunctionCall(Identifier(return),List(Literal(number,0,int),
Literal(number,0,int))))))),List(YulObject(EmptyContract_deployed,Code(Block(List(ExpressionStatement(FunctionCall(Identifier(mstore),List(Literal(number,64,int),
Literal(number,128,int)))),
ExpressionStatement(FunctionCall(Identifier(return),List(Literal(number,0,int),
Literal(number,0,int))))))),List(),List())),List())
```

which has the `mstore` and `return` command in both blocks, but when i look
at the yul file written to disk it's missing the `mstore` command in the
`_deployed` block:

```
object "EmptyContract" {
    code {
            mstore(64,128)
            return(0,0)
    }
    object "EmptyContract_deployed" {
        code {
            return(0,0)
        }
    }
}
```

solc is happy to take this as valid Yul and produces EVM that ganache
accepts. but i don't know if this is right. i certainly intended both
commands to be in both places, but i don't really know if they should be. i
looked at the call sites for `translateContract` in `translateProgram` and
they don't seem to do anything that would change what's in
`_deployed`. There's only one call to `translateProgram` and it's right
before the file gets written to disk via `yulString` in
`YulAST.scala:83`. That leads me to `object.mustache`, but I'm not sure
that something there would be causing this error (if it is indeed an
error).

* removing print statement

* debugging path manipulations in testing script

* more descriptive comment text for polling ganache

* swapping todo and error text in parser

Co-authored-by: Michael Coblenz <mcoblenz@cs.cmu.edu>
Co-authored-by: Michael Coblenz <mcoblenz@umd.edu>
  • Loading branch information
3 people committed Apr 1, 2021
1 parent 3672347 commit bb481c2
Show file tree
Hide file tree
Showing 19 changed files with 261 additions and 116 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,13 @@ project/plugins/project/
*.proto
.idea/libraries/

.idea/modules/*.iml
.idea/modules.xml

# Gradle build script caches and whatnot
buildscript/.gradle
buildscript/build
# Vim
*.swp

.bsp
7 changes: 6 additions & 1 deletion .idea/codeStyles/Project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

88 changes: 0 additions & 88 deletions .idea/modules/Obsidian.iml

This file was deleted.

2 changes: 1 addition & 1 deletion .idea/sbt.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 14 additions & 9 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,27 +1,32 @@
dist: trusty
dist: focal
language: scala
scala:
- 2.13.2
- 2.13.5

node_js:
- 15.11.0

# This may be necessary depending on the tests we eventually run. For example,
# the chaincode base server uses "java.util.Base64", which is only supported
# as of java version 8
jdk:
- oraclejdk8
- oraclejdk12 ## as of 9 march 2021, up to jdk12 seems to work but 13/14/15 fail

before_install:
- ./travis_specific/install-protobuf.sh
- curl -sSL http://bit.ly/2ysbOFE | bash -s 1.4.1 -s
- mv bin $HOME
- mv bin/* $HOME/bin/
- export PATH=${PATH}:${HOME}/protobuf/
- export PATH=${PATH}:${HOME}/bin/

- ./travis_specific/install_ganache.sh

script:
- gradle publish -b Obsidian_Runtime/build.gradle
- sbt ++2.13.2 test
- sbt ++2.13.5 test # this actually builds Obsidian
- bash travis_specific/tests.sh
- bash travis_specific/ganache_tests.sh

cache:
directories:
- $HOME/.m2

addons:
hosts:
- localhost
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name := "Obsidian"

version := "0.1"

scalaVersion := "2.13.2"
scalaVersion := "2.13.5"

organization := "edu.cmu.cs.obsidian"

Expand Down
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=1.3.10
sbt.version=1.4.7
2 changes: 2 additions & 0 deletions resources/tests/GanacheTests/EmptyContract.obs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
main contract EmptyContract {
}
150 changes: 150 additions & 0 deletions resources/tests/GanacheTests/EmptyContract.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
#!/bin/bash

# todo these are constants that i think are likely to change per-test in
# the future; i've hoisted them up here so that they can get read in from a
# config file per test later. (issue #302)
NAME=EmptyContract
GAS=30000000 # this is a magic number (issue #302)
GAS_HEX=$(printf '%x' $GAS)
GAS_PRICE='0x9184e72a000' # this is a magic number from the YUL docs (issue #302)
START_ETH=5000000 # this is a magic number (issue #302)
NUM_ACCT=1

# check to make sure that both tools are installed, fail otherwise.
if ! hash ganache-cli
then
echo "ganache-cli is not installed, Install it with 'npm install -g ganache-cli'."
exit 1
fi

# compile the contract to yul, also creating the directory to work in
sbt "runMain edu.cmu.cs.obsidian.Main --yul resources/tests/GanacheTests/$NAME.obs"

# check to make sure that solc succeeded, failing otherwise
if [ $? -ne 0 ]; then
echo "$NAME test failed: sbt exited cannot compile obs to yul"
exit 1
fi

if [ ! -d "$NAME" ]; then
echo "$NAME directory failed to get created"
exit 1
fi

cd "$NAME"

# generate the evm from yul
echo "running solc to produce evm bytecode"
docker run -v "$( pwd -P )":/sources ethereum/solc:stable --abi --bin --strict-assembly /sources/"$NAME".yul > "$NAME".evm

# check to make sure that solc succeeded, failing otherwise
if [ $? -ne 0 ]; then
echo "$NAME test failed: solc cannot compile yul code"
exit 1
fi

# todo this is a bit of a hack. solc is supposed to output a json object
# and it just isn't. so this is grepping through to grab the right lines
# with the hex that represents the output. this likely fails if the binary
# is more than one line long. (issue #302)
TOP=`grep -n "Binary representation" $NAME.evm | cut -f1 -d:`
BOT=`grep -n "Text representation" $NAME.evm | cut -f1 -d:`
TOP=$((TOP+1)) # drop the line with the name
BOT=$((BOT-1)) # drop the empty line after the binary
EVM_BIN=`sed -n $TOP','$BOT'p' $NAME.evm`
echo "binary representation is: $EVM_BIN"

# start up ganache
echo "starting ganache-cli"
ganache-cli --gasLimit "$GAS" --accounts="$NUM_ACCT" --defaultBalanceEther="$START_ETH" &> /dev/null &

# form the JSON object to ask for the list of accounts
ACCT_DATA=$( jq -ncM \
--arg "jn" "2.0" \
--arg "mn" "eth_accounts" \
--argjson "pn" "[]" \
--arg "idn" "1" \
'{"jsonrpc":$jn,"method":$mn,"params":$pn,"id":$idn}'
)

# ganache-cli takes a little while to start up, and the first thing that we
# need from it is the list of accounts. so we poll on the account endpoint
# until we get a good result to avoid using sleep or something less precise.
echo "querying ganache-cli until accounts are available"
KEEPGOING=1
ACCTS=""
until [ "$KEEPGOING" -eq 0 ] ;
do
ACCTS=$(curl --silent -X POST --data "$ACCT_DATA" http://localhost:8545)
KEEPGOING=$?
sleep 1
done
echo

# we'll return this at the exit at the bottom of the file; TravisCI says a
# job passes or fails based on the last command run
RET=0

# todo: i'm not sure what account to mark as the "to" account. i think i
# can use that later to test the output of running more complicated
# contracts. i'll need to make more than one account when i start up
# ganache. (issue #302)
ACCT=`echo $ACCTS | jq '.result[0]' | tr -d '"'`
echo "ACCT is $ACCT"

# todo what's that 0x0 mean?
PARAMS=$( jq -ncM \
--arg "fn" "$ACCT" \
--arg "gn" "0x$GAS_HEX" \
--arg "gpn" "$GAS_PRICE" \
--arg "vn" "0x0" \
--arg "dn" "0x$EVM_BIN" \
'{"from":$fn,"gas":$gn,"gasPrice":$gpn,"value":$vn,"data":$dn}'
)

SEND_DATA=$( jq -ncM \
--arg "jn" "2.0" \
--arg "mn" "eth_sendTransaction" \
--argjson "pn" "$PARAMS" \
--arg "idn" "1" \
'{"jsonrpc":$jn,"method":$mn,"params":$pn,"id":$idn}'
)

echo "transaction being sent is given by"
echo "$SEND_DATA" # | jq -M #todo why doesn't this work on travis? also below. (issue #302)
echo

RESP=$(curl -s -X POST --data "$SEND_DATA" http://localhost:8545)
echo "response from ganache is: $RESP"
# ((echo "$RESP" | tr -d '\n') ; echo) # | jq -M # (issue #302)

# todo: this is not an exhaustive or principled way to check the output of
# curling a post. (issue #302)
if [ "$RESP" == "400 Bad Request" ]
then
echo "got a 400 bad response from ganache-cli"
exit 1
fi

ERROR=$(echo "$RESP" | tr -d '\n' | jq '.error.message')
if [ "$ERROR" != "null" ]
then
RET=1
echo "transaction produced an error: $ERROR"
fi

# todo check the result of test somehow to indicate failure or not (issue #302)

# clean up by killing ganache and the local files
# todo: make this a subroutine that can get called at any of the exits (issue #302)
echo "killing ganache-cli"
kill -9 $(lsof -t -i:8545)

# todo: for debugging it's nice to be able to look at these. maybe delete
# them by default but take a flag to keep them around. (issue #302)
rm "$NAME.yul"
rm "$NAME.evm"
cd "../"
rmdir "$NAME"

exit "$RET"
10 changes: 10 additions & 0 deletions resources/tests/GanacheTests/SimpleCall.obs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
main contract SimpleCall{
transaction incr(int x) returns int {
return 4;
}

transaction main(int x) returns int {
int y = incr (x);
return y;
}
}
5 changes: 5 additions & 0 deletions resources/tests/GanacheTests/SimpleCall.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env bash

# just a stub for now, and one that works
echo "no test written yet for SimpleCall.obs"
exit 0
3 changes: 1 addition & 2 deletions resources/tests/YulTests/EmptyContract.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,4 @@ if [ $? -ne 0 ]; then
exit 1
fi


rm -rf EmptyContract
rm -rf EmptyContract

0 comments on commit bb481c2

Please sign in to comment.