diff --git a/circle.yml b/circle.yml index dc1152dd5325ac64e2cde65e75507cf76bbd48b0..6e02174f21c2c243fb7ad974a265090e5f51a1ba 100644 --- a/circle.yml +++ b/circle.yml @@ -1,50 +1,39 @@ machine: environment: + GOPATH: $HOME/.go_workspace + REPO: ${GOPATH}/src/github.com/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME} + DOCKER_VERSION: 1.9.1 + DOCKER_MACHINE_VERSION: 0.6.0 + ERIS_CLI_BRANCH: develop GO15VENDOREXPERIMENT: 1 - # We want to work from GOPATH src - GOPATH_REPO: ${GOPATH%%:*}/src/github.com/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME} - HOME_REPO: ${HOME}/${CIRCLE_PROJECT_REPONAME} post: - - mkdir -p $GOPATH_REPO - # Will overwrite anything from cache, note trailing slash on source matters - - rsync -a --delete $HOME_REPO/ $GOPATH_REPO - - rm -rf $HOME_REPO - # Let our build_dir point a go src location - - ln -s $GOPATH_REPO $HOME_REPO - git config --global user.email "billings@erisindustries.com" - git config --global user.name "Billings the Bot" + - rm -rf ${GOPATH%%:*}/src/github.com/${CIRCLE_PROJECT_USERNAME} + - mkdir -p ${GOPATH%%:*}/src/github.com/${CIRCLE_PROJECT_USERNAME} + - cp -r ${HOME}/${CIRCLE_PROJECT_REPONAME} ${GOPATH%%:*}/src/github.com/${CIRCLE_PROJECT_USERNAME}/. dependencies: - pre: - - mkdir -p ~/cache - # Fill cache of overrides - - test -e ~/cache/docker || curl -L -o ~/cache/docker 'http://s3-external-1.amazonaws.com/circle-downloads/docker-1.9.0-circleci' - # leaving here in case we wish to override go version - # - test -e ~/cache/go || curl https://storage.googleapis.com/golang/go1.6.linux-amd64.tar.gz | tar xzC ~/cache - # - sudo rm -rf /usr/local/go - # - sudo cp -r ~/cache/go /usr/local/go - # Override host docker - - sudo cp ~/cache/docker /usr/bin/docker - - sudo chmod +x /usr/bin/docker - # Override host go + override: + - sudo curl -L -o /usr/bin/docker http://s3-external-1.amazonaws.com/circle-downloads/docker-$DOCKER_VERSION-circleci; sudo chmod 0775 /usr/bin/docker; sudo usermod -a -G docker $USER; true - sudo service docker start + # - sudo curl -sSL -o /usr/local/bin/docker-machine https://github.com/docker/machine/releases/download/v$DOCKER_MACHINE_VERSION/docker-machine-linux-x86_64; sudo chmod 0755 /usr/local/bin/docker-machine + # - "go get github.com/eris-ltd/eris-cli/cmd/eris; cd ${GOPATH%%:*}/src/github.com/eris-ltd/eris-cli && git checkout origin/$ERIS_CLI_BRANCH && go install ./cmd/eris" - "sudo apt-get update && sudo apt-get install -y libgmp3-dev" - cache_directories: - - ~/cache + # jq and curl is a dependency for the integration framework + - sudo apt-get install jq curl && go get github.com/Masterminds/glide test: pre: - - go get github.com/stretchr/testify - - go get github.com/Masterminds/glide - - cd $GOPATH_REPO && glide install - # Install erisdb cmd for tests - - cd $GOPATH_REPO && go install ./cmd/eris-db + - cd $REPO && glide install + # Test the build target for eris-db + - echo "Build target eris-db..." && cd $REPO && go install ./cmd/eris-db && eris-db --help + # Test the build target for eris-client + - echo "Build target eris-client..." && cd $REPO && go install ./client/cmd/eris-client && eris-client --help override: # We only wish to test our packages not vendored ones - - echo "Running unit tests..." - - cd $GOPATH_REPO && go test -v `glide novendor` - - echo "Running integration tests..." - - cd $GOPATH_REPO && go test -v -tags integration `glide novendor` + - echo "Running unit tests..." && cd $REPO && glide novendor | xargs go test -tags integration + # - echo "Running integration tests..." && cd $REPO && "tests/circle_test.sh" # | tee $CIRCLE_ARTIFACTS/output.log; test ${PIPESTATUS[0]} -eq 0" deployment: diff --git a/client/client.go b/client/client.go index b79d85bca9c33ea650ee64d6f51a3ca393e36b44..b1bfd146452d730b88f3a237cdf8308cdfd6e7b7 100644 --- a/client/client.go +++ b/client/client.go @@ -30,8 +30,10 @@ type NodeClient interface { Broadcast(transaction txs.Tx) (*txs.Receipt, error) Status() (ChainId []byte, ValidatorPublicKey []byte, LatestBlockHash []byte, - BlockHeight int, LatestBlockTime int64, err error) + LatestBlockHeight int, LatestBlockTime int64, err error) GetAccount(address []byte) (*acc.Account, error) + QueryContract(address, data []byte) (ret []byte, gasUsed int64, err error) + QueryContractCode(address, code, data []byte) (ret []byte, gasUsed int64, err error) } // NOTE [ben] Compiler check to ensure ErisNodeClient successfully implements @@ -88,14 +90,50 @@ func (erisNodeClient *ErisNodeClient) GetAccount(address []byte) (*acc.Account, // Status returns the ChainId (GenesisHash), validator's PublicKey, latest block hash // the block height and the latest block time. -func (erisClient *ErisNodeClient) Status() (ChainId []byte, ValidatorPublicKey []byte, LatestBlockHash []byte, - BlockHeight int, LatestBlockTime int64, err error) { - client := rpcclient.NewClientURI(erisClient.broadcastRPC) +func (erisNodeClient *ErisNodeClient) Status() (ChainId []byte, ValidatorPublicKey []byte, LatestBlockHash []byte, LatestBlockHeight int, LatestBlockTime int64, err error) { + client := rpcclient.NewClientURI(erisNodeClient.broadcastRPC) res, err := tendermint_client.Status(client) if err != nil { err = fmt.Errorf("Error connecting to node (%s) to get status: %s", - erisClient.broadcastRPC, err.Error()) - return nil, nil, nil, 0, 0, err + erisNodeClient.broadcastRPC, err.Error()) + return nil, nil, nil, int(0), int64(0), err + } + // unwrap return results + ChainId = res.GenesisHash + ValidatorPublicKey = res.PubKey.Bytes() + LatestBlockHash = res.LatestBlockHash + LatestBlockHeight = res.LatestBlockHeight + LatestBlockTime = res.LatestBlockTime + return +} + +// QueryContract executes the contract code at address with the given data +func (erisNodeClient *ErisNodeClient) QueryContract(address, data []byte) (ret []byte, gasUsed int64, err error) { + client := rpcclient.NewClientURI(erisNodeClient.broadcastRPC) + var callerAddress = make([]byte, len(address)) + var calleeAddress = make([]byte, len(address)) + copy(callerAddress, address) + copy(calleeAddress, address) + callResult, err := tendermint_client.Call(client, callerAddress, calleeAddress, data) + if err != nil { + err = fmt.Errorf("Error connnecting to node (%s) to query contract at (%X) with data (%X)", + erisNodeClient.broadcastRPC, address, data, err.Error()) + return nil, int64(0), err } - return res.GenesisHash, res.PubKey.Bytes(), res.LatestBlockHash, res.LatestBlockHeight, res.LatestBlockTime, nil + return callResult.Return, callResult.GasUsed, nil } + +// QueryContractCode executes the contract code at address with the given data but with provided code +func (erisNodeClient *ErisNodeClient) QueryContractCode(address, code, data []byte) (ret []byte, gasUsed int64, err error) { + client := rpcclient.NewClientURI(erisNodeClient.broadcastRPC) + // TODO: [ben] Call and CallCode have an inconsistent signature; it makes sense for both to only + // have a single address that is the contract to query. + callResult, err := tendermint_client.CallCode(client, address, code, data) + if err != nil { + err = fmt.Errorf("Error connnecting to node (%s) to query contract code at (%X) with data (%X) and code (%X)", + erisNodeClient.broadcastRPC, address, data, code, err.Error()) + return nil, int64(0), err + } + return callResult.Return, callResult.GasUsed, nil +} + diff --git a/client/mock/client_mock.go b/client/mock/client_mock.go index 7cfd54101e9e4431a4b0a9c93d58339e068b1426..1760f859a4fb58ebfc7e379e74af550b8613d11e 100644 --- a/client/mock/client_mock.go +++ b/client/mock/client_mock.go @@ -85,3 +85,18 @@ func (mock *MockNodeClient) Status() (ChainId []byte, LatestBlockTime = 0 return } + + +// QueryContract executes the contract code at address with the given data +func (mock *MockNodeClient) QueryContract(address, data []byte) (ret []byte, gasUsed int64, err error) { + // return zero + ret = make([]byte, 0) + return ret, 0, nil +} + +// QueryContractCode executes the contract code at address with the given data but with provided code +func (mock *MockNodeClient) QueryContractCode(address, code, data []byte) (ret []byte, gasUsed int64, err error) { + // return zero + ret = make([]byte, 0) + return ret, 0, nil +} diff --git a/tests/circle_test.sh b/tests/circle_test.sh new file mode 100755 index 0000000000000000000000000000000000000000..c545693889dc6728352191f1540881bf765c0cd6 --- /dev/null +++ b/tests/circle_test.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +# ---------------------------------------------------------- +# PURPOSE + +# This is the test manager for epm to be ran from circle ci. +# It will run the testing sequence for eris-db using docker. + +# ---------------------------------------------------------- +# REQUIREMENTS + +# docker installed locally +# docker-machine installed locally +# eris installed locally +# jq installed locally + +# ---------------------------------------------------------- +# USAGE + +# circle_test.sh + +# ---------------------------------------------------------- +# Set defaults + +uuid=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 12 | head -n 1) +machine="eris-test-edb-$uuid" +ver=$(cat version/version.go | tail -n 1 | cut -d ' ' -f 4 | tr -d '"') +start=`pwd` + +# ---------------------------------------------------------- +# Run integration tests + +tests/test_client.sh +test_exit=$? + +# ---------------------------------------------------------- +# Cleanup + +echo +echo +echo "Cleaning up" +cd $start +exit $test_exit diff --git a/tests/test_client.sh b/tests/test_client.sh new file mode 100755 index 0000000000000000000000000000000000000000..162a1a24d4cef93fe02a84ebf0ccdf1eee548cbe --- /dev/null +++ b/tests/test_client.sh @@ -0,0 +1,262 @@ +#!/usr/bin/env bash + +# Copyright 2015, 2016 Eris Industries (UK) Ltd. +# This file is part of Eris-RT + +# Eris-RT is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# Eris-RT is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with Eris-RT. If not, see <http://www.gnu.org/licenses/>. + +# ---------------------------------------------------------- +# PURPOSE + +# This is the integration test manager for eris-db. It will +# run the integration testing sequence for eris-db using docker +# and the dependent eris components within the eris platform +# for eris-db. Specifically eris-db and the eris-db client +# require a key management component for signing transactions +# and validating blocks. + +# ---------------------------------------------------------- +# REQUIREMENTS + +# eris installed locally + +# ---------------------------------------------------------- +# USAGE + +# test_client.sh + +# ---------------------------------------------------------- +# Set defaults + +# Where are the Things? + +name=eris-db +base=github.com/eris-ltd/$name +repo=`pwd` +if [ "$CIRCLE_BRANCH" ] +then + ci=true + linux=true +elif [ "$TRAVIS_BRANCH" ] +then + ci=true + osx=true +elif [ "$APPVEYOR_REPO_BRANCH" ] +then + ci=true + win=true +else + repo=$GOPATH/src/$base + ci=false +fi + +branch=${CIRCLE_BRANCH:=master} +branch=${branch/-/_} +branch=${branch/\//_} + +# Other variables +was_running=0 +test_exit=0 +chains_dir=$HOME/.eris/chains + +export ERIS_PULL_APPROVE="true" +export ERIS_MIGRATE_APPROVE="true" + +# --------------------------------------------------------------------------- +# Needed functionality + +ensure_running(){ + if [[ "$(eris services ls -qr | grep $1)" == "$1" ]] + then + echo "$1 already started. Not starting." + was_running=1 + else + echo "Starting service: $1" + eris services start $1 1>/dev/null + early_exit + sleep 3 # boot time + fi +} + +early_exit(){ + if [ $? -eq 0 ] + then + return 0 + fi + + echo "There was an error during setup; keys were not properly imported. Exiting." + if [ "$was_running" -eq 0 ] + then + if [ "$ci" = true ] + then + eris services stop keys + else + eris services stop -r keys + fi + fi + exit 1 +} + +get_uuid() { + if [[ "$(uname -s)" == "Linux" ]] + then + uuid=$(cat /proc/sys/kernel/random/uuid | tr -dc 'a-zA-Z0-9' | fold -w 12 | head -n 1) + elif [[ "$(uname -s)" == "Darwin" ]] + then + uuid=$(uuidgen | tr -dc 'a-zA-Z0-9' | fold -w 12 | head -n 1) + else + uuid="2231587f0fe5" + fi + echo $uuid +} + +test_build() { + echo "" + echo "Building eris-db in a docker container." + set -e + # tests/build_tool.sh 1>/dev/null + set +e + if [ $? -ne 0 ] + then + echo "Could not build eris-db. Debug via by directly running [`pwd`/tests/build_tool.sh]" + exit 1 + fi + echo "Build complete." + echo "" +} + +test_setup(){ + echo "Getting Setup" + if [ "$ci" = true ] + then + eris init --yes --pull-images=ERIS_PULL_APPROVE --testing=true 1>/dev/null + fi + + ensure_running keys + echo "Setup complete" +} + +start_chain(){ + echo + echo "starting new chain for client tests..." + if [ $? -ne 0 ] + then + test_exit=1 + return 1 + fi + eris chains make $uuid --account-types=Participant:2,Validator:1 + eris chains new $uuid --dir "$uuid"/"$uuid"_validator_000 + if [ $? -ne 0 ] + then + test_exit=1 + return 1 + fi + sleep 3 # let 'er boot + + # set variables for chain + CHAIN_ID=$uuid + eris_client_ip=$(eris chains inspect $uuid NetworkSettings.IPAddress) + ERIS_CLIENT_NODE_ADDRESS="tcp://$(eris chains inspect $uuid NetworkSettings.IPAddress):46657" + ERIS_CLIENT_SIGN_ADDRESS="http://$(eris services inspect keys NetworkSettings.IPAddress):4767" + echo "chainid:" $CHAIN_ID + echo "node address:" $ERIS_CLIENT_NODE_ADDRESS + echo "keys address:" $ERIS_CLIENT_SIGN_ADDRESS + + # set addresses from participants + query1=". | ."$uuid"_participant_000.address" + participant_000_address=$(cat $chains_dir/$uuid/addresses.csv | grep "participant_000" | cut -d ',' -f 1) + participant_001_address=$(cat $chains_dir/$uuid/addresses.csv | grep "participant_000" | cut -d ',' -f 1) + echo "participant0 address:" $participant_000_address + echo "participant1 address:" $participant_001_address + } + + stop_chain(){ + echo + echo "stopping test chain for client tests..." + eris chains stop --force $uuid + if [ ! "$ci" = true ] + then + eris chains rm --data $uuid + fi + rm -rf $HOME/.eris/scratch/data/$uuid + rm -rf $chains_dir/$uuid +} + +perform_client_tests(){ + uuid=$(get_uuid) + start_chain + + echo + echo "simplest client send transaction test" + amount=1000 + eris-client tx send --amt $amount --to $participant_001_address --addr $participant_000_address + sleep 5 # poll for resulting state - sleeping, rather than waiting for confirmation + sender_amt=$(curl "$eris_client_ip"/get_account?address=$participant_000_address | jq '. | .result[1].account.balance') + receiver_amt=$(curl "$eris_client_ip"/get_account?address=$participant_001_address | jq '. | .result[1].account.balance') + difference='expr $receiver_amt - $sender_amt' + if [[ "$difference" != "$amount" ]] + then + echo "simple send transaction failed" + return 1 + fi + echo + stop_chain +} + +test_teardown(){ + if [ "$ci" = false ] + then + echo + if [ "$was_running" -eq 0 ] + then + eris chains rm $uuid -xf + eris services stop -rx keys + fi + echo + fi + if [ "$test_exit" -eq 0 ] + then + echo "Tests complete! Tests are Green. :)" + else + echo "Tests complete. Tests are Red. :(" + fi + cd $start + exit $test_exit +} + +# --------------------------------------------------------------------------- +# Get the things build and dependencies turned on + +echo "Hello! I'm the marmot that tests the eris-db tooling" +start=`pwd` +cd $repo +test_setup +test_build +echo + +# --------------------------------------------------------------------------- +# Go ahead with node integration tests + +# TODO + +# --------------------------------------------------------------------------- +# Go ahead with client integration tests ! + +echo "Running Client Tests..." +perform_client_tests + +# --------------------------------------------------------------------------- +# Cleaning up + +test_teardown \ No newline at end of file