diff --git a/tests/compose.yaml b/tests/compose.yaml new file mode 100644 index 0000000..8f5723f --- /dev/null +++ b/tests/compose.yaml @@ -0,0 +1,55 @@ +version: "3" + +services: + cosmos: + image: tendermintdev/rosetta-cli:v0.6.7 + command: + [ + "simd", + "start", + "--pruning", + "nothing", + "--grpc-web.enable", + "true", + "--grpc-web.address", + "0.0.0.0:9091" + ] + ports: + - 9090:9090 + - 26657:26657 + logging: + driver: "none" + + rosetta: + image: tendermintdev/rosetta-cli:v0.6.7 + command: + [ + "simd", + "rosetta", + "--blockchain", + "app", + "--network", + "network", + "--tendermint", + "cosmos:26657", + "--grpc", + "cosmos:9090", + "--addr", + ":8080" + ] + ports: + - 8080:8080 + + faucet: + image: tendermintdev/rosetta-cli:v0.6.7 + working_dir: /rosetta + command: ["python3", "./config/faucet.py"] + expose: + - 8080 + + test_rosetta: + image: tendermintdev/rosetta-cli:v0.6.7 + volumes: + - ./config:/rosetta/config:z + command: ["./config/run_tests.sh"] + working_dir: /rosetta \ No newline at end of file diff --git a/tests/config/bootstrap.json b/tests/config/bootstrap.json new file mode 100644 index 0000000..ddf5041 --- /dev/null +++ b/tests/config/bootstrap.json @@ -0,0 +1,12 @@ +[ + { + "account_identifier": { + "address":"cosmos1f3d3s7jjy5zune554w7fnhrhyuxhll7s7rps0h" + }, + "currency":{ + "symbol":"stake", + "decimals":0 + }, + "value": "999990000000" + } +] \ No newline at end of file diff --git a/tests/config/data.sh b/tests/config/data.sh new file mode 100644 index 0000000..d1822e8 --- /dev/null +++ b/tests/config/data.sh @@ -0,0 +1,59 @@ +#!/bin/sh + +set -e + +wait_simd() { + timeout 30 sh -c 'until nc -z $0 $1; do sleep 1; done' localhost 9090 +} +# this script is used to recreate the data dir +echo clearing /root/.simapp +rm -rf /root/.simapp +echo initting new chain +# init config files +simd init simd --chain-id testing + +# create accounts +simd keys add fd --keyring-backend=test + +addr=$(simd keys show fd -a --keyring-backend=test) +val_addr=$(simd keys show fd --keyring-backend=test --bech val -a) + +# give the accounts some money +simd add-genesis-account "$addr" 1000000000000stake --keyring-backend=test + +# save config for the daemon +simd gentx fd 10000000stake --chain-id testing --keyring-backend=test + +# input genTx to the genesis file +simd collect-gentxs +# verify genesis file is fine +simd validate-genesis +echo changing network settings +sed -i 's/127.0.0.1/0.0.0.0/g' /root/.simapp/config/config.toml + +# start simd +echo starting simd... +simd start --pruning=nothing & +pid=$! +echo simd started with PID $pid + +echo awaiting for simd to be ready +wait_simd +echo simd is ready +sleep 10 + + +# send transaction to deterministic address +echo sending transaction with addr $addr +simd tx bank send "$addr" cosmos19g9cm8ymzchq2qkcdv3zgqtwayj9asv3hjv5u5 100stake --yes --keyring-backend=test --broadcast-mode=block --chain-id=testing + +sleep 10 + +echo stopping simd... +kill -9 $pid + +echo zipping data dir and saving to /tmp/data.tar.gz + +tar -czvf /tmp/data.tar.gz /root/.simapp + +echo new address for bootstrap.json "$addr" "$val_addr" \ No newline at end of file diff --git a/tests/config/faucet.py b/tests/config/faucet.py new file mode 100644 index 0000000..7bdc6d8 --- /dev/null +++ b/tests/config/faucet.py @@ -0,0 +1,23 @@ +from http.server import HTTPServer, BaseHTTPRequestHandler +import subprocess + +import os + +class SimpleHTTPRequestHandler(BaseHTTPRequestHandler): + def do_POST(self): + try: + content_len = int(self.headers.get('Content-Length')) + addr = self.rfile.read(content_len).decode("utf-8") + print("sending funds to " + addr) + subprocess.call(['sh', './send_funds.sh', addr]) + self.send_response(200) + self.end_headers() + except Exception as e: + print("failed " + str(e)) + os._exit(1) + + +if __name__ == "__main__": + print("starting faucet server...") + httpd = HTTPServer(('0.0.0.0', 8000), SimpleHTTPRequestHandler) + httpd.serve_forever() \ No newline at end of file diff --git a/configs/rosetta-config-cli.json b/tests/config/rosetta-config-cli.json similarity index 100% rename from configs/rosetta-config-cli.json rename to tests/config/rosetta-config-cli.json diff --git a/tests/config/rosetta.json b/tests/config/rosetta.json new file mode 100644 index 0000000..b4adc6a --- /dev/null +++ b/tests/config/rosetta.json @@ -0,0 +1,51 @@ +{ + "network": { + "blockchain": "app", + "network": "network" + }, + "online_url": "http://rosetta:8080", + "data_directory": "", + "http_timeout": 300, + "max_retries": 5, + "retry_elapsed_time": 0, + "max_online_connections": 0, + "max_sync_concurrency": 0, + "tip_delay": 60, + "log_configuration": true, + "construction": { + "offline_url": "http://rosetta:8080", + "max_offline_connections": 0, + "stale_depth": 0, + "broadcast_limit": 0, + "ignore_broadcast_failures": false, + "clear_broadcasts": false, + "broadcast_behind_tip": false, + "block_broadcast_limit": 0, + "rebroadcast_all": false, + "constructor_dsl_file": "transfer.ros", + "end_conditions": { + "create_account": 1, + "transfer": 1 + } + }, + "data": { + "active_reconciliation_concurrency": 0, + "inactive_reconciliation_concurrency": 0, + "inactive_reconciliation_frequency": 0, + "log_blocks": false, + "log_transactions": false, + "log_balance_changes": false, + "log_reconciliations": false, + "ignore_reconciliation_error": false, + "exempt_accounts": "", + "bootstrap_balances": "bootstrap.json", + "interesting_accounts": "", + "reconciliation_disabled": false, + "inactive_discrepency_search_disabled": false, + "balance_tracking_disabled": false, + "coin_tracking_disabled": false, + "end_conditions": { + "tip": true + } + } +} \ No newline at end of file diff --git a/tests/config/run_tests.sh b/tests/config/run_tests.sh new file mode 100644 index 0000000..cf9aef5 --- /dev/null +++ b/tests/config/run_tests.sh @@ -0,0 +1,17 @@ +#!/bin/sh + + +set -e + +wait_for_rosetta() { + timeout 30 sh -c 'until nc -z $0 $1; do sleep 1; done' rosetta 8080 +} + +echo "waiting for rosetta instance to be up" +wait_for_rosetta + +echo "checking data API" +rosetta-cli check:data --configuration-file ./config/rosetta.json + +echo "checking construction API" +rosetta-cli check:construction --configuration-file ./config/rosetta.json \ No newline at end of file diff --git a/tests/config/send_funds.sh b/tests/config/send_funds.sh new file mode 100644 index 0000000..3a89753 --- /dev/null +++ b/tests/config/send_funds.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +set -e +addr=$(simd keys show fd -a --keyring-backend=test) +echo "12345678" | simd tx bank send "$addr" "$1" 100stake --chain-id="testing" --node tcp://cosmos:26657 --yes --keyring-backend=test \ No newline at end of file diff --git a/tests/config/transfer.ros b/tests/config/transfer.ros new file mode 100644 index 0000000..f16113f --- /dev/null +++ b/tests/config/transfer.ros @@ -0,0 +1,105 @@ +request_funds(1){ + find_account{ + currency = {"symbol":"stake", "decimals":0}; + random_account = find_balance({ + "minimum_balance":{ + "value": "0", + "currency": {{currency}} + }, + "create_limit":1 + }); + }, + send_funds{ + account_identifier = {{random_account.account_identifier}}; + address = {{account_identifier.address}}; + idk = http_request({ + "method": "POST", + "url": "http:\/\/faucet:8000", + "timeout": 10, + "body": {{random_account.account_identifier.address}} + }); + }, + // Create a separate scenario to request funds so that + // the address we are using to request funds does not + // get rolled back if funds do not yet exist. + request{ + loaded_account = find_balance({ + "account_identifier": {{random_account.account_identifier}}, + "minimum_balance":{ + "value": "50", + "currency": {{currency}} + } + }); + } +} +create_account(1){ + create{ + network = {"network":"network", "blockchain":"app"}; + key = generate_key({"curve_type": "secp256k1"}); + account = derive({ + "network_identifier": {{network}}, + "public_key": {{key.public_key}} + }); + // If the account is not saved, the key will be lost! + save_account({ + "account_identifier": {{account.account_identifier}}, + "keypair": {{key}} + }); + } +} +transfer(3){ + transfer{ + transfer.network = {"network":"network", "blockchain":"app"}; + currency = {"symbol":"stake", "decimals":0}; + sender = find_balance({ + "minimum_balance":{ + "value": "100", + "currency": {{currency}} + } + }); + acc_identifier = {{sender.account_identifier}}; + sender_address = {{acc_identifier.address}}; + // Set the recipient_amount as some value <= sender.balance-max_fee + max_fee = "0"; + fee_amount = "1"; + fee_value = 0 - {{fee_amount}}; + available_amount = {{sender.balance.value}} - {{max_fee}}; + recipient_amount = random_number({"minimum": "1", "maximum": {{available_amount}}}); + print_message({"recipient_amount":{{recipient_amount}}}); + // Find recipient and construct operations + sender_amount = 0 - {{recipient_amount}}; + recipient = find_balance({ + "not_account_identifier":[{{sender.account_identifier}}], + "minimum_balance":{ + "value": "0", + "currency": {{currency}} + }, + "create_limit": 100, + "create_probability": 50 + }); + transfer.confirmation_depth = "1"; + recipient_account_identifier = {{recipient.account_identifier}}; + recipient_address = {{recipient_account_identifier.address}}; + transfer.operations = [ + { + "operation_identifier":{"index":0}, + "type":"/cosmos.bank.v1beta1.MsgSend", + "account":{{sender.account_identifier}}, + "metadata": { + "amount": [ + { + "amount": {{recipient_amount}}, + "denom": {{currency.symbol}} + } + ], + "from_address": {{sender_address}}, + "to_address": {{recipient_address}} + } + } + ]; + transfer.preprocess_metadata = { + "gas_price": "1stake", + "gas_limit": 250000 + }; + } +} \ No newline at end of file diff --git a/tests/rosetta-ci/Dockerfile b/tests/rosetta-ci/Dockerfile new file mode 100644 index 0000000..4a83b47 --- /dev/null +++ b/tests/rosetta-ci/Dockerfile @@ -0,0 +1,31 @@ +FROM golang:1.19-alpine as build + +RUN apk add --no-cache tar git + +# prepare node data +WORKDIR /node +COPY ./contrib/rosetta/rosetta-ci/data.tar.gz data.tar.gz +RUN tar -zxvf data.tar.gz -C . + +# build simd +WORKDIR /simd +COPY . ./ +RUN go build -o simd ./simapp/simd/ + +FROM alpine +RUN apk add gcc git libc-dev python3 --no-cache + +ENV PATH=$PATH:/bin + +COPY --from=build /simd/simd /bin/simd + +WORKDIR /rosetta +COPY ./contrib/rosetta/configuration ./ +RUN chmod +x run_tests.sh +RUN chmod +x send_funds.sh +RUN chmod +x faucet.py + +COPY --from=build /node/root /root/ +WORKDIR /root/.simapp + +RUN chmod -R 0777 ./ \ No newline at end of file diff --git a/tests/rosetta-cli/Dockerfile b/tests/rosetta-cli/Dockerfile new file mode 100644 index 0000000..3bbec3f --- /dev/null +++ b/tests/rosetta-cli/Dockerfile @@ -0,0 +1,18 @@ +FROM golang:1.19-alpine as build + +RUN apk add git gcc libc-dev --no-cache + +ARG ROSETTA_VERSION="v0.7.3" + +# build rosetta CLI +WORKDIR /rosetta +RUN git clone https://github.com/coinbase/rosetta-cli . +RUN git checkout tags/$ROSETTA_VERSION +RUN go build -o rosetta-cli ./main.go + +FROM alpine +RUN apk add gcc libc-dev python3 --no-cache + +ENV PATH=$PATH:/bin + +COPY --from=build /rosetta/rosetta-cli /bin/rosetta-cli \ No newline at end of file