Rosetta API implementation (#7695)
Ref: #7492 Co-authored-by: Jonathan Gimeno <jgimeno@gmail.com> Co-authored-by: Alessio Treglia <alessio@tendermint.com> Co-authored-by: Frojdi Dymylja <33157909+fdymylja@users.noreply.github.com> Co-authored-by: Robert Zaremba <robert@zaremba.ch> Co-authored-by: Federico Kunze <31522760+fedekunze@users.noreply.github.com>
This commit is contained in:
parent
d226254578
commit
57f5e96570
17
.github/workflows/test.yml
vendored
17
.github/workflows/test.yml
vendored
@ -188,6 +188,23 @@ jobs:
|
||||
name: "${{ github.sha }}-${{ matrix.part }}-race-output"
|
||||
path: ./${{ matrix.part }}-race-output.txt
|
||||
|
||||
test-rosetta:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: technote-space/get-diff-action@v4
|
||||
id: git_diff
|
||||
with:
|
||||
PATTERNS: |
|
||||
**/**.go
|
||||
go.mod
|
||||
go.sum
|
||||
- name: test rosetta
|
||||
run: |
|
||||
make test-rosetta
|
||||
# if: env.GIT_DIFF
|
||||
|
||||
race-detector-report:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [test-race, install-tparse]
|
||||
|
||||
17
Makefile
17
Makefile
@ -309,6 +309,11 @@ test-cover:
|
||||
@export VERSION=$(VERSION); bash -x contrib/test_cover.sh
|
||||
.PHONY: test-cover
|
||||
|
||||
test-rosetta:
|
||||
docker build -t rosetta-ci:latest -f contrib/rosetta/node/Dockerfile .
|
||||
docker-compose -f contrib/rosetta/docker-compose.yaml up --abort-on-container-exit --exit-code-from test_rosetta --build
|
||||
.PHONY: test-rosetta
|
||||
|
||||
benchmark:
|
||||
@go test -mod=readonly -bench=. $(PACKAGES_NOSIMULATION)
|
||||
.PHONY: benchmark
|
||||
@ -463,3 +468,15 @@ localnet-stop:
|
||||
docker-compose down
|
||||
|
||||
.PHONY: localnet-start localnet-stop
|
||||
|
||||
###############################################################################
|
||||
### rosetta ###
|
||||
###############################################################################
|
||||
# builds rosetta test data dir
|
||||
rosetta-data:
|
||||
-docker container rm data_dir_build
|
||||
docker build -t rosetta-ci:latest -f contrib/rosetta/node/Dockerfile .
|
||||
docker run --name data_dir_build -t rosetta-ci:latest sh /rosetta/data.sh
|
||||
docker cp data_dir_build:/tmp/data.tar.gz "$(CURDIR)/contrib/rosetta/node/data.tar.gz"
|
||||
docker container rm data_dir_build
|
||||
.PHONY: rosetta-data
|
||||
24
contrib/rosetta/README.md
Normal file
24
contrib/rosetta/README.md
Normal file
@ -0,0 +1,24 @@
|
||||
# rosetta
|
||||
|
||||
This directory contains the files required to run the rosetta CI. It builds `simapp` based on the current codebase.
|
||||
|
||||
## docker-compose.yaml
|
||||
|
||||
Builds:
|
||||
- cosmos-sdk simapp node, with prefixed data directory, keys etc. This is required to test historical balances.
|
||||
- faucet is required so we can test construction API, it was literally impossible to put there a deterministic address to request funds for
|
||||
- rosetta is the rosetta node used by rosetta-cli to interact with the cosmos-sdk app
|
||||
- test_rosetta runs the rosetta-cli test against construction API and data API
|
||||
|
||||
## configuration
|
||||
|
||||
Contains the required files to set up rosetta cli and make it work against its workflows
|
||||
|
||||
## node
|
||||
|
||||
Contains the files for a deterministic network, with fixed keys and some actions on there, to test parsing of msgs and historical balances.
|
||||
|
||||
## Notes
|
||||
|
||||
- Keyring password is 12345678
|
||||
- data.sh creates node data, it's required in case consensus breaking changes are made to quickly recreate replicable node data for rosetta
|
||||
12
contrib/rosetta/configuration/bootstrap.json
Normal file
12
contrib/rosetta/configuration/bootstrap.json
Normal file
@ -0,0 +1,12 @@
|
||||
[
|
||||
{
|
||||
"account_identifier": {
|
||||
"address":"cosmos1hdmjfmqmf8ck4pv4evu0s3up0ucm0yjjqfl87e"
|
||||
},
|
||||
"currency":{
|
||||
"symbol":"stake",
|
||||
"decimals":0
|
||||
},
|
||||
"value": "999900000000"
|
||||
}
|
||||
]
|
||||
58
contrib/rosetta/configuration/data.sh
Normal file
58
contrib/rosetta/configuration/data.sh
Normal file
@ -0,0 +1,58 @@
|
||||
#!/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)
|
||||
|
||||
# give the accounts some money
|
||||
simd add-genesis-account "$addr" 1000000000000stake --keyring-backend=test
|
||||
|
||||
# save configs for the daemon
|
||||
simd gentx fd --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" cosmos1wjmt63j4fv9nqda92nsrp2jp2vsukcke4va3pt 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"
|
||||
25
contrib/rosetta/configuration/faucet.py
Normal file
25
contrib/rosetta/configuration/faucet.py
Normal file
@ -0,0 +1,25 @@
|
||||
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()
|
||||
51
contrib/rosetta/configuration/rosetta.json
Normal file
51
contrib/rosetta/configuration/rosetta.json
Normal file
@ -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": 3
|
||||
}
|
||||
},
|
||||
"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
|
||||
}
|
||||
}
|
||||
}
|
||||
29
contrib/rosetta/configuration/run_tests.sh
Executable file
29
contrib/rosetta/configuration/run_tests.sh
Executable file
@ -0,0 +1,29 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
addr="abcd"
|
||||
|
||||
send_tx() {
|
||||
echo '12345678' | simd tx bank send $addr "$1" "$2"
|
||||
}
|
||||
|
||||
detect_account() {
|
||||
line=$1
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
echo "checking staking API"
|
||||
rosetta-cli check:construction --configuration-file ./config/staking.json
|
||||
5
contrib/rosetta/configuration/send_funds.sh
Normal file
5
contrib/rosetta/configuration/send_funds.sh
Normal file
@ -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
|
||||
30
contrib/rosetta/configuration/staking.json
Normal file
30
contrib/rosetta/configuration/staking.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"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": "staking.ros",
|
||||
"end_conditions": {
|
||||
"staking": 3
|
||||
}
|
||||
}
|
||||
}
|
||||
147
contrib/rosetta/configuration/staking.ros
Normal file
147
contrib/rosetta/configuration/staking.ros
Normal file
@ -0,0 +1,147 @@
|
||||
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": "100",
|
||||
"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}}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
staking(1){
|
||||
stake{
|
||||
stake.network = {"network":"network", "blockchain":"app"};
|
||||
currency = {"symbol":"stake", "decimals":0};
|
||||
sender = find_balance({
|
||||
"minimum_balance":{
|
||||
"value": "100",
|
||||
"currency": {{currency}}
|
||||
}
|
||||
});
|
||||
// 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 = "1";
|
||||
print_message({"recipient_amount":{{recipient_amount}}});
|
||||
// Find recipient and construct operations
|
||||
recipient = {{sender.account_identifier}};
|
||||
sender_amount = 0 - {{recipient_amount}};
|
||||
stake.confirmation_depth = "1";
|
||||
stake.operations = [
|
||||
{
|
||||
"operation_identifier":{"index":0},
|
||||
"type":"fee",
|
||||
"account":{{sender.account_identifier}},
|
||||
"amount":{
|
||||
"value":{{fee_value}},
|
||||
"currency":{{currency}}
|
||||
}
|
||||
},
|
||||
{
|
||||
"operation_identifier":{"index":1},
|
||||
"type":"cosmos.staking.v1beta1.MsgDelegate",
|
||||
"account":{{sender.account_identifier}},
|
||||
"amount":{
|
||||
"value":{{sender_amount}},
|
||||
"currency":{{currency}}
|
||||
}
|
||||
},
|
||||
{
|
||||
"operation_identifier":{"index":2},
|
||||
"type":"cosmos.staking.v1beta1.MsgDelegate",
|
||||
"account": {
|
||||
"address": "staking_account",
|
||||
"sub_account": {
|
||||
"address" : "cosmosvaloper1hdmjfmqmf8ck4pv4evu0s3up0ucm0yjj9atjj2"
|
||||
}
|
||||
},
|
||||
"amount":{
|
||||
"value":{{recipient_amount}},
|
||||
"currency":{{currency}}
|
||||
}
|
||||
}
|
||||
];
|
||||
},
|
||||
undelegate{
|
||||
print_message({"undelegate":{{sender}}});
|
||||
|
||||
undelegate.network = {"network":"network", "blockchain":"app"};
|
||||
undelegate.confirmation_depth = "1";
|
||||
undelegate.operations = [
|
||||
{
|
||||
"operation_identifier":{"index":0},
|
||||
"type":"fee",
|
||||
"account":{{sender.account_identifier}},
|
||||
"amount":{
|
||||
"value":{{fee_value}},
|
||||
"currency":{{currency}}
|
||||
}
|
||||
},
|
||||
{
|
||||
"operation_identifier":{"index":1},
|
||||
"type":"cosmos.staking.v1beta1.MsgUndelegate",
|
||||
"account":{{sender.account_identifier}},
|
||||
"amount":{
|
||||
"value":{{recipient_amount}},
|
||||
"currency":{{currency}}
|
||||
}
|
||||
},
|
||||
{
|
||||
"operation_identifier":{"index":2},
|
||||
"type":"cosmos.staking.v1beta1.MsgUndelegate",
|
||||
"account": {
|
||||
"address": "staking_account",
|
||||
"sub_account": {
|
||||
"address" : "cosmosvaloper1hdmjfmqmf8ck4pv4evu0s3up0ucm0yjj9atjj2"
|
||||
}
|
||||
},
|
||||
"amount":{
|
||||
"value":{{sender_amount}},
|
||||
"currency":{{currency}}
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
109
contrib/rosetta/configuration/transfer.ros
Normal file
109
contrib/rosetta/configuration/transfer.ros
Normal file
@ -0,0 +1,109 @@
|
||||
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": "100",
|
||||
"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}}
|
||||
}
|
||||
});
|
||||
// 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";
|
||||
transfer.operations = [
|
||||
{
|
||||
"operation_identifier":{"index":0},
|
||||
"type":"fee",
|
||||
"account":{{sender.account_identifier}},
|
||||
"amount":{
|
||||
"value":{{fee_value}},
|
||||
"currency":{{currency}}
|
||||
}
|
||||
},
|
||||
{
|
||||
"operation_identifier":{"index":1},
|
||||
"type":"cosmos.bank.v1beta1.MsgSend",
|
||||
"account":{{sender.account_identifier}},
|
||||
"amount":{
|
||||
"value":{{sender_amount}},
|
||||
"currency":{{currency}}
|
||||
}
|
||||
},
|
||||
{
|
||||
"operation_identifier":{"index":2},
|
||||
"type":"cosmos.bank.v1beta1.MsgSend",
|
||||
"account":{{recipient.account_identifier}},
|
||||
"amount":{
|
||||
"value":{{recipient_amount}},
|
||||
"currency":{{currency}}
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
39
contrib/rosetta/docker-compose.yaml
Normal file
39
contrib/rosetta/docker-compose.yaml
Normal file
@ -0,0 +1,39 @@
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
cosmos:
|
||||
image: rosetta-ci:latest
|
||||
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: rosetta-ci:latest
|
||||
command: [
|
||||
"simd",
|
||||
"rosetta",
|
||||
"--blockchain", "app",
|
||||
"--network", "network",
|
||||
"--tendermint", "cosmos:26657",
|
||||
"--grpc", "cosmos:9090",
|
||||
"--addr", ":8080",
|
||||
]
|
||||
ports:
|
||||
- 8080:8080
|
||||
|
||||
faucet:
|
||||
image: rosetta-ci:latest
|
||||
working_dir: /rosetta
|
||||
command: ["python3", "faucet.py"]
|
||||
expose:
|
||||
- 8080
|
||||
|
||||
test_rosetta:
|
||||
image: tendermintdev/rosetta-cli:v0.6.6
|
||||
volumes:
|
||||
- ./configuration:/rosetta/config:z
|
||||
command: ["./config/run_tests.sh"]
|
||||
working_dir: /rosetta
|
||||
32
contrib/rosetta/node/Dockerfile
Normal file
32
contrib/rosetta/node/Dockerfile
Normal file
@ -0,0 +1,32 @@
|
||||
FROM golang:1.15-alpine as build
|
||||
|
||||
RUN apk add --no-cache tar
|
||||
|
||||
# prepare node data
|
||||
WORKDIR /node
|
||||
COPY ./contrib/rosetta/node/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 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 ./
|
||||
|
||||
BIN
contrib/rosetta/node/data.tar.gz
Normal file
BIN
contrib/rosetta/node/data.tar.gz
Normal file
Binary file not shown.
18
contrib/rosetta/rosetta-cli/Dockerfile
Normal file
18
contrib/rosetta/rosetta-cli/Dockerfile
Normal file
@ -0,0 +1,18 @@
|
||||
FROM golang:1.15-alpine as build
|
||||
|
||||
RUN apk add git gcc libc-dev --no-cache
|
||||
|
||||
ARG ROSETTA_VERSION="v0.5.23"
|
||||
|
||||
# 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
|
||||
@ -5,6 +5,7 @@
|
||||
- Jonathan Gimeno (@jgimeno)
|
||||
- David Grierson (@senormonito)
|
||||
- Alessio Treglia (@alessio)
|
||||
- Frojdy Dymylja (@fdymylja)
|
||||
|
||||
## Context
|
||||
|
||||
@ -35,9 +36,11 @@ We will achieve these delivering on these principles by the following:
|
||||
|
||||
1. There will be an external repo called [cosmos-rosetta-gateway](https://github.com/tendermint/cosmos-rosetta-gateway)
|
||||
for the implementation of the core Rosetta API features, particularly:
|
||||
a. The types and interfaces. This separates design from implementation detail.
|
||||
b. Some core implementations: specifically, the `Service` functionality as this is independent of the Cosmos SDK version.
|
||||
2. Due to differences between the Cosmos release series, each series will have its own specific API implementations of `Network` struct and `Adapter` interface.
|
||||
a. The types and interfaces (`Client`, `OfflineClient`...), this separates design from implementation detail.
|
||||
b. The `Server` functionality as this is independent of the Cosmos SDK version.
|
||||
c. The `Online/OfflineNetwork`, which is not exported, and implements the rosetta API using the `Client` interface to query the node, build tx and so on.
|
||||
d. The `errors` package to extend rosetta errors.
|
||||
2. Due to differences between the Cosmos release series, each series will have its own specific implementation of `Client` interface.
|
||||
3. There will be two options for starting an API service in applications:
|
||||
a. API shares the application process
|
||||
b. API-specific process.
|
||||
@ -49,143 +52,130 @@ We will achieve these delivering on these principles by the following:
|
||||
|
||||
As section will describe the proposed external library, including the service implementation, plus the defined types and interfaces.
|
||||
|
||||
#### Service
|
||||
#### Server
|
||||
|
||||
`Service` is a simple `struct` that is started and listens to the port specified in the options. This is meant to be used across all the Cosmos SDK versions that are actively supported.
|
||||
`Server` is a simple `struct` that is started and listens to the port specified in the settings. This is meant to be used across all the Cosmos SDK versions that are actively supported.
|
||||
|
||||
The constructor follows:
|
||||
|
||||
`func New(options Options, network Network) (*Service, error)`
|
||||
`func NewServer(settings Settings) (Server, error)`
|
||||
|
||||
`Settings`, which are used to construct a new server, are the following:
|
||||
```go
|
||||
// Settings define the rosetta server settings
|
||||
type Settings struct {
|
||||
// Network contains the information regarding the network
|
||||
Network *types.NetworkIdentifier
|
||||
// Client is the online API handler
|
||||
Client crgtypes.Client
|
||||
// Listen is the address the handler will listen at
|
||||
Listen string
|
||||
// Offline defines if the rosetta service should be exposed in offline mode
|
||||
Offline bool
|
||||
// Retries is the number of readiness checks that will be attempted when instantiating the handler
|
||||
// valid only for online API
|
||||
Retries int
|
||||
// RetryWait is the time that will be waited between retries
|
||||
RetryWait time.Duration
|
||||
}
|
||||
```
|
||||
|
||||
#### Types
|
||||
|
||||
`Service` accepts an `Options` `struct` that holds service configuration values, such as the port the service would be listening to:
|
||||
Package types uses a mixture of rosetta types and custom defined type wrappers, that the client must parse and return while executing operations.
|
||||
|
||||
```golang
|
||||
type Options struct {
|
||||
ListenAddress string
|
||||
|
||||
##### Interfaces
|
||||
|
||||
Every SDK version uses a different format to connect (rpc, gRPC, etc), query and build transactions, we have abstracted this in what is the `Client` interface.
|
||||
The client uses rosetta types, whilst the `Online/OfflineNetwork` takes care of returning correctly parsed rosetta responses and errors.
|
||||
|
||||
Each Cosmos SDK release series will have their own `Client` implementations.
|
||||
Developers can implement their own custom `Client`s as required.
|
||||
|
||||
```go
|
||||
// Client defines the API the client implementation should provide.
|
||||
type Client interface {
|
||||
// Needed if the client needs to perform some action before connecting.
|
||||
Bootstrap() error
|
||||
// Ready checks if the servicer constraints for queries are satisfied
|
||||
// for example the node might still not be ready, it's useful in process
|
||||
// when the rosetta instance might come up before the node itself
|
||||
// the servicer must return nil if the node is ready
|
||||
Ready() error
|
||||
|
||||
// Data API
|
||||
|
||||
// Balances fetches the balance of the given address
|
||||
// if height is not nil, then the balance will be displayed
|
||||
// at the provided height, otherwise last block balance will be returned
|
||||
Balances(ctx context.Context, addr string, height *int64) ([]*types.Amount, error)
|
||||
// BlockByHashAlt gets a block and its transaction at the provided height
|
||||
BlockByHash(ctx context.Context, hash string) (BlockResponse, error)
|
||||
// BlockByHeightAlt gets a block given its height, if height is nil then last block is returned
|
||||
BlockByHeight(ctx context.Context, height *int64) (BlockResponse, error)
|
||||
// BlockTransactionsByHash gets the block, parent block and transactions
|
||||
// given the block hash.
|
||||
BlockTransactionsByHash(ctx context.Context, hash string) (BlockTransactionsResponse, error)
|
||||
// BlockTransactionsByHash gets the block, parent block and transactions
|
||||
// given the block hash.
|
||||
BlockTransactionsByHeight(ctx context.Context, height *int64) (BlockTransactionsResponse, error)
|
||||
// GetTx gets a transaction given its hash
|
||||
GetTx(ctx context.Context, hash string) (*types.Transaction, error)
|
||||
// GetUnconfirmedTx gets an unconfirmed Tx given its hash
|
||||
// NOTE(fdymylja): NOT IMPLEMENTED YET!
|
||||
GetUnconfirmedTx(ctx context.Context, hash string) (*types.Transaction, error)
|
||||
// Mempool returns the list of the current non confirmed transactions
|
||||
Mempool(ctx context.Context) ([]*types.TransactionIdentifier, error)
|
||||
// Peers gets the peers currently connected to the node
|
||||
Peers(ctx context.Context) ([]*types.Peer, error)
|
||||
// Status returns the node status, such as sync data, version etc
|
||||
Status(ctx context.Context) (*types.SyncStatus, error)
|
||||
|
||||
// Construction API
|
||||
|
||||
// PostTx posts txBytes to the node and returns the transaction identifier plus metadata related
|
||||
// to the transaction itself.
|
||||
PostTx(txBytes []byte) (res *types.TransactionIdentifier, meta map[string]interface{}, err error)
|
||||
// ConstructionMetadataFromOptions
|
||||
ConstructionMetadataFromOptions(ctx context.Context, options map[string]interface{}) (meta map[string]interface{}, err error)
|
||||
OfflineClient
|
||||
}
|
||||
|
||||
// OfflineClient defines the functionalities supported without having access to the node
|
||||
type OfflineClient interface {
|
||||
NetworkInformationProvider
|
||||
// SignedTx returns the signed transaction given the tx bytes (msgs) plus the signatures
|
||||
SignedTx(ctx context.Context, txBytes []byte, sigs []*types.Signature) (signedTxBytes []byte, err error)
|
||||
// TxOperationsAndSignersAccountIdentifiers returns the operations related to a transaction and the account
|
||||
// identifiers if the transaction is signed
|
||||
TxOperationsAndSignersAccountIdentifiers(signed bool, hexBytes []byte) (ops []*types.Operation, signers []*types.AccountIdentifier, err error)
|
||||
// ConstructionPayload returns the construction payload given the request
|
||||
ConstructionPayload(ctx context.Context, req *types.ConstructionPayloadsRequest) (resp *types.ConstructionPayloadsResponse, err error)
|
||||
// PreprocessOperationsToOptions returns the options given the preprocess operations
|
||||
PreprocessOperationsToOptions(ctx context.Context, req *types.ConstructionPreprocessRequest) (options map[string]interface{}, err error)
|
||||
// AccountIdentifierFromPublicKey returns the account identifier given the public key
|
||||
AccountIdentifierFromPublicKey(pubKey *types.PublicKey) (*types.AccountIdentifier, error)
|
||||
}
|
||||
```
|
||||
|
||||
The `Network` type holds network-specific properties (i.e. configuration values) and adapters. Pre-configured concrete types will be available for each Cosmos SDK release. Applications can also create their own custom types.
|
||||
|
||||
```golang
|
||||
type Network struct {
|
||||
Properties rosetta.NetworkProperties
|
||||
Adapter rosetta.Adapter
|
||||
}
|
||||
```
|
||||
|
||||
A `NetworkProperties` `struct` comprises basic values that are required by a Rosetta API `Service`:
|
||||
|
||||
```golang
|
||||
type NetworkProperties struct {
|
||||
// Mandatory properties
|
||||
Blockchain string
|
||||
Network string
|
||||
SupportedOperations []string
|
||||
}
|
||||
```
|
||||
|
||||
Rosetta API services use `Blockchain` and `Network` as identifiers, e.g. the developers of _gaia_, the application that powers the Cosmos Hub, may want to set those to `Cosmos Hub` and `cosmos-hub-3` respectively.
|
||||
|
||||
`SupportedOperations` contains the transaction types that are supported by the library. At the present time,
|
||||
only `cosmos-sdk/MsgSend` is supported in Launchpad. Additional operations will be added in due time.
|
||||
|
||||
For Launchpad we will map the amino type name to the operation supported, in Stargate we will use the protoc one.
|
||||
|
||||
#### Interfaces
|
||||
|
||||
Every SDK version uses a different format to connect (rpc, gRpc, etc), we have abstracted this in what is called the
|
||||
Adapter. This is an interface that defines the methods an adapter implementation must provide in order to be used
|
||||
in the `Network` interface.
|
||||
|
||||
Each Cosmos SDK release series will have their own Adapter implementations.
|
||||
Developers can implement their own custom adapters as required.
|
||||
|
||||
```golang
|
||||
type Adapter interface {
|
||||
DataAPI
|
||||
ConstructionAPI
|
||||
}
|
||||
|
||||
type DataAPI interface {
|
||||
server.NetworkAPIServicer
|
||||
server.AccountAPIServicer
|
||||
server.MempoolAPIServicer
|
||||
server.BlockAPIServicer
|
||||
server.ConstructionAPIServicer
|
||||
}
|
||||
|
||||
type ConstructionAPI interface {
|
||||
server.ConstructionAPIServicer
|
||||
}
|
||||
```
|
||||
|
||||
Example in pseudo-code of an Adapter interface:
|
||||
|
||||
```golang
|
||||
type SomeAdapter struct {
|
||||
cosmosClient client
|
||||
tendermintClient client
|
||||
}
|
||||
|
||||
func NewSomeAdapter(cosmosClient client, tendermintClient client) rosetta.Adapter {
|
||||
return &SomeAdapter{cosmosClient: cosmosClient, tendermintClient: tendermintClient}
|
||||
}
|
||||
|
||||
func (s SomeAdapter) NetworkStatus(ctx context.Context, request *types.NetworkRequest) (*types.NetworkStatusResponse, *types.Error) {
|
||||
resp := s.tendermintClient.CallStatus()
|
||||
// ... Parse status Response
|
||||
// build NetworkStatusResponse
|
||||
return networkStatusResp, nil
|
||||
}
|
||||
|
||||
func (s SomeAdapter) AccountBalance(ctx context.Context, request *types.AccountBalanceRequest) (*types.AccountBalanceResponse, *types.Error) {
|
||||
resp := s.cosmosClient.Account()
|
||||
// ... Parse cosmos specific account response
|
||||
// build AccountBalanceResponse
|
||||
return AccountBalanceResponse, nil
|
||||
}
|
||||
|
||||
// And we repeat for all the methods defined in the interface.
|
||||
```
|
||||
|
||||
For further information about the `Servicer` interfaces, please refer to the [Coinbase's rosetta-sdk-go's documentation](https://pkg.go.dev/github.com/coinbase/rosetta-sdk-go@v0.5.9/server).
|
||||
|
||||
### 2. Cosmos SDK Implementation
|
||||
|
||||
As described, each Cosmos SDK release series will have version specific implementations of `Network` and `Adapter`, as
|
||||
well as a `NewNetwork` constructor.
|
||||
The cosmos sdk implementation, based on version, takes care of satisfying the `Client` interface.
|
||||
In Stargate, Launchpad and 0.37, we have introduced the concept of rosetta.Msg, this message is not in the shared repository as the sdk.Msg type differs between cosmos-sdk versions.
|
||||
|
||||
Due to separation of interface and implementation, application developers have the option to override as needed,
|
||||
using this code as reference.
|
||||
The rosetta.Msg interface follows:
|
||||
|
||||
```golang
|
||||
// NewNetwork returns the default application configuration.
|
||||
func NewNetwork(options Options) service.Network {
|
||||
cosmosClient := cosmos.NewClient(fmt.Sprintf("http://%s", options.CosmosEndpoint))
|
||||
tendermintClient := tendermint.NewClient(fmt.Sprintf("http://%s", options.TendermintEndpoint))
|
||||
|
||||
return service.Network{
|
||||
Properties: rosetta.NetworkProperties{
|
||||
Blockchain: options.Blockchain,
|
||||
Network: options.Network,
|
||||
SupportedOperations: []string{OperationTransfer},
|
||||
},
|
||||
Adapter: newAdapter(
|
||||
cosmosClient,
|
||||
tendermintClient,
|
||||
properties{
|
||||
Blockchain: options.Blockchain,
|
||||
Network: options.Network,
|
||||
OfflineMode: options.OfflineMode,
|
||||
},
|
||||
),
|
||||
}
|
||||
```go
|
||||
// Msg represents a cosmos-sdk message that can be converted from and to a rosetta operation.
|
||||
type Msg interface {
|
||||
sdk.Msg
|
||||
ToOperations(withStatus, hasError bool) []*types.Operation
|
||||
FromOperations(ops []*types.Operation) (sdk.Msg, error)
|
||||
}
|
||||
```
|
||||
|
||||
Hence developers who want to extend the rosetta set of supported operations just need to extend their module's sdk.Msgs with the `ToOperations` and `FromOperations` methods.
|
||||
### 3. API service invocation
|
||||
|
||||
As stated at the start, application developers will have two methods for invocation of the Rosetta API service:
|
||||
@ -195,67 +185,13 @@ As stated at the start, application developers will have two methods for invocat
|
||||
|
||||
#### Shared Process (Only Stargate)
|
||||
|
||||
Rosetta API service could run within the same execution process as the application. New configuration option and
|
||||
command line flags would be provided to support this:
|
||||
Rosetta API service could run within the same execution process as the application. This would be enabled via app.toml settings, and if gRPC is not enabled the rosetta instance would be spinned in offline mode (tx building capabilities only).
|
||||
|
||||
```golang
|
||||
if config.Rosetta.Enable {
|
||||
....
|
||||
get contecxt, flags, etc
|
||||
...
|
||||
|
||||
h, err := service.New(
|
||||
service.Options{ListenAddress: config.Rosetta.ListenAddress},
|
||||
rosetta.NewNetwork(cdc, options),
|
||||
)
|
||||
if err != nil {
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
go func() {
|
||||
if err := h.Start(config); err != nil {
|
||||
errCh <- err
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
#### Separate API service
|
||||
|
||||
Client application developers can write a new command to launch a Rosetta API server as a separate process too:
|
||||
Client application developers can write a new command to launch a Rosetta API server as a separate process too, using the rosetta command contained in the `/server/rosetta` package. Construction of the command depends on cosmos sdk version. Examples can be found inside `simd` for stargate, and `contrib/rosetta/simapp` for other release series.
|
||||
|
||||
```golang
|
||||
func RosettaCommand(cdc *codec.Codec) *cobra.Command {
|
||||
|
||||
...
|
||||
cmd := &cobra.Command{
|
||||
Use: "rosetta",
|
||||
....
|
||||
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
....
|
||||
get contecxt, flags, etc
|
||||
...
|
||||
|
||||
h, err := service.New(
|
||||
service.Options{Endpoint: endpoint},
|
||||
rosetta.NewNetwork(cdc, options),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
h.Start()
|
||||
}
|
||||
}
|
||||
...
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## Status
|
||||
|
||||
|
||||
@ -13,3 +13,4 @@ This folder contains documentation on how to run a node and interact with it.
|
||||
1. [Interacting with a Node](./interact-node.md)
|
||||
1. [Generating, Signing and Broadcasting Transactions](./txs.md)
|
||||
1. [Cosmos Upgrade Manager](./cosmovisor.md)
|
||||
1. [Rosetta API](./rosetta.md)
|
||||
|
||||
83
docs/run-node/rosetta.md
Normal file
83
docs/run-node/rosetta.md
Normal file
@ -0,0 +1,83 @@
|
||||
# Rosetta
|
||||
|
||||
Package rosetta implements the rosetta API for the current cosmos sdk release series.
|
||||
|
||||
The client satisfies [cosmos-rosetta-gateway](https://github.com/tendermint/cosmos-rosetta-gateway) `Client` interface implementation.
|
||||
|
||||
## Extension
|
||||
|
||||
There are two ways in which you can customize and extend the implementation with your custom settings.
|
||||
|
||||
### Message extension
|
||||
|
||||
In order to make an `sdk.Msg` understandable by rosetta the only thing which is required is adding the methods to your message that satisfy the `rosetta.Msg` interface.
|
||||
Examples on how to do so can be found in the staking types such as `MsgDelegate`, or in bank types such as `MsgSend`.
|
||||
|
||||
### Client interface override
|
||||
|
||||
In case more customization is required, it's possible to embed the Client type and override the methods which require customizations.
|
||||
|
||||
Example:
|
||||
```go
|
||||
package custom_client
|
||||
import (
|
||||
|
||||
"context"
|
||||
"github.com/coinbase/rosetta-sdk-go/types"
|
||||
"github.com/cosmos/cosmos-sdk/server/rosetta"
|
||||
)
|
||||
|
||||
// CustomClient embeds the standard cosmos client
|
||||
// which means that it implements the cosmos-rosetta-gateway Client
|
||||
// interface while at the same time allowing to customize certain methods
|
||||
type CustomClient struct {
|
||||
*rosetta.Client
|
||||
}
|
||||
|
||||
func (c *CustomClient) ConstructionPayload(_ context.Context, request *types.ConstructionPayloadsRequest) (resp *types.ConstructionPayloadsResponse, err error) {
|
||||
// provide custom signature bytes
|
||||
panic("implement me")
|
||||
}
|
||||
```
|
||||
|
||||
### Error extension
|
||||
|
||||
Since rosetta requires to provide 'returned' errors to network options. In order to declare a new rosetta error, we use the `errors` package in cosmos-rosetta-gateway.
|
||||
|
||||
Example:
|
||||
|
||||
```go
|
||||
package custom_errors
|
||||
import crgerrs "github.com/tendermint/cosmos-rosetta-gateway/errors"
|
||||
|
||||
var customErrRetriable = true
|
||||
var CustomError = crgerrs.RegisterError(100, "custom message", customErrRetriable, "description")
|
||||
```
|
||||
|
||||
Note: errors must be registered before cosmos-rosetta-gateway's `Server`.`Start` method is called. Otherwise the registration will be ignored. Errors with same code will be ignored too.
|
||||
|
||||
## Integration in app.go
|
||||
|
||||
To integrate rosetta as a command in your application, in app.go, in your root command simply use the `server.RosettaCommand` method.
|
||||
|
||||
Example:
|
||||
|
||||
```go
|
||||
package app
|
||||
import (
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/server"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func buildAppCommand(rootCmd *cobra.Command) {
|
||||
// more app.go init stuff
|
||||
// ...
|
||||
// add rosetta command
|
||||
rootCmd.AddCommand(server.RosettaCommand(encodingConfig.InterfaceRegistry, encodingConfig.Marshaler))
|
||||
}
|
||||
```
|
||||
|
||||
A full implementation example can be found in `simapp` package.
|
||||
|
||||
NOTE: when using a customized client, the command cannot be used as the constructors required **may** differ, so it's required to create a new one. We intend to provide a way to init a customized client without writing extra code in the future.
|
||||
10
go.mod
10
go.mod
@ -4,18 +4,16 @@ module github.com/cosmos/cosmos-sdk
|
||||
|
||||
require (
|
||||
github.com/99designs/keyring v1.1.6
|
||||
github.com/DataDog/zstd v1.4.5 // indirect
|
||||
github.com/armon/go-metrics v0.3.6
|
||||
github.com/bgentry/speakeasy v0.1.0
|
||||
github.com/btcsuite/btcd v0.21.0-beta
|
||||
github.com/btcsuite/btcutil v1.0.2
|
||||
github.com/coinbase/rosetta-sdk-go v0.5.9
|
||||
github.com/confio/ics23/go v0.6.3
|
||||
github.com/cosmos/go-bip39 v1.0.0
|
||||
github.com/cosmos/iavl v0.15.3
|
||||
github.com/cosmos/ledger-cosmos-go v0.11.1
|
||||
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
|
||||
github.com/dgraph-io/ristretto v0.0.3 // indirect
|
||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
|
||||
github.com/enigmampc/btcutil v1.0.3-0.20200723161021-e2fb6adb2a25
|
||||
github.com/gogo/gateway v1.1.0
|
||||
github.com/gogo/protobuf v1.3.3
|
||||
@ -31,7 +29,7 @@ require (
|
||||
github.com/magiconair/properties v1.8.4
|
||||
github.com/mattn/go-isatty v0.0.12
|
||||
github.com/otiai10/copy v1.4.2
|
||||
github.com/pelletier/go-toml v1.8.0 // indirect
|
||||
github.com/pelletier/go-toml v1.8.1 // indirect
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/prometheus/client_golang v1.8.0
|
||||
github.com/prometheus/common v0.15.0
|
||||
@ -41,11 +39,12 @@ require (
|
||||
github.com/spf13/afero v1.3.4 // indirect
|
||||
github.com/spf13/cast v1.3.1
|
||||
github.com/spf13/cobra v1.1.1
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect; indirects
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/spf13/viper v1.7.1
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/tendermint/btcd v0.1.1
|
||||
github.com/tendermint/cosmos-rosetta-gateway v0.3.0-rc1
|
||||
github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15
|
||||
github.com/tendermint/go-amino v0.16.0
|
||||
github.com/tendermint/tendermint v0.34.3
|
||||
@ -54,6 +53,7 @@ require (
|
||||
google.golang.org/genproto v0.0.0-20210114201628-6edceaf6022f
|
||||
google.golang.org/grpc v1.35.0
|
||||
google.golang.org/protobuf v1.25.0
|
||||
gopkg.in/ini.v1 v1.61.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
|
||||
116
go.sum
116
go.sum
@ -12,6 +12,19 @@ cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiy
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/99designs/keyring v1.1.6 h1:kVDC2uCgVwecxCk+9zoCt2uEL6dt+dfVzMvGgnVcIuM=
|
||||
github.com/99designs/keyring v1.1.6/go.mod h1:16e0ds7LGQQcT59QqkTg72Hh5ShM51Byv5PEmW6uoRU=
|
||||
github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4=
|
||||
github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc=
|
||||
github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4=
|
||||
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc=
|
||||
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
|
||||
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
|
||||
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
|
||||
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
@ -26,6 +39,8 @@ github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||
github.com/VictoriaMetrics/fastcache v1.5.7/go.mod h1:ptDBkNMQI4RtmVo8VS/XwRY6RoTu1dAWCbrk+6WsEM8=
|
||||
github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE=
|
||||
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
||||
github.com/Workiva/go-datastructures v1.0.52 h1:PLSK6pwn8mYdaoaCZEMsXBpBotr4HHn9abU0yMQt0NI=
|
||||
@ -37,9 +52,11 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
@ -48,6 +65,7 @@ github.com/armon/go-metrics v0.3.6/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
|
||||
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
|
||||
github.com/aws/aws-sdk-go v1.25.48/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
@ -57,6 +75,7 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
|
||||
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||
github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ=
|
||||
github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0=
|
||||
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
|
||||
github.com/btcsuite/btcd v0.21.0-beta h1:At9hIZdJW0s9E/fAz28nrz6AmcNlSVucCH796ZteX1M=
|
||||
@ -76,6 +95,7 @@ github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46f
|
||||
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||
@ -83,9 +103,13 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
|
||||
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
|
||||
github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9/go.mod h1:1MxXX1Ux4x6mqPmjkUgTP1CdXIBXKX7T+Jk9Gxrmx+U=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
|
||||
github.com/coinbase/rosetta-sdk-go v0.5.8/go.mod h1:xd4wYUhV3LkY78SPH8BUhc88rXfn2jYgN9BfiSjbcvM=
|
||||
github.com/coinbase/rosetta-sdk-go v0.5.9 h1:CuGQE3HFmYwdEACJnuOtVI9cofqPsGvq6FdFIzaOPKI=
|
||||
github.com/coinbase/rosetta-sdk-go v0.5.9/go.mod h1:xd4wYUhV3LkY78SPH8BUhc88rXfn2jYgN9BfiSjbcvM=
|
||||
github.com/confio/ics23/go v0.0.0-20200817220745-f173e6211efb/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg=
|
||||
github.com/confio/ics23/go v0.6.3 h1:PuGK2V1NJWZ8sSkNDq91jgT/cahFEW9RGp4Y5jxulf0=
|
||||
github.com/confio/ics23/go v0.6.3/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg=
|
||||
@ -120,6 +144,7 @@ github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
|
||||
github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
|
||||
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I=
|
||||
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE=
|
||||
@ -134,29 +159,39 @@ github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUn
|
||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
|
||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||
github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA=
|
||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b h1:HBah4D48ypg3J7Np4N+HY/ZR76fx3HEUGxDU6Uk39oQ=
|
||||
github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM=
|
||||
github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
|
||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||
github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/enigmampc/btcutil v1.0.3-0.20200723161021-e2fb6adb2a25 h1:2vLKys4RBU4pn2T/hjXMbvwTr1Cvy5THHrQkbeY9HRk=
|
||||
github.com/enigmampc/btcutil v1.0.3-0.20200723161021-e2fb6adb2a25/go.mod h1:hTr8+TLQmkUkgcuh3mcr5fjrT9c64ZzsBCdCEC6UppY=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/ethereum/go-ethereum v1.9.23 h1:SIKhg/z4Q7AbvqcxuPYvMxf36che/Rq/Pp0IdYEkbtw=
|
||||
github.com/ethereum/go-ethereum v1.9.23/go.mod h1:JIfVb6esrqALTExdz9hRYvrP0xBDf6wCncIu1hNwHpM=
|
||||
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ=
|
||||
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64=
|
||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A=
|
||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg=
|
||||
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 h1:E2s37DuLxFhQDg5gKsWoLBOB0n+ZW8s599zru8FJ2/Y=
|
||||
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0=
|
||||
github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
|
||||
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
|
||||
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
|
||||
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
||||
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
|
||||
@ -164,6 +199,7 @@ github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
@ -175,6 +211,8 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
|
||||
github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
|
||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
@ -207,6 +245,7 @@ github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.2-0.20200707131729-196ae77b8a26/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw=
|
||||
github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
@ -218,9 +257,13 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
|
||||
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa h1:Q75Upo5UN4JbPFURXZ8nLKYUvF85dyFRop/vQ0Rv+64=
|
||||
github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
@ -241,8 +284,10 @@ github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.2.1/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI=
|
||||
@ -293,13 +338,18 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/holiman/uint256 v1.1.1/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
|
||||
github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc=
|
||||
github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o=
|
||||
github.com/improbable-eng/grpc-web v0.13.0 h1:7XqtaBWaOCH0cVGKHyvhtcuo6fgW32Y10yRKrDHFHOc=
|
||||
github.com/improbable-eng/grpc-web v0.13.0/go.mod h1:6hRR09jOEG81ADP5wCQju1z71g6OL4eEvELdran/3cs=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY=
|
||||
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
||||
github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
|
||||
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
@ -317,8 +367,10 @@ github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.1.1-0.20170430222011-975b5c4c7c21/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU=
|
||||
github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d h1:Z+RDyXzjKE0i2sTjZ/b1uxiGtPhFy34Ou/Tk0qwN0kM=
|
||||
github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d/go.mod h1:JJNrCn9otv/2QP4D7SMJBgaleKpOf66PnW6F5WGNRIc=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
@ -331,23 +383,36 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/libp2p/go-buffer-pool v0.0.2 h1:QNK2iAFa8gjAe1SPz6mHSMuCcjs+X1wlHzeOSqcmlfs=
|
||||
github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM=
|
||||
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
|
||||
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
|
||||
github.com/lucasjones/reggen v0.0.0-20180717132126-cdb49ff09d77/go.mod h1:5ELEyG+X8f+meRWHuqUOewBOhvHkl7M76pdGEansxW4=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.4 h1:8KGKTcQQGm0Kv7vEbKFErAoAOFyyacLStRtQSeYtvkY=
|
||||
github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
|
||||
github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
@ -365,6 +430,8 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8=
|
||||
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
@ -374,6 +441,8 @@ github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ib
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
|
||||
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
|
||||
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
|
||||
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
|
||||
github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
|
||||
@ -389,6 +458,8 @@ github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtb
|
||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
@ -420,11 +491,13 @@ github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIw
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.8.0 h1:Keo9qb7iRJs2voHvunFtuuYFsbWeOBh8/P9v/kVMFtw=
|
||||
github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs=
|
||||
github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM=
|
||||
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
|
||||
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
|
||||
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
|
||||
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ=
|
||||
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
|
||||
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
|
||||
@ -472,6 +545,7 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4=
|
||||
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ=
|
||||
github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc=
|
||||
@ -482,11 +556,14 @@ github.com/regen-network/cosmos-proto v0.3.1 h1:rV7iM4SSFAagvy8RiyhiACbWEGotmqzy
|
||||
github.com/regen-network/cosmos-proto v0.3.1/go.mod h1:jO0sVX6a1B36nmE8C9xBFXpNwWejXC7QqCOnH3O0+YM=
|
||||
github.com/regen-network/protobuf v1.3.3-alpha.regen.1 h1:OHEc+q5iIAXpqiqFKeLpu5NwTIkVXUs48vFMwzqpqY4=
|
||||
github.com/regen-network/protobuf v1.3.3-alpha.regen.1/go.mod h1:2DjTFR1HhMQhiWC5sZ4OhQ3+NtdbZ6oBDKQwq5Ou+FI=
|
||||
github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
|
||||
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521/go.mod h1:RvLn4FgxWubrpZHtQLnOf6EwhN2hEMusxZOhcW9H3UQ=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs=
|
||||
github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo=
|
||||
@ -497,6 +574,7 @@ github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0
|
||||
github.com/sasha-s/go-deadlock v0.2.0 h1:lMqc+fUb7RrFS3gQLtoQsJ7/6TV/pAIFvBsqX73DK8Y=
|
||||
github.com/sasha-s/go-deadlock v0.2.0/go.mod h1:StQn567HiB1fF2yJ44N9au7wOhrPS3iZqiDbRupzT10=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/shirou/gopsutil v2.20.5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
@ -535,6 +613,9 @@ github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/y
|
||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
|
||||
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q=
|
||||
github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570/go.mod h1:8OR4w3TdeIHIh1g6EMY5p0gVNOovcWC+1vpc7naMuAw=
|
||||
github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3/go.mod h1:hpGUWaI9xL8pRQCTXQgocU38Qw1g0Us7n5PxxTwTCYU=
|
||||
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
|
||||
@ -545,6 +626,7 @@ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoH
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
@ -556,6 +638,8 @@ github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzH
|
||||
github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8=
|
||||
github.com/tendermint/btcd v0.1.1 h1:0VcxPfflS2zZ3RiOAHkBiFUcPvbtRj5O7zHmcJWHV7s=
|
||||
github.com/tendermint/btcd v0.1.1/go.mod h1:DC6/m53jtQzr/NFmMNEu0rxf18/ktVoVtMrnDD5pN+U=
|
||||
github.com/tendermint/cosmos-rosetta-gateway v0.3.0-rc1 h1:UEkXlFMcWdQWPOA/Rf8nV4WruMK7JO2U8iX7rMTuC24=
|
||||
github.com/tendermint/cosmos-rosetta-gateway v0.3.0-rc1/go.mod h1:gBPw8WV2Erm4UGHlBRiM3zaEBst4bsuihmMCNQdgP/s=
|
||||
github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15 h1:hqAk8riJvK4RMWx1aInLzndwxKalgi5rTqgfXxOxbEI=
|
||||
github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15/go.mod h1:z4YtwM70uOnk8h0pjJYlj3zdYwi9l03By6iAIF5j/Pk=
|
||||
github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E=
|
||||
@ -568,13 +652,21 @@ github.com/tendermint/tendermint v0.34.3/go.mod h1:h57vnXeOlrdvvNFCqPBSaOrpOivl+
|
||||
github.com/tendermint/tm-db v0.6.2/go.mod h1:GYtQ67SUvATOcoY8/+x6ylk8Qo02BQyLrAs+yAcLvGI=
|
||||
github.com/tendermint/tm-db v0.6.3 h1:ZkhQcKnB8/2jr5EaZwGndN4owkPsGezW2fSisS9zGbg=
|
||||
github.com/tendermint/tm-db v0.6.3/go.mod h1:lfA1dL9/Y/Y8wwyPp2NMLyn5P5Ptr/gvDFNWtrCWSf8=
|
||||
github.com/tidwall/gjson v1.6.1/go.mod h1:BaHyNc5bjzYkPqgLq7mdVzeiRtULKULXLgZFKsxEHI0=
|
||||
github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E=
|
||||
github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/tidwall/sjson v1.1.2/go.mod h1:SEzaDwxiPzKzNfUEO4HbYF/m4UCSJDsGgNqsS1LvdoY=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/vmihailenco/msgpack/v5 v5.0.0-beta.9/go.mod h1:HVxBVPUK/+fZMonk4bi1islLa8V3cfnBug0+4dykPzo=
|
||||
github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
|
||||
github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
@ -623,6 +715,7 @@ golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWP
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
|
||||
@ -636,15 +729,18 @@ golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHl
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mobile v0.0.0-20200801112145-973feb4309de/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@ -669,6 +765,7 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@ -681,6 +778,7 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@ -692,6 +790,7 @@ golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -713,12 +812,16 @@ golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200824131525-c12d262b63d8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211 h1:9UQO31fZ+0aKQOFldThf7BKPMJTiBfWycGh/u3UoO88=
|
||||
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -754,6 +857,7 @@ golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
@ -818,9 +922,14 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy
|
||||
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
||||
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.61.0 h1:LBCdW4FmFYL4s/vDZD1RQYX7oAR6IjujCYgMdbHBR10=
|
||||
gopkg.in/ini.v1 v1.61.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
|
||||
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
@ -834,6 +943,9 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
||||
@ -101,6 +101,29 @@ type APIConfig struct {
|
||||
// Ref: https://github.com/cosmos/cosmos-sdk/issues/6420
|
||||
}
|
||||
|
||||
// RosettaConfig defines the Rosetta API listener configuration.
|
||||
type RosettaConfig struct {
|
||||
// Address defines the API server to listen on
|
||||
Address string `mapstructure:"address"`
|
||||
|
||||
// Blockchain defines the blockchain name
|
||||
// defaults to DefaultBlockchain
|
||||
Blockchain string `mapstructure:"blockchain"`
|
||||
|
||||
// Network defines the network name
|
||||
Network string `mapstructure:"network"`
|
||||
|
||||
// Retries defines the maximum number of retries
|
||||
// rosetta will do before quitting
|
||||
Retries int `mapstructure:"retries"`
|
||||
|
||||
// Enable defines if the API server should be enabled.
|
||||
Enable bool `mapstructure:"enable"`
|
||||
|
||||
// Offline defines if the server must be run in offline mode
|
||||
Offline bool `mapstructure:"offline"`
|
||||
}
|
||||
|
||||
// GRPCConfig defines configuration for the gRPC server.
|
||||
type GRPCConfig struct {
|
||||
// Enable defines if the gRPC server should be enabled.
|
||||
@ -138,6 +161,7 @@ type Config struct {
|
||||
Telemetry telemetry.Config `mapstructure:"telemetry"`
|
||||
API APIConfig `mapstructure:"api"`
|
||||
GRPC GRPCConfig `mapstructure:"grpc"`
|
||||
Rosetta RosettaConfig `mapstructure:"rosetta"`
|
||||
GRPCWeb GRPCWebConfig `mapstructure:"grpc-web"`
|
||||
StateSync StateSyncConfig `mapstructure:"state-sync"`
|
||||
}
|
||||
@ -198,6 +222,14 @@ func DefaultConfig() *Config {
|
||||
Enable: true,
|
||||
Address: DefaultGRPCAddress,
|
||||
},
|
||||
Rosetta: RosettaConfig{
|
||||
Enable: false,
|
||||
Address: ":8080",
|
||||
Blockchain: "app",
|
||||
Network: "network",
|
||||
Retries: 3,
|
||||
Offline: false,
|
||||
},
|
||||
GRPCWeb: GRPCWebConfig{
|
||||
Enable: true,
|
||||
Address: DefaultGRPCWebAddress,
|
||||
@ -252,6 +284,14 @@ func GetConfig(v *viper.Viper) Config {
|
||||
RPCMaxBodyBytes: v.GetUint("api.rpc-max-body-bytes"),
|
||||
EnableUnsafeCORS: v.GetBool("api.enabled-unsafe-cors"),
|
||||
},
|
||||
Rosetta: RosettaConfig{
|
||||
Enable: v.GetBool("rosetta.enable"),
|
||||
Address: v.GetString("rosetta.address"),
|
||||
Blockchain: v.GetString("rosetta.blockchain"),
|
||||
Network: v.GetString("rosetta.network"),
|
||||
Retries: v.GetInt("rosetta.retries"),
|
||||
Offline: v.GetBool("rosetta.offline"),
|
||||
},
|
||||
GRPC: GRPCConfig{
|
||||
Enable: v.GetBool("grpc.enable"),
|
||||
Address: v.GetString("grpc.address"),
|
||||
|
||||
@ -135,6 +135,30 @@ rpc-max-body-bytes = {{ .API.RPCMaxBodyBytes }}
|
||||
# EnableUnsafeCORS defines if CORS should be enabled (unsafe - use it at your own risk).
|
||||
enabled-unsafe-cors = {{ .API.EnableUnsafeCORS }}
|
||||
|
||||
###############################################################################
|
||||
### Rosetta Configuration ###
|
||||
###############################################################################
|
||||
|
||||
[rosetta]
|
||||
|
||||
# Enable defines if the Rosetta API server should be enabled.
|
||||
enable = {{ .Rosetta.Enable }}
|
||||
|
||||
# Address defines the Rosetta API server to listen on.
|
||||
address = "{{ .Rosetta.Address }}"
|
||||
|
||||
# Network defines the name of the blockchain that will be returned by Rosetta.
|
||||
blockchain = "{{ .Rosetta.Blockchain }}"
|
||||
|
||||
# Network defines the name of the network that will be returned by Rosetta.
|
||||
network = "{{ .Rosetta.Network }}"
|
||||
|
||||
# Retries defines the number of retries when connecting to the node before failing.
|
||||
retries = {{ .Rosetta.Retries }}
|
||||
|
||||
# Offline defines if Rosetta server should run in offline mode.
|
||||
offline = {{ .Rosetta.Offline }}
|
||||
|
||||
###############################################################################
|
||||
### gRPC Configuration ###
|
||||
###############################################################################
|
||||
|
||||
42
server/rosetta.go
Normal file
42
server/rosetta.go
Normal file
@ -0,0 +1,42 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/server/rosetta"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
)
|
||||
|
||||
// RosettaCommand builds the rosetta root command given
|
||||
// a protocol buffers serializer/deserializer
|
||||
func RosettaCommand(ir codectypes.InterfaceRegistry, cdc codec.Marshaler) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "rosetta",
|
||||
Short: "spin up a rosetta server",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
conf, err := rosetta.FromFlags(cmd.Flags())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
protoCodec, ok := cdc.(*codec.ProtoCodec)
|
||||
if !ok {
|
||||
return fmt.Errorf("exoected *codec.ProtoMarshaler, got: %T", cdc)
|
||||
}
|
||||
conf.WithCodec(ir, protoCodec)
|
||||
|
||||
rosettaSrv, err := rosetta.ServerFromConfig(conf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return rosettaSrv.Start()
|
||||
},
|
||||
}
|
||||
rosetta.SetFlags(cmd.Flags())
|
||||
|
||||
return cmd
|
||||
}
|
||||
221
server/rosetta/client_offline.go
Normal file
221
server/rosetta/client_offline.go
Normal file
@ -0,0 +1,221 @@
|
||||
package rosetta
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"strings"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
"github.com/coinbase/rosetta-sdk-go/types"
|
||||
crgerrs "github.com/tendermint/cosmos-rosetta-gateway/errors"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/tx"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/tx/signing"
|
||||
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
|
||||
)
|
||||
|
||||
func (c *Client) OperationStatuses() []*types.OperationStatus {
|
||||
return []*types.OperationStatus{
|
||||
{
|
||||
Status: StatusSuccess,
|
||||
Successful: true,
|
||||
},
|
||||
{
|
||||
Status: StatusReverted,
|
||||
Successful: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) Version() string {
|
||||
return c.version
|
||||
}
|
||||
|
||||
func (c *Client) SupportedOperations() []string {
|
||||
var supportedOperations []string
|
||||
for _, ii := range c.ir.ListImplementations("cosmos.base.v1beta1.Msg") {
|
||||
resolve, err := c.ir.Resolve(ii)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := resolve.(Msg); ok {
|
||||
supportedOperations = append(supportedOperations, strings.TrimLeft(ii, "/"))
|
||||
}
|
||||
}
|
||||
|
||||
supportedOperations = append(supportedOperations, OperationFee)
|
||||
|
||||
return supportedOperations
|
||||
}
|
||||
|
||||
func (c *Client) SignedTx(ctx context.Context, txBytes []byte, signatures []*types.Signature) (signedTxBytes []byte, err error) {
|
||||
TxConfig := c.getTxConfig()
|
||||
rawTx, err := TxConfig.TxDecoder()(txBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
txBldr, err := TxConfig.WrapTxBuilder(rawTx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var sigs = make([]signing.SignatureV2, len(signatures))
|
||||
for i, signature := range signatures {
|
||||
if signature.PublicKey.CurveType != types.Secp256k1 {
|
||||
return nil, crgerrs.ErrUnsupportedCurve
|
||||
}
|
||||
|
||||
cmp, err := btcec.ParsePubKey(signature.PublicKey.Bytes, btcec.S256())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
compressedPublicKey := make([]byte, secp256k1.PubKeySize)
|
||||
copy(compressedPublicKey, cmp.SerializeCompressed())
|
||||
pubKey := &secp256k1.PubKey{Key: compressedPublicKey}
|
||||
|
||||
accountInfo, err := c.accountInfo(ctx, sdk.AccAddress(pubKey.Address()).String(), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sig := signing.SignatureV2{
|
||||
PubKey: pubKey,
|
||||
Data: &signing.SingleSignatureData{
|
||||
SignMode: signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON,
|
||||
Signature: signature.Bytes,
|
||||
},
|
||||
Sequence: accountInfo.GetSequence(),
|
||||
}
|
||||
sigs[i] = sig
|
||||
}
|
||||
|
||||
if err = txBldr.SetSignatures(sigs...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
txBytes, err = c.getTxConfig().TxEncoder()(txBldr.GetTx())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return txBytes, nil
|
||||
}
|
||||
|
||||
func (c *Client) ConstructionPayload(_ context.Context, request *types.ConstructionPayloadsRequest) (resp *types.ConstructionPayloadsResponse, err error) {
|
||||
// check if there is at least one operation
|
||||
if len(request.Operations) < 1 {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrInvalidOperation, "expected at least one operation")
|
||||
}
|
||||
|
||||
// convert rosetta operations to sdk msgs and fees (if present)
|
||||
msgs, fee, err := opsToMsgsAndFees(c.ir, request.Operations)
|
||||
if err != nil {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrInvalidOperation, err.Error())
|
||||
}
|
||||
|
||||
metadata, err := getMetadataFromPayloadReq(request)
|
||||
if err != nil {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrBadArgument, err.Error())
|
||||
}
|
||||
|
||||
txFactory := tx.Factory{}.WithAccountNumber(metadata.AccountNumber).WithChainID(metadata.ChainID).
|
||||
WithGas(metadata.Gas).WithSequence(metadata.Sequence).WithMemo(metadata.Memo).WithFees(fee.String())
|
||||
|
||||
TxConfig := c.getTxConfig()
|
||||
txFactory = txFactory.WithTxConfig(TxConfig)
|
||||
|
||||
txBldr, err := tx.BuildUnsignedTx(txFactory, msgs...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Sign_mode_legacy_amino is being used as default here, as sign_mode_direct
|
||||
// needs the signer infos to be set before hand but rosetta doesn't have a way
|
||||
// to do this yet. To be revisited in future versions of sdk and rosetta
|
||||
if txFactory.SignMode() == signing.SignMode_SIGN_MODE_UNSPECIFIED {
|
||||
txFactory = txFactory.WithSignMode(signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON)
|
||||
}
|
||||
|
||||
signerData := authsigning.SignerData{
|
||||
ChainID: txFactory.ChainID(),
|
||||
AccountNumber: txFactory.AccountNumber(),
|
||||
Sequence: txFactory.Sequence(),
|
||||
}
|
||||
|
||||
signBytes, err := TxConfig.SignModeHandler().GetSignBytes(txFactory.SignMode(), signerData, txBldr.GetTx())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
txBytes, err := TxConfig.TxEncoder()(txBldr.GetTx())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
accIdentifiers := getAccountIdentifiersByMsgs(msgs)
|
||||
|
||||
payloads := make([]*types.SigningPayload, len(accIdentifiers))
|
||||
for i, accID := range accIdentifiers {
|
||||
payloads[i] = &types.SigningPayload{
|
||||
AccountIdentifier: accID,
|
||||
Bytes: crypto.Sha256(signBytes),
|
||||
SignatureType: types.Ecdsa,
|
||||
}
|
||||
}
|
||||
|
||||
return &types.ConstructionPayloadsResponse{
|
||||
UnsignedTransaction: hex.EncodeToString(txBytes),
|
||||
Payloads: payloads,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getAccountIdentifiersByMsgs(msgs []sdk.Msg) []*types.AccountIdentifier {
|
||||
var accIdentifiers []*types.AccountIdentifier
|
||||
for _, msg := range msgs {
|
||||
for _, signer := range msg.GetSigners() {
|
||||
accIdentifiers = append(accIdentifiers, &types.AccountIdentifier{Address: signer.String()})
|
||||
}
|
||||
}
|
||||
|
||||
return accIdentifiers
|
||||
}
|
||||
|
||||
func (c *Client) PreprocessOperationsToOptions(_ context.Context, req *types.ConstructionPreprocessRequest) (options map[string]interface{}, err error) {
|
||||
operations := req.Operations
|
||||
if len(operations) < 1 {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrBadArgument, "invalid number of operations")
|
||||
}
|
||||
|
||||
msgs, err := opsToMsgs(c.ir, operations)
|
||||
if err != nil {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrInvalidOperation, err.Error())
|
||||
}
|
||||
|
||||
if len(msgs) < 1 || len(msgs[0].GetSigners()) < 1 {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrInvalidOperation, "operation produced no msg or signers")
|
||||
}
|
||||
|
||||
memo, ok := req.Metadata["memo"]
|
||||
if !ok {
|
||||
memo = ""
|
||||
}
|
||||
|
||||
defaultGas := float64(200000)
|
||||
|
||||
gas := req.SuggestedFeeMultiplier
|
||||
if gas == nil {
|
||||
gas = &defaultGas
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
OptionAddress: msgs[0].GetSigners()[0],
|
||||
OptionMemo: memo,
|
||||
OptionGas: gas,
|
||||
}, nil
|
||||
}
|
||||
447
server/rosetta/client_online.go
Normal file
447
server/rosetta/client_online.go
Normal file
@ -0,0 +1,447 @@
|
||||
package rosetta
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/version"
|
||||
|
||||
abcitypes "github.com/tendermint/tendermint/abci/types"
|
||||
|
||||
"github.com/tendermint/btcd/btcec"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
|
||||
|
||||
"github.com/coinbase/rosetta-sdk-go/types"
|
||||
"google.golang.org/grpc/metadata"
|
||||
|
||||
"github.com/tendermint/tendermint/rpc/client/http"
|
||||
tmtypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
crgerrs "github.com/tendermint/cosmos-rosetta-gateway/errors"
|
||||
crgtypes "github.com/tendermint/cosmos-rosetta-gateway/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
grpctypes "github.com/cosmos/cosmos-sdk/types/grpc"
|
||||
authclient "github.com/cosmos/cosmos-sdk/x/auth/client"
|
||||
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
|
||||
auth "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
bank "github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
)
|
||||
|
||||
// interface assertion
|
||||
var _ crgtypes.Client = (*Client)(nil)
|
||||
|
||||
const tmWebsocketPath = "/websocket"
|
||||
const defaultNodeTimeout = 15 * time.Second
|
||||
|
||||
// Client implements a single network client to interact with cosmos based chains
|
||||
type Client struct {
|
||||
config *Config
|
||||
|
||||
auth auth.QueryClient
|
||||
bank bank.QueryClient
|
||||
|
||||
ir codectypes.InterfaceRegistry
|
||||
|
||||
clientCtx client.Context
|
||||
|
||||
version string
|
||||
}
|
||||
|
||||
func (c *Client) AccountIdentifierFromPublicKey(pubKey *types.PublicKey) (*types.AccountIdentifier, error) {
|
||||
if pubKey.CurveType != "secp256k1" {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrUnsupportedCurve, "only secp256k1 supported")
|
||||
}
|
||||
|
||||
cmp, err := btcec.ParsePubKey(pubKey.Bytes, btcec.S256())
|
||||
if err != nil {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrBadArgument, err.Error())
|
||||
}
|
||||
|
||||
compressedPublicKey := make([]byte, secp256k1.PubKeySize)
|
||||
copy(compressedPublicKey, cmp.SerializeCompressed())
|
||||
|
||||
pk := secp256k1.PubKey{Key: compressedPublicKey}
|
||||
|
||||
return &types.AccountIdentifier{
|
||||
Address: sdk.AccAddress(pk.Address()).String(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewClient instantiates a new online servicer
|
||||
func NewClient(cfg *Config) (*Client, error) {
|
||||
info := version.NewInfo()
|
||||
|
||||
v := info.Version
|
||||
if v == "" {
|
||||
v = "unknown"
|
||||
}
|
||||
|
||||
return &Client{
|
||||
config: cfg,
|
||||
ir: cfg.InterfaceRegistry,
|
||||
version: fmt.Sprintf("%s/%s", info.AppName, v),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Client) accountInfo(ctx context.Context, addr string, height *int64) (auth.AccountI, error) {
|
||||
if height != nil {
|
||||
strHeight := strconv.FormatInt(*height, 10)
|
||||
ctx = metadata.AppendToOutgoingContext(ctx, grpctypes.GRPCBlockHeightHeader, strHeight)
|
||||
}
|
||||
|
||||
accountInfo, err := c.auth.Account(ctx, &auth.QueryAccountRequest{
|
||||
Address: addr,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, crgerrs.FromGRPCToRosettaError(err)
|
||||
}
|
||||
|
||||
var account auth.AccountI
|
||||
err = c.ir.UnpackAny(accountInfo.Account, &account)
|
||||
if err != nil {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrCodec, err.Error())
|
||||
}
|
||||
|
||||
return account, nil
|
||||
}
|
||||
|
||||
func (c *Client) Balances(ctx context.Context, addr string, height *int64) ([]*types.Amount, error) {
|
||||
if height != nil {
|
||||
strHeight := strconv.FormatInt(*height, 10)
|
||||
ctx = metadata.AppendToOutgoingContext(ctx, grpctypes.GRPCBlockHeightHeader, strHeight)
|
||||
}
|
||||
|
||||
balance, err := c.bank.AllBalances(ctx, &bank.QueryAllBalancesRequest{
|
||||
Address: addr,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, crgerrs.FromGRPCToRosettaError(err)
|
||||
}
|
||||
|
||||
availableCoins, err := c.coins(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return sdkCoinsToRosettaAmounts(balance.Balances, availableCoins), nil
|
||||
}
|
||||
|
||||
func (c *Client) BlockByHash(ctx context.Context, hash string) (crgtypes.BlockResponse, error) {
|
||||
bHash, err := hex.DecodeString(hash)
|
||||
if err != nil {
|
||||
return crgtypes.BlockResponse{}, fmt.Errorf("invalid block hash: %s", err)
|
||||
}
|
||||
|
||||
block, err := c.clientCtx.Client.BlockByHash(ctx, bHash)
|
||||
if err != nil {
|
||||
return crgtypes.BlockResponse{}, err
|
||||
}
|
||||
|
||||
return buildBlockResponse(block), nil
|
||||
}
|
||||
|
||||
func (c *Client) BlockByHeight(ctx context.Context, height *int64) (crgtypes.BlockResponse, error) {
|
||||
block, err := c.clientCtx.Client.Block(ctx, height)
|
||||
if err != nil {
|
||||
return crgtypes.BlockResponse{}, err
|
||||
}
|
||||
|
||||
return buildBlockResponse(block), nil
|
||||
}
|
||||
|
||||
func buildBlockResponse(block *tmtypes.ResultBlock) crgtypes.BlockResponse {
|
||||
return crgtypes.BlockResponse{
|
||||
Block: TMBlockToRosettaBlockIdentifier(block),
|
||||
ParentBlock: TMBlockToRosettaParentBlockIdentifier(block),
|
||||
MillisecondTimestamp: timeToMilliseconds(block.Block.Time),
|
||||
TxCount: int64(len(block.Block.Txs)),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) BlockTransactionsByHash(ctx context.Context, hash string) (crgtypes.BlockTransactionsResponse, error) {
|
||||
blockResp, err := c.BlockByHash(ctx, hash)
|
||||
if err != nil {
|
||||
return crgtypes.BlockTransactionsResponse{}, err
|
||||
}
|
||||
|
||||
txs, err := c.listTransactionsInBlock(ctx, blockResp.Block.Index)
|
||||
if err != nil {
|
||||
return crgtypes.BlockTransactionsResponse{}, err
|
||||
}
|
||||
|
||||
return crgtypes.BlockTransactionsResponse{
|
||||
BlockResponse: blockResp,
|
||||
Transactions: sdkTxsWithHashToRosettaTxs(txs),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Client) BlockTransactionsByHeight(ctx context.Context, height *int64) (crgtypes.BlockTransactionsResponse, error) {
|
||||
blockResp, err := c.BlockByHeight(ctx, height)
|
||||
if err != nil {
|
||||
return crgtypes.BlockTransactionsResponse{}, err
|
||||
}
|
||||
|
||||
txs, err := c.listTransactionsInBlock(ctx, blockResp.Block.Index)
|
||||
if err != nil {
|
||||
return crgtypes.BlockTransactionsResponse{}, err
|
||||
}
|
||||
|
||||
return crgtypes.BlockTransactionsResponse{
|
||||
BlockResponse: blockResp,
|
||||
Transactions: sdkTxsWithHashToRosettaTxs(txs),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Coins fetches the existing coins in the application
|
||||
func (c *Client) coins(ctx context.Context) (sdk.Coins, error) {
|
||||
supply, err := c.bank.TotalSupply(ctx, &bank.QueryTotalSupplyRequest{})
|
||||
if err != nil {
|
||||
return nil, crgerrs.FromGRPCToRosettaError(err)
|
||||
}
|
||||
return supply.Supply, nil
|
||||
}
|
||||
|
||||
// listTransactionsInBlock returns the list of the transactions in a block given its height
|
||||
func (c *Client) listTransactionsInBlock(ctx context.Context, height int64) ([]*sdkTxWithHash, error) {
|
||||
txQuery := fmt.Sprintf(`tx.height=%d`, height)
|
||||
txList, err := c.clientCtx.Client.TxSearch(ctx, txQuery, true, nil, nil, "")
|
||||
if err != nil {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrUnknown, err.Error())
|
||||
}
|
||||
|
||||
sdkTxs, err := tmResultTxsToSdkTxsWithHash(c.clientCtx.TxConfig.TxDecoder(), txList.Txs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sdkTxs, nil
|
||||
}
|
||||
|
||||
func (c *Client) TxOperationsAndSignersAccountIdentifiers(signed bool, txBytes []byte) (ops []*types.Operation, signers []*types.AccountIdentifier, err error) {
|
||||
txConfig := c.getTxConfig()
|
||||
rawTx, err := txConfig.TxDecoder()(txBytes)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
txBldr, err := txConfig.WrapTxBuilder(rawTx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var accountIdentifierSigners []*types.AccountIdentifier
|
||||
if signed {
|
||||
addrs := txBldr.GetTx().GetSigners()
|
||||
for _, addr := range addrs {
|
||||
signer := &types.AccountIdentifier{
|
||||
Address: addr.String(),
|
||||
}
|
||||
accountIdentifierSigners = append(accountIdentifierSigners, signer)
|
||||
}
|
||||
}
|
||||
|
||||
return sdkTxToOperations(txBldr.GetTx(), false, false), accountIdentifierSigners, nil
|
||||
}
|
||||
|
||||
// GetTx returns a transaction given its hash
|
||||
func (c *Client) GetTx(_ context.Context, hash string) (*types.Transaction, error) {
|
||||
txResp, err := authclient.QueryTx(c.clientCtx, hash)
|
||||
if err != nil {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrUnknown, err.Error())
|
||||
}
|
||||
var sdkTx sdk.Tx
|
||||
err = c.ir.UnpackAny(txResp.Tx, &sdkTx)
|
||||
if err != nil {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrCodec, err.Error())
|
||||
}
|
||||
return sdkTxWithHashToOperations(&sdkTxWithHash{
|
||||
HexHash: txResp.TxHash,
|
||||
Code: txResp.Code,
|
||||
Log: txResp.RawLog,
|
||||
Tx: sdkTx,
|
||||
}), nil
|
||||
}
|
||||
|
||||
// GetUnconfirmedTx gets an unconfirmed transaction given its hash
|
||||
func (c *Client) GetUnconfirmedTx(ctx context.Context, hash string) (*types.Transaction, error) {
|
||||
res, err := c.clientCtx.Client.UnconfirmedTxs(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrNotFound, "unconfirmed tx not found")
|
||||
}
|
||||
|
||||
hashAsBytes, err := hex.DecodeString(hash)
|
||||
if err != nil {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrInterpreting, "invalid hash")
|
||||
}
|
||||
|
||||
for _, tx := range res.Txs {
|
||||
if bytes.Equal(tx.Hash(), hashAsBytes) {
|
||||
sdkTx, err := tmTxToSdkTx(c.clientCtx.TxConfig.TxDecoder(), tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.Transaction{
|
||||
TransactionIdentifier: TmTxToRosettaTxsIdentifier(tx),
|
||||
Operations: sdkTxToOperations(sdkTx, false, false),
|
||||
Metadata: nil,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrNotFound, "transaction not found in mempool")
|
||||
}
|
||||
|
||||
// Mempool returns the unconfirmed transactions in the mempool
|
||||
func (c *Client) Mempool(ctx context.Context) ([]*types.TransactionIdentifier, error) {
|
||||
txs, err := c.clientCtx.Client.UnconfirmedTxs(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return TMTxsToRosettaTxsIdentifiers(txs.Txs), nil
|
||||
}
|
||||
|
||||
// Peers gets the number of peers
|
||||
func (c *Client) Peers(ctx context.Context) ([]*types.Peer, error) {
|
||||
netInfo, err := c.clientCtx.Client.NetInfo(ctx)
|
||||
if err != nil {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrUnknown, err.Error())
|
||||
}
|
||||
return TmPeersToRosettaPeers(netInfo.Peers), nil
|
||||
}
|
||||
|
||||
func (c *Client) Status(ctx context.Context) (*types.SyncStatus, error) {
|
||||
status, err := c.clientCtx.Client.Status(ctx)
|
||||
if err != nil {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrUnknown, err.Error())
|
||||
}
|
||||
return TMStatusToRosettaSyncStatus(status), err
|
||||
}
|
||||
|
||||
func (c *Client) getTxConfig() client.TxConfig {
|
||||
return c.clientCtx.TxConfig
|
||||
}
|
||||
|
||||
func (c *Client) PostTx(txBytes []byte) (*types.TransactionIdentifier, map[string]interface{}, error) {
|
||||
// sync ensures it will go through checkTx
|
||||
res, err := c.clientCtx.BroadcastTxSync(txBytes)
|
||||
if err != nil {
|
||||
return nil, nil, crgerrs.WrapError(crgerrs.ErrUnknown, err.Error())
|
||||
}
|
||||
// check if tx was broadcast successfully
|
||||
if res.Code != abcitypes.CodeTypeOK {
|
||||
return nil, nil, crgerrs.WrapError(crgerrs.ErrUnknown, fmt.Sprintf("transaction broadcast failure: (%d) %s ", res.Code, res.RawLog))
|
||||
}
|
||||
|
||||
return &types.TransactionIdentifier{
|
||||
Hash: res.TxHash,
|
||||
},
|
||||
map[string]interface{}{
|
||||
Log: res.RawLog,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Client) ConstructionMetadataFromOptions(ctx context.Context, options map[string]interface{}) (meta map[string]interface{}, err error) {
|
||||
if len(options) == 0 {
|
||||
return nil, crgerrs.ErrBadArgument
|
||||
}
|
||||
|
||||
addr, ok := options[OptionAddress]
|
||||
if !ok {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrInvalidAddress, "no address provided")
|
||||
}
|
||||
|
||||
addrString, ok := addr.(string)
|
||||
if !ok {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrInvalidAddress, "address is not a string")
|
||||
}
|
||||
|
||||
accountInfo, err := c.accountInfo(ctx, addrString, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gas, ok := options[OptionGas]
|
||||
if !ok {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrInvalidAddress, "gas not set")
|
||||
}
|
||||
|
||||
memo, ok := options[OptionMemo]
|
||||
if !ok {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrInvalidMemo, "memo not set")
|
||||
}
|
||||
|
||||
status, err := c.clientCtx.Client.Status(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
OptionAccountNumber: accountInfo.GetAccountNumber(),
|
||||
OptionSequence: accountInfo.GetSequence(),
|
||||
OptionChainID: status.NodeInfo.Network,
|
||||
OptionGas: gas,
|
||||
OptionMemo: memo,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Client) Ready() error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), defaultNodeTimeout)
|
||||
defer cancel()
|
||||
_, err := c.clientCtx.Client.Health(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = c.bank.TotalSupply(ctx, &bank.QueryTotalSupplyRequest{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) Bootstrap() error {
|
||||
grpcConn, err := grpc.Dial(c.config.GRPCEndpoint, grpc.WithInsecure())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmRPC, err := http.New(c.config.TendermintRPC, tmWebsocketPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
authClient := auth.NewQueryClient(grpcConn)
|
||||
bankClient := bank.NewQueryClient(grpcConn)
|
||||
|
||||
// NodeURI and Client are set from here otherwise
|
||||
// WitNodeURI will require to create a new client
|
||||
// it's done here because WithNodeURI panics if
|
||||
// connection to tendermint node fails
|
||||
clientCtx := client.Context{
|
||||
Client: tmRPC,
|
||||
NodeURI: c.config.TendermintRPC,
|
||||
}
|
||||
clientCtx = clientCtx.
|
||||
WithJSONMarshaler(c.config.Codec).
|
||||
WithInterfaceRegistry(c.config.InterfaceRegistry).
|
||||
WithTxConfig(authtx.NewTxConfig(c.config.Codec, authtx.DefaultSignModes)).
|
||||
WithAccountRetriever(auth.AccountRetriever{}).
|
||||
WithBroadcastMode(flags.BroadcastBlock)
|
||||
|
||||
c.auth = authClient
|
||||
c.bank = bankClient
|
||||
c.clientCtx = clientCtx
|
||||
c.ir = c.config.InterfaceRegistry
|
||||
|
||||
return nil
|
||||
}
|
||||
22
server/rosetta/codec.go
Normal file
22
server/rosetta/codec.go
Normal file
@ -0,0 +1,22 @@
|
||||
package rosetta
|
||||
|
||||
import (
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
|
||||
authcodec "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
bankcodec "github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
)
|
||||
|
||||
// MakeCodec generates the codec required to interact
|
||||
// with the cosmos APIs used by the rosetta gateway
|
||||
func MakeCodec() (*codec.ProtoCodec, codectypes.InterfaceRegistry) {
|
||||
ir := codectypes.NewInterfaceRegistry()
|
||||
cdc := codec.NewProtoCodec(ir)
|
||||
|
||||
authcodec.RegisterInterfaces(ir)
|
||||
bankcodec.RegisterInterfaces(ir)
|
||||
cryptocodec.RegisterInterfaces(ir)
|
||||
|
||||
return cdc, ir
|
||||
}
|
||||
203
server/rosetta/config.go
Normal file
203
server/rosetta/config.go
Normal file
@ -0,0 +1,203 @@
|
||||
package rosetta
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/coinbase/rosetta-sdk-go/types"
|
||||
"github.com/spf13/pflag"
|
||||
crg "github.com/tendermint/cosmos-rosetta-gateway/server"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
)
|
||||
|
||||
// configuration defaults constants
|
||||
const (
|
||||
// DefaultBlockchain defines the default blockchain identifier name
|
||||
DefaultBlockchain = "app"
|
||||
// DefaultAddr defines the default rosetta binding address
|
||||
DefaultAddr = ":8080"
|
||||
// DefaultRetries is the default number of retries
|
||||
DefaultRetries = 5
|
||||
// DefaultTendermintEndpoint is the default value for the tendermint endpoint
|
||||
DefaultTendermintEndpoint = "localhost:26657"
|
||||
// DefaultGRPCEndpoint is the default value for the gRPC endpoint
|
||||
DefaultGRPCEndpoint = "localhost:9090"
|
||||
// DefaultNetwork defines the default network name
|
||||
DefaultNetwork = "network"
|
||||
// DefaultOffline defines the default offline value
|
||||
DefaultOffline = false
|
||||
)
|
||||
|
||||
// configuration flags
|
||||
const (
|
||||
FlagBlockchain = "blockchain"
|
||||
FlagNetwork = "network"
|
||||
FlagTendermintEndpoint = "tendermint"
|
||||
FlagGRPCEndpoint = "grpc"
|
||||
FlagAddr = "addr"
|
||||
FlagRetries = "retries"
|
||||
FlagOffline = "offline"
|
||||
)
|
||||
|
||||
// Config defines the configuration of the rosetta server
|
||||
type Config struct {
|
||||
// Blockchain defines the blockchain name
|
||||
// defaults to DefaultBlockchain
|
||||
Blockchain string
|
||||
// Network defines the network name
|
||||
Network string
|
||||
// TendermintRPC defines the endpoint to connect to
|
||||
// tendermint RPC, specifying 'tcp://' before is not
|
||||
// required, usually it's at port 26657 of the
|
||||
TendermintRPC string
|
||||
// GRPCEndpoint defines the cosmos application gRPC endpoint
|
||||
// usually it is located at 9090 port
|
||||
GRPCEndpoint string
|
||||
// Addr defines the default address to bind the rosetta server to
|
||||
// defaults to DefaultAddr
|
||||
Addr string
|
||||
// Retries defines the maximum number of retries
|
||||
// rosetta will do before quitting
|
||||
Retries int
|
||||
// Offline defines if the server must be run in offline mode
|
||||
Offline bool
|
||||
// Codec overrides the default data and construction api client codecs
|
||||
Codec *codec.ProtoCodec
|
||||
// InterfaceRegistry overrides the default data and construction api interface registry
|
||||
InterfaceRegistry codectypes.InterfaceRegistry
|
||||
}
|
||||
|
||||
// NetworkIdentifier returns the network identifier given the configuration
|
||||
func (c *Config) NetworkIdentifier() *types.NetworkIdentifier {
|
||||
return &types.NetworkIdentifier{
|
||||
Blockchain: c.Blockchain,
|
||||
Network: c.Network,
|
||||
}
|
||||
}
|
||||
|
||||
// validate validates a configuration and sets
|
||||
// its defaults in case they were not provided
|
||||
func (c *Config) validate() error {
|
||||
if (c.Codec == nil) != (c.InterfaceRegistry == nil) {
|
||||
return fmt.Errorf("codec and interface registry must be both different from nil or nil")
|
||||
}
|
||||
|
||||
if c.Addr == "" {
|
||||
c.Addr = DefaultAddr
|
||||
}
|
||||
if c.Blockchain == "" {
|
||||
c.Blockchain = DefaultBlockchain
|
||||
}
|
||||
if c.Retries == 0 {
|
||||
c.Retries = DefaultRetries
|
||||
}
|
||||
// these are must
|
||||
if c.Network == "" {
|
||||
return fmt.Errorf("network not provided")
|
||||
}
|
||||
if c.Offline {
|
||||
return fmt.Errorf("offline mode is not supported for stargate implementation due to how sigv2 works")
|
||||
}
|
||||
|
||||
// these are optional but it must be online
|
||||
if c.GRPCEndpoint == "" {
|
||||
return fmt.Errorf("grpc endpoint not provided")
|
||||
}
|
||||
if c.TendermintRPC == "" {
|
||||
return fmt.Errorf("tendermint rpc not provided")
|
||||
}
|
||||
if !strings.HasPrefix(c.TendermintRPC, "tcp://") {
|
||||
c.TendermintRPC = fmt.Sprintf("tcp://%s", c.TendermintRPC)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithCodec extends the configuration with a predefined Codec
|
||||
func (c *Config) WithCodec(ir codectypes.InterfaceRegistry, cdc *codec.ProtoCodec) {
|
||||
c.Codec = cdc
|
||||
c.InterfaceRegistry = ir
|
||||
}
|
||||
|
||||
// FromFlags gets the configuration from flags
|
||||
func FromFlags(flags *pflag.FlagSet) (*Config, error) {
|
||||
blockchain, err := flags.GetString(FlagBlockchain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
network, err := flags.GetString(FlagNetwork)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tendermintRPC, err := flags.GetString(FlagTendermintEndpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gRPCEndpoint, err := flags.GetString(FlagGRPCEndpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addr, err := flags.GetString(FlagAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
retries, err := flags.GetInt(FlagRetries)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
offline, err := flags.GetBool(FlagOffline)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conf := &Config{
|
||||
Blockchain: blockchain,
|
||||
Network: network,
|
||||
TendermintRPC: tendermintRPC,
|
||||
GRPCEndpoint: gRPCEndpoint,
|
||||
Addr: addr,
|
||||
Retries: retries,
|
||||
Offline: offline,
|
||||
}
|
||||
err = conf.validate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return conf, nil
|
||||
}
|
||||
|
||||
func ServerFromConfig(conf *Config) (crg.Server, error) {
|
||||
err := conf.validate()
|
||||
if err != nil {
|
||||
return crg.Server{}, err
|
||||
}
|
||||
client, err := NewClient(conf)
|
||||
if err != nil {
|
||||
return crg.Server{}, err
|
||||
}
|
||||
return crg.NewServer(
|
||||
crg.Settings{
|
||||
Network: &types.NetworkIdentifier{
|
||||
Blockchain: conf.Blockchain,
|
||||
Network: conf.Network,
|
||||
},
|
||||
Client: client,
|
||||
Listen: conf.Addr,
|
||||
Offline: conf.Offline,
|
||||
Retries: conf.Retries,
|
||||
RetryWait: 15 * time.Second,
|
||||
})
|
||||
}
|
||||
|
||||
// SetFlags sets the configuration flags to the given flagset
|
||||
func SetFlags(flags *pflag.FlagSet) {
|
||||
flags.String(FlagBlockchain, DefaultBlockchain, "the blockchain type")
|
||||
flags.String(FlagNetwork, DefaultNetwork, "the network name")
|
||||
flags.String(FlagTendermintEndpoint, DefaultTendermintEndpoint, "the tendermint rpc endpoint, without tcp://")
|
||||
flags.String(FlagGRPCEndpoint, DefaultGRPCEndpoint, "the app gRPC endpoint")
|
||||
flags.String(FlagAddr, DefaultAddr, "the address rosetta will bind to")
|
||||
flags.Int(FlagRetries, DefaultRetries, "the number of retries that will be done before quitting")
|
||||
flags.Bool(FlagOffline, DefaultOffline, "run rosetta only with construction API")
|
||||
}
|
||||
211
server/rosetta/conv_from_rosetta.go
Normal file
211
server/rosetta/conv_from_rosetta.go
Normal file
@ -0,0 +1,211 @@
|
||||
package rosetta
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/coinbase/rosetta-sdk-go/types"
|
||||
tmcoretypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// timeToMilliseconds converts time to milliseconds timestamp
|
||||
func timeToMilliseconds(t time.Time) int64 {
|
||||
return t.UnixNano() / (int64(time.Millisecond) / int64(time.Nanosecond))
|
||||
}
|
||||
|
||||
// sdkCoinsToRosettaAmounts converts []sdk.Coin to rosetta amounts
|
||||
// availableCoins keeps track of current available coins vs the coins
|
||||
// owned by an address. This is required to support historical balances
|
||||
// as rosetta expects them to be set to 0, if an address does not own them
|
||||
func sdkCoinsToRosettaAmounts(ownedCoins []sdk.Coin, availableCoins sdk.Coins) []*types.Amount {
|
||||
amounts := make([]*types.Amount, len(availableCoins))
|
||||
ownedCoinsMap := make(map[string]sdk.Int, len(availableCoins))
|
||||
|
||||
for _, ownedCoin := range ownedCoins {
|
||||
ownedCoinsMap[ownedCoin.Denom] = ownedCoin.Amount
|
||||
}
|
||||
|
||||
for i, coin := range availableCoins {
|
||||
value, owned := ownedCoinsMap[coin.Denom]
|
||||
if !owned {
|
||||
amounts[i] = &types.Amount{
|
||||
Value: sdk.NewInt(0).String(),
|
||||
Currency: &types.Currency{
|
||||
Symbol: coin.Denom,
|
||||
},
|
||||
}
|
||||
continue
|
||||
}
|
||||
amounts[i] = &types.Amount{
|
||||
Value: value.String(),
|
||||
Currency: &types.Currency{
|
||||
Symbol: coin.Denom,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return amounts
|
||||
}
|
||||
|
||||
// sdkTxsWithHashToRosettaTxs converts sdk transactions wrapped with their hash to rosetta transactions
|
||||
func sdkTxsWithHashToRosettaTxs(txs []*sdkTxWithHash) []*types.Transaction {
|
||||
converted := make([]*types.Transaction, len(txs))
|
||||
for i, tx := range txs {
|
||||
converted[i] = sdkTxWithHashToOperations(tx)
|
||||
}
|
||||
|
||||
return converted
|
||||
}
|
||||
|
||||
func sdkTxWithHashToOperations(tx *sdkTxWithHash) *types.Transaction {
|
||||
hasError := tx.Code != 0
|
||||
return &types.Transaction{
|
||||
TransactionIdentifier: &types.TransactionIdentifier{Hash: tx.HexHash},
|
||||
Operations: sdkTxToOperations(tx.Tx, true, hasError),
|
||||
Metadata: map[string]interface{}{
|
||||
Log: tx.Log,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// sdkTxToOperations converts an sdk.Tx to rosetta operations
|
||||
func sdkTxToOperations(tx sdk.Tx, withStatus, hasError bool) []*types.Operation {
|
||||
var operations []*types.Operation
|
||||
|
||||
msgOps := sdkMsgsToRosettaOperations(tx.GetMsgs(), withStatus, hasError)
|
||||
operations = append(operations, msgOps...)
|
||||
|
||||
feeTx := tx.(sdk.FeeTx)
|
||||
feeOps := sdkFeeTxToOperations(feeTx, withStatus, len(msgOps))
|
||||
operations = append(operations, feeOps...)
|
||||
|
||||
return operations
|
||||
}
|
||||
|
||||
// sdkFeeTxToOperations converts sdk.FeeTx to rosetta operations
|
||||
func sdkFeeTxToOperations(feeTx sdk.FeeTx, withStatus bool, previousOps int) []*types.Operation {
|
||||
feeCoins := feeTx.GetFee()
|
||||
var ops []*types.Operation
|
||||
if feeCoins != nil {
|
||||
var feeOps = rosettaFeeOperationsFromCoins(feeCoins, feeTx.FeePayer().String(), withStatus, previousOps)
|
||||
ops = append(ops, feeOps...)
|
||||
}
|
||||
|
||||
return ops
|
||||
}
|
||||
|
||||
// rosettaFeeOperationsFromCoins returns the list of rosetta fee operations given sdk coins
|
||||
func rosettaFeeOperationsFromCoins(coins sdk.Coins, account string, withStatus bool, previousOps int) []*types.Operation {
|
||||
feeOps := make([]*types.Operation, 0)
|
||||
var status string
|
||||
if withStatus {
|
||||
status = StatusSuccess
|
||||
}
|
||||
|
||||
for i, coin := range coins {
|
||||
op := &types.Operation{
|
||||
OperationIdentifier: &types.OperationIdentifier{
|
||||
Index: int64(previousOps + i),
|
||||
},
|
||||
Type: OperationFee,
|
||||
Status: status,
|
||||
Account: &types.AccountIdentifier{
|
||||
Address: account,
|
||||
},
|
||||
Amount: &types.Amount{
|
||||
Value: "-" + coin.Amount.String(),
|
||||
Currency: &types.Currency{
|
||||
Symbol: coin.Denom,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
feeOps = append(feeOps, op)
|
||||
}
|
||||
|
||||
return feeOps
|
||||
}
|
||||
|
||||
// sdkMsgsToRosettaOperations converts sdk messages to rosetta operations
|
||||
func sdkMsgsToRosettaOperations(msgs []sdk.Msg, withStatus bool, hasError bool) []*types.Operation {
|
||||
var operations []*types.Operation
|
||||
for _, msg := range msgs {
|
||||
if rosettaMsg, ok := msg.(Msg); ok {
|
||||
operations = append(operations, rosettaMsg.ToOperations(withStatus, hasError)...)
|
||||
}
|
||||
}
|
||||
|
||||
return operations
|
||||
}
|
||||
|
||||
// TMTxsToRosettaTxsIdentifiers converts a tendermint raw transactions into an array of rosetta tx identifiers
|
||||
func TMTxsToRosettaTxsIdentifiers(txs []tmtypes.Tx) []*types.TransactionIdentifier {
|
||||
converted := make([]*types.TransactionIdentifier, len(txs))
|
||||
for i, tx := range txs {
|
||||
converted[i] = TmTxToRosettaTxsIdentifier(tx)
|
||||
}
|
||||
|
||||
return converted
|
||||
}
|
||||
|
||||
// TmTxToRosettaTxsIdentifier converts a tendermint raw transaction into a rosetta tx identifier
|
||||
func TmTxToRosettaTxsIdentifier(tx tmtypes.Tx) *types.TransactionIdentifier {
|
||||
return &types.TransactionIdentifier{Hash: fmt.Sprintf("%x", tx.Hash())}
|
||||
}
|
||||
|
||||
// TMBlockToRosettaBlockIdentifier converts a tendermint result block to a rosetta block identifier
|
||||
func TMBlockToRosettaBlockIdentifier(block *tmcoretypes.ResultBlock) *types.BlockIdentifier {
|
||||
return &types.BlockIdentifier{
|
||||
Index: block.Block.Height,
|
||||
Hash: block.Block.Hash().String(),
|
||||
}
|
||||
}
|
||||
|
||||
// TmPeersToRosettaPeers converts tendermint peers to rosetta ones
|
||||
func TmPeersToRosettaPeers(peers []tmcoretypes.Peer) []*types.Peer {
|
||||
converted := make([]*types.Peer, len(peers))
|
||||
|
||||
for i, peer := range peers {
|
||||
converted[i] = &types.Peer{
|
||||
PeerID: peer.NodeInfo.Moniker,
|
||||
Metadata: map[string]interface{}{
|
||||
"addr": peer.NodeInfo.ListenAddr,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return converted
|
||||
}
|
||||
|
||||
// TMStatusToRosettaSyncStatus converts a tendermint status to rosetta sync status
|
||||
func TMStatusToRosettaSyncStatus(status *tmcoretypes.ResultStatus) *types.SyncStatus {
|
||||
// determine sync status
|
||||
var stage = StageSynced
|
||||
if status.SyncInfo.CatchingUp {
|
||||
stage = StageSyncing
|
||||
}
|
||||
|
||||
return &types.SyncStatus{
|
||||
CurrentIndex: status.SyncInfo.LatestBlockHeight,
|
||||
TargetIndex: nil, // sync info does not allow us to get target height
|
||||
Stage: &stage,
|
||||
}
|
||||
}
|
||||
|
||||
// TMBlockToRosettaParentBlockIdentifier returns the parent block identifier from the last block
|
||||
func TMBlockToRosettaParentBlockIdentifier(block *tmcoretypes.ResultBlock) *types.BlockIdentifier {
|
||||
if block.Block.Height == 1 {
|
||||
return &types.BlockIdentifier{
|
||||
Index: 1,
|
||||
Hash: fmt.Sprintf("%X", block.BlockID.Hash.Bytes()),
|
||||
}
|
||||
}
|
||||
|
||||
return &types.BlockIdentifier{
|
||||
Index: block.Block.Height - 1,
|
||||
Hash: fmt.Sprintf("%X", block.Block.LastBlockID.Hash.Bytes()),
|
||||
}
|
||||
}
|
||||
95
server/rosetta/conv_to_rosetta.go
Normal file
95
server/rosetta/conv_to_rosetta.go
Normal file
@ -0,0 +1,95 @@
|
||||
package rosetta
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gogo/protobuf/jsonpb"
|
||||
|
||||
"github.com/coinbase/rosetta-sdk-go/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// opsToMsgsAndFees converts rosetta operations to sdk.Msg and fees represented as sdk.Coins
|
||||
func opsToMsgsAndFees(interfaceRegistry jsonpb.AnyResolver, ops []*types.Operation) ([]sdk.Msg, sdk.Coins, error) {
|
||||
var feeAmnt []*types.Amount
|
||||
var newOps []*types.Operation
|
||||
var msgType string
|
||||
// find the fee operation and put it aside
|
||||
for _, op := range ops {
|
||||
switch op.Type {
|
||||
case OperationFee:
|
||||
amount := op.Amount
|
||||
feeAmnt = append(feeAmnt, amount)
|
||||
default:
|
||||
// check if operation matches the one already used
|
||||
// as, at the moment, we only support operations
|
||||
// that represent a single cosmos-sdk message
|
||||
switch {
|
||||
// if msgType was not set then set it
|
||||
case msgType == "":
|
||||
msgType = op.Type
|
||||
// if msgType does not match op.Type then it means we're trying to send multiple messages in a single tx
|
||||
case msgType != op.Type:
|
||||
return nil, nil, fmt.Errorf("only single message operations are supported: %s - %s", msgType, op.Type)
|
||||
}
|
||||
// append operation to new ops list
|
||||
newOps = append(newOps, op)
|
||||
}
|
||||
}
|
||||
// convert all operations, except fee op to sdk.Msgs
|
||||
msgs, err := opsToMsgs(interfaceRegistry, newOps)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return msgs, amountsToCoins(feeAmnt), nil
|
||||
}
|
||||
|
||||
// amountsToCoins converts rosetta amounts to sdk coins
|
||||
func amountsToCoins(amounts []*types.Amount) sdk.Coins {
|
||||
var feeCoins sdk.Coins
|
||||
|
||||
for _, amount := range amounts {
|
||||
absValue := strings.Trim(amount.Value, "-")
|
||||
value, err := strconv.ParseInt(absValue, 10, 64)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
coin := sdk.NewCoin(amount.Currency.Symbol, sdk.NewInt(value))
|
||||
feeCoins = append(feeCoins, coin)
|
||||
}
|
||||
|
||||
return feeCoins
|
||||
}
|
||||
|
||||
func opsToMsgs(interfaceRegistry jsonpb.AnyResolver, ops []*types.Operation) ([]sdk.Msg, error) {
|
||||
var msgs []sdk.Msg
|
||||
var operationsByType = make(map[string][]*types.Operation)
|
||||
for _, op := range ops {
|
||||
operationsByType[op.Type] = append(operationsByType[op.Type], op)
|
||||
}
|
||||
|
||||
for opName, operations := range operationsByType {
|
||||
if opName == OperationFee {
|
||||
continue
|
||||
}
|
||||
|
||||
msgType, err := interfaceRegistry.Resolve("/" + opName) // Types are registered as /proto-name in the interface registry.
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if rosettaMsg, ok := msgType.(Msg); ok {
|
||||
m, err := rosettaMsg.FromOperations(operations)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
msgs = append(msgs, m)
|
||||
}
|
||||
}
|
||||
|
||||
return msgs, nil
|
||||
}
|
||||
41
server/rosetta/types.go
Normal file
41
server/rosetta/types.go
Normal file
@ -0,0 +1,41 @@
|
||||
package rosetta
|
||||
|
||||
import (
|
||||
"github.com/coinbase/rosetta-sdk-go/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// statuses
|
||||
const (
|
||||
StatusSuccess = "Success"
|
||||
StatusReverted = "Reverted"
|
||||
StageSynced = "synced"
|
||||
StageSyncing = "syncing"
|
||||
)
|
||||
|
||||
// misc
|
||||
const (
|
||||
Log = "log"
|
||||
)
|
||||
|
||||
// operations
|
||||
const (
|
||||
OperationFee = "fee"
|
||||
)
|
||||
|
||||
// options
|
||||
const (
|
||||
OptionAccountNumber = "account_number"
|
||||
OptionAddress = "address"
|
||||
OptionChainID = "chain_id"
|
||||
OptionSequence = "sequence"
|
||||
OptionMemo = "memo"
|
||||
OptionGas = "gas"
|
||||
)
|
||||
|
||||
type Msg interface {
|
||||
sdk.Msg
|
||||
ToOperations(withStatus, hasError bool) []*types.Operation
|
||||
FromOperations(ops []*types.Operation) (sdk.Msg, error)
|
||||
}
|
||||
112
server/rosetta/util.go
Normal file
112
server/rosetta/util.go
Normal file
@ -0,0 +1,112 @@
|
||||
package rosetta
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/coinbase/rosetta-sdk-go/types"
|
||||
|
||||
tmcoretypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// tmResultTxsToSdkTxsWithHash converts tendermint result txs to cosmos sdk.Tx
|
||||
func tmResultTxsToSdkTxsWithHash(decode sdk.TxDecoder, txs []*tmcoretypes.ResultTx) ([]*sdkTxWithHash, error) {
|
||||
converted := make([]*sdkTxWithHash, len(txs))
|
||||
for i, tx := range txs {
|
||||
sdkTx, err := decode(tx.Tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
converted[i] = &sdkTxWithHash{
|
||||
HexHash: fmt.Sprintf("%X", tx.Tx.Hash()),
|
||||
Code: tx.TxResult.Code,
|
||||
Log: tx.TxResult.Log,
|
||||
Tx: sdkTx,
|
||||
}
|
||||
}
|
||||
|
||||
return converted, nil
|
||||
}
|
||||
|
||||
func tmTxToSdkTx(decode sdk.TxDecoder, tx tmtypes.Tx) (sdk.Tx, error) {
|
||||
sdkTx, err := decode(tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return sdkTx, err
|
||||
}
|
||||
|
||||
type sdkTxWithHash struct {
|
||||
HexHash string
|
||||
Code uint32
|
||||
Log string
|
||||
Tx sdk.Tx
|
||||
}
|
||||
|
||||
type PayloadReqMetadata struct {
|
||||
ChainID string
|
||||
Sequence uint64
|
||||
AccountNumber uint64
|
||||
Gas uint64
|
||||
Memo string
|
||||
}
|
||||
|
||||
// getMetadataFromPayloadReq obtains the metadata from the request to /construction/payloads endpoint.
|
||||
func getMetadataFromPayloadReq(req *types.ConstructionPayloadsRequest) (*PayloadReqMetadata, error) {
|
||||
chainID, ok := req.Metadata[OptionChainID].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("chain_id metadata was not provided")
|
||||
}
|
||||
|
||||
sequence, ok := req.Metadata[OptionSequence]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("sequence metadata was not provided")
|
||||
}
|
||||
|
||||
seqNum, ok := sequence.(float64)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid sequence value")
|
||||
}
|
||||
|
||||
accountNum, ok := req.Metadata[OptionAccountNumber]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("account_number metadata was not provided")
|
||||
}
|
||||
|
||||
accNum, ok := accountNum.(float64)
|
||||
if !ok {
|
||||
fmt.Printf("this is type %T", accountNum)
|
||||
return nil, fmt.Errorf("invalid account_number value")
|
||||
}
|
||||
|
||||
gasNum, ok := req.Metadata[OptionGas]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("gas metadata was not provided")
|
||||
}
|
||||
|
||||
gasF64, ok := gasNum.(float64)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid gas value")
|
||||
}
|
||||
|
||||
memo, ok := req.Metadata[OptionMemo]
|
||||
if !ok {
|
||||
memo = ""
|
||||
}
|
||||
|
||||
memoStr, ok := memo.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid memo")
|
||||
}
|
||||
|
||||
return &PayloadReqMetadata{
|
||||
ChainID: chainID,
|
||||
Sequence: uint64(seqNum),
|
||||
AccountNumber: uint64(accNum),
|
||||
Gas: uint64(gasF64),
|
||||
Memo: memoStr,
|
||||
}, nil
|
||||
}
|
||||
@ -9,7 +9,14 @@ import (
|
||||
"runtime/pprof"
|
||||
"time"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/server/rosetta"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
crgserver "github.com/tendermint/cosmos-rosetta-gateway/server"
|
||||
"github.com/tendermint/tendermint/abci/server"
|
||||
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
|
||||
tmos "github.com/tendermint/tendermint/libs/os"
|
||||
@ -18,7 +25,6 @@ import (
|
||||
pvm "github.com/tendermint/tendermint/privval"
|
||||
"github.com/tendermint/tendermint/proxy"
|
||||
"github.com/tendermint/tendermint/rpc/client/local"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
@ -280,7 +286,6 @@ func startInProcess(ctx *Context, clientCtx client.Context, appCreator types.App
|
||||
}
|
||||
|
||||
var apiSrv *api.Server
|
||||
|
||||
if config.API.Enable {
|
||||
genDoc, err := genDocProvider()
|
||||
if err != nil {
|
||||
@ -326,6 +331,42 @@ func startInProcess(ctx *Context, clientCtx client.Context, appCreator types.App
|
||||
}
|
||||
}
|
||||
|
||||
var rosettaSrv crgserver.Server
|
||||
if config.Rosetta.Enable {
|
||||
offlineMode := config.Rosetta.Offline
|
||||
if !config.GRPC.Enable { // If GRPC is not enabled rosetta cannot work in online mode, so it works in offline mode.
|
||||
offlineMode = true
|
||||
}
|
||||
|
||||
conf := &rosetta.Config{
|
||||
Blockchain: config.Rosetta.Blockchain,
|
||||
Network: config.Rosetta.Network,
|
||||
TendermintRPC: ctx.Config.RPC.ListenAddress,
|
||||
GRPCEndpoint: config.GRPC.Address,
|
||||
Addr: config.Rosetta.Address,
|
||||
Retries: config.Rosetta.Retries,
|
||||
Offline: offlineMode,
|
||||
}
|
||||
conf.WithCodec(clientCtx.InterfaceRegistry, clientCtx.JSONMarshaler.(*codec.ProtoCodec))
|
||||
|
||||
rosettaSrv, err = rosetta.ServerFromConfig(conf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
errCh := make(chan error)
|
||||
go func() {
|
||||
if err := rosettaSrv.Start(); err != nil {
|
||||
errCh <- err
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-errCh:
|
||||
return err
|
||||
case <-time.After(5 * time.Second): // assume server started successfully
|
||||
}
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if tmNode.IsRunning() {
|
||||
_ = tmNode.Stop()
|
||||
|
||||
@ -90,6 +90,9 @@ func initRootCmd(rootCmd *cobra.Command, encodingConfig params.EncodingConfig) {
|
||||
txCommand(),
|
||||
keys.Commands(simapp.DefaultNodeHome),
|
||||
)
|
||||
|
||||
// add rosetta
|
||||
rootCmd.AddCommand(server.RosettaCommand(encodingConfig.InterfaceRegistry, encodingConfig.Marshaler))
|
||||
}
|
||||
|
||||
func addModuleInitFlags(startCmd *cobra.Command) {
|
||||
|
||||
@ -132,6 +132,37 @@ func (t *Tx) GetSigners() []sdk.AccAddress {
|
||||
return signers
|
||||
}
|
||||
|
||||
func (t *Tx) GetGas() uint64 {
|
||||
return t.AuthInfo.Fee.GasLimit
|
||||
}
|
||||
func (t *Tx) GetFee() sdk.Coins {
|
||||
return t.AuthInfo.Fee.Amount
|
||||
}
|
||||
func (t *Tx) FeePayer() sdk.AccAddress {
|
||||
feePayer := t.AuthInfo.Fee.Payer
|
||||
if feePayer != "" {
|
||||
payerAddr, err := sdk.AccAddressFromBech32(feePayer)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return payerAddr
|
||||
}
|
||||
// use first signer as default if no payer specified
|
||||
return t.GetSigners()[0]
|
||||
}
|
||||
|
||||
func (t *Tx) FeeGranter() sdk.AccAddress {
|
||||
feePayer := t.AuthInfo.Fee.Granter
|
||||
if feePayer != "" {
|
||||
granterAddr, err := sdk.AccAddressFromBech32(feePayer)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return granterAddr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnpackInterfaces implements the UnpackInterfaceMessages.UnpackInterfaces method
|
||||
func (t *Tx) UnpackInterfaces(unpacker codectypes.AnyUnpacker) error {
|
||||
if t.Body != nil {
|
||||
|
||||
@ -435,7 +435,7 @@ func (s *IntegrationTestSuite) TestCLISendGenerateSignAndBroadcast() {
|
||||
// Write the output to disk
|
||||
signedTxFile := testutil.WriteToNewTempFile(s.T(), signedTx.String())
|
||||
|
||||
// Validate Signature
|
||||
// validate Signature
|
||||
res, err = authtest.TxValidateSignaturesExec(val1.ClientCtx, signedTxFile.Name())
|
||||
s.Require().NoError(err)
|
||||
s.Require().True(strings.Contains(res.String(), "[OK]"))
|
||||
|
||||
@ -16,7 +16,7 @@ import (
|
||||
func GetValidateSignaturesCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "validate-signatures [file]",
|
||||
Short: "Validate transactions signatures",
|
||||
Short: "validate transactions signatures",
|
||||
Long: `Print the addresses that must sign the transaction, those who have already
|
||||
signed it, and make sure that signatures are in the correct order.
|
||||
|
||||
@ -96,7 +96,7 @@ func printAndValidateSigs(
|
||||
success = false
|
||||
}
|
||||
|
||||
// Validate the actual signature over the transaction bytes since we can
|
||||
// validate the actual signature over the transaction bytes since we can
|
||||
// reach out to a full node to query accounts.
|
||||
if !offline && success {
|
||||
accNum, accSeq, err := clientCtx.AccountRetriever.GetAccountNumberSequence(clientCtx, sigAddr)
|
||||
|
||||
@ -1,6 +1,13 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/coinbase/rosetta-sdk-go/types"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
)
|
||||
@ -62,6 +69,83 @@ func (msg MsgSend) GetSigners() []sdk.AccAddress {
|
||||
return []sdk.AccAddress{from}
|
||||
}
|
||||
|
||||
// Rosetta interface
|
||||
func (msg *MsgSend) ToOperations(withStatus bool, hasError bool) []*types.Operation {
|
||||
var operations []*types.Operation
|
||||
|
||||
fromAddress := msg.FromAddress
|
||||
toAddress := msg.ToAddress
|
||||
amounts := msg.Amount
|
||||
if len(amounts) == 0 {
|
||||
return []*types.Operation{}
|
||||
}
|
||||
|
||||
coin := amounts[0]
|
||||
sendOp := func(account, amount string, index int) *types.Operation {
|
||||
var status string
|
||||
if withStatus {
|
||||
status = "Success"
|
||||
if hasError {
|
||||
status = "Reverted"
|
||||
}
|
||||
}
|
||||
return &types.Operation{
|
||||
OperationIdentifier: &types.OperationIdentifier{
|
||||
Index: int64(index),
|
||||
},
|
||||
Type: proto.MessageName(msg),
|
||||
Status: status,
|
||||
Account: &types.AccountIdentifier{
|
||||
Address: account,
|
||||
},
|
||||
Amount: &types.Amount{
|
||||
Value: amount,
|
||||
Currency: &types.Currency{
|
||||
Symbol: coin.Denom,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
operations = append(operations,
|
||||
sendOp(fromAddress, "-"+coin.Amount.String(), 0),
|
||||
sendOp(toAddress, coin.Amount.String(), 1),
|
||||
)
|
||||
|
||||
return operations
|
||||
}
|
||||
|
||||
func (msg MsgSend) FromOperations(ops []*types.Operation) (sdk.Msg, error) {
|
||||
var (
|
||||
from, to sdk.AccAddress
|
||||
sendAmt sdk.Coin
|
||||
err error
|
||||
)
|
||||
|
||||
for _, op := range ops {
|
||||
if strings.HasPrefix(op.Amount.Value, "-") {
|
||||
from, err = sdk.AccAddressFromBech32(op.Account.Address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
to, err = sdk.AccAddressFromBech32(op.Account.Address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
amount, err := strconv.ParseInt(op.Amount.Value, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid amount")
|
||||
}
|
||||
|
||||
sendAmt = sdk.NewCoin(op.Amount.Currency.Symbol, sdk.NewInt(amount))
|
||||
}
|
||||
|
||||
return NewMsgSend(from, to, sdk.NewCoins(sendAmt)), nil
|
||||
}
|
||||
|
||||
var _ sdk.Msg = &MsgMultiSend{}
|
||||
|
||||
// NewMsgMultiSend - construct arbitrary multi-in, multi-out send msg.
|
||||
|
||||
@ -2,6 +2,12 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
rosettatypes "github.com/coinbase/rosetta-sdk-go/types"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/server/rosetta"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
)
|
||||
@ -90,6 +96,50 @@ func (msg MsgWithdrawDelegatorReward) ValidateBasic() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (msg *MsgWithdrawDelegatorReward) ToOperations(withStatus, hasError bool) []*rosettatypes.Operation {
|
||||
|
||||
var status string
|
||||
if withStatus {
|
||||
status = rosetta.StatusSuccess
|
||||
if hasError {
|
||||
status = rosetta.StatusReverted
|
||||
}
|
||||
}
|
||||
|
||||
op := &rosettatypes.Operation{
|
||||
OperationIdentifier: &rosettatypes.OperationIdentifier{
|
||||
Index: 0,
|
||||
},
|
||||
RelatedOperations: nil,
|
||||
Type: proto.MessageName(msg),
|
||||
Status: status,
|
||||
Account: &rosettatypes.AccountIdentifier{
|
||||
Address: msg.DelegatorAddress,
|
||||
SubAccount: &rosettatypes.SubAccountIdentifier{
|
||||
Address: msg.ValidatorAddress,
|
||||
},
|
||||
},
|
||||
}
|
||||
return []*rosettatypes.Operation{op}
|
||||
}
|
||||
|
||||
func (msg *MsgWithdrawDelegatorReward) FromOperations(ops []*rosettatypes.Operation) (sdk.Msg, error) {
|
||||
if len(ops) != 1 {
|
||||
return nil, fmt.Errorf("expected one operation")
|
||||
}
|
||||
op := ops[0]
|
||||
if op.Account == nil {
|
||||
return nil, fmt.Errorf("account identifier must be specified")
|
||||
}
|
||||
if op.Account.SubAccount == nil {
|
||||
return nil, fmt.Errorf("account identifier subaccount must be specified")
|
||||
}
|
||||
return &MsgWithdrawDelegatorReward{
|
||||
DelegatorAddress: op.Account.Address,
|
||||
ValidatorAddress: op.Account.SubAccount.Address,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewMsgWithdrawValidatorCommission(valAddr sdk.ValAddress) *MsgWithdrawValidatorCommission {
|
||||
return &MsgWithdrawValidatorCommission{
|
||||
ValidatorAddress: valAddr.String(),
|
||||
|
||||
@ -72,7 +72,7 @@ func (s *IntegrationTestSuite) TestGenTxCmd() {
|
||||
err := cmd.ExecuteContext(ctx)
|
||||
s.Require().NoError(err)
|
||||
|
||||
// Validate generated transaction.
|
||||
// validate generated transaction.
|
||||
open, err := os.Open(genTxFile)
|
||||
s.Require().NoError(err)
|
||||
|
||||
|
||||
@ -138,7 +138,7 @@ func (suite *MsgTestSuite) TestNewMsgConnectionOpenTry() {
|
||||
{"invalid counterparty client ID", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "connectiontotest", "test/conn1", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false},
|
||||
{"invalid nil counterparty client", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "connectiontotest", "clienttotest", nil, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false},
|
||||
{"invalid client unpacking", &types.MsgConnectionOpenTry{connectionID, "clienttotesta", invalidAny, counterparty, 500, []*types.Version{ibctesting.ConnectionVersion}, clientHeight, suite.proof, suite.proof, suite.proof, clientHeight, signer.String()}, false},
|
||||
{"counterparty failed Validate", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "connectiontotest", "clienttotest", invalidClient, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false},
|
||||
{"counterparty failed validate", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "connectiontotest", "clienttotest", invalidClient, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false},
|
||||
{"empty counterparty prefix", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "connectiontotest", "clienttotest", clientState, emptyPrefix, []*types.Version{ibctesting.ConnectionVersion}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false},
|
||||
{"empty counterpartyVersions", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{}, 500, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false},
|
||||
{"empty proofInit", types.NewMsgConnectionOpenTry(connectionID, "clienttotesta", "connectiontotest", "clienttotest", clientState, prefix, []*types.Version{ibctesting.ConnectionVersion}, 500, emptyProof, suite.proof, suite.proof, clientHeight, clientHeight, signer), false},
|
||||
@ -188,7 +188,7 @@ func (suite *MsgTestSuite) TestNewMsgConnectionOpenAck() {
|
||||
{"invalid counterparty connection ID", types.NewMsgConnectionOpenAck(connectionID, "test/conn1", clientState, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, ibctesting.ConnectionVersion, signer), false},
|
||||
{"invalid nil counterparty client", types.NewMsgConnectionOpenAck(connectionID, connectionID, nil, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, ibctesting.ConnectionVersion, signer), false},
|
||||
{"invalid unpacking counterparty client", &types.MsgConnectionOpenAck{connectionID, connectionID, ibctesting.ConnectionVersion, invalidAny, clientHeight, suite.proof, suite.proof, suite.proof, clientHeight, signer.String()}, false},
|
||||
{"counterparty client failed Validate", types.NewMsgConnectionOpenAck(connectionID, connectionID, invalidClient, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, ibctesting.ConnectionVersion, signer), false},
|
||||
{"counterparty client failed validate", types.NewMsgConnectionOpenAck(connectionID, connectionID, invalidClient, suite.proof, suite.proof, suite.proof, clientHeight, clientHeight, ibctesting.ConnectionVersion, signer), false},
|
||||
{"empty proofTry", types.NewMsgConnectionOpenAck(connectionID, connectionID, clientState, emptyProof, suite.proof, suite.proof, clientHeight, clientHeight, ibctesting.ConnectionVersion, signer), false},
|
||||
{"empty proofClient", types.NewMsgConnectionOpenAck(connectionID, connectionID, clientState, suite.proof, emptyProof, suite.proof, clientHeight, clientHeight, ibctesting.ConnectionVersion, signer), false},
|
||||
{"empty proofConsensus", types.NewMsgConnectionOpenAck(connectionID, connectionID, clientState, suite.proof, suite.proof, emptyProof, clientHeight, clientHeight, ibctesting.ConnectionVersion, signer), false},
|
||||
|
||||
@ -3,9 +3,8 @@ package rest
|
||||
import (
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/rest"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/rest"
|
||||
)
|
||||
|
||||
func RegisterHandlers(clientCtx client.Context, rtr *mux.Router) {
|
||||
|
||||
@ -3,9 +3,8 @@ package rest
|
||||
import (
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/rest"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/rest"
|
||||
)
|
||||
|
||||
func RegisterHandlers(clientCtx client.Context, rtr *mux.Router) {
|
||||
|
||||
@ -2,9 +2,17 @@ package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
|
||||
rosettatypes "github.com/coinbase/rosetta-sdk-go/types"
|
||||
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
|
||||
"github.com/cosmos/cosmos-sdk/server/rosetta"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
||||
)
|
||||
@ -249,6 +257,90 @@ func (msg MsgDelegate) ValidateBasic() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Rosetta Msg interface.
|
||||
func (msg *MsgDelegate) ToOperations(withStatus bool, hasError bool) []*rosettatypes.Operation {
|
||||
var operations []*rosettatypes.Operation
|
||||
delAddr := msg.DelegatorAddress
|
||||
valAddr := msg.ValidatorAddress
|
||||
coin := msg.Amount
|
||||
delOp := func(account *rosettatypes.AccountIdentifier, amount string, index int) *rosettatypes.Operation {
|
||||
var status string
|
||||
if withStatus {
|
||||
status = rosetta.StatusSuccess
|
||||
if hasError {
|
||||
status = rosetta.StatusReverted
|
||||
}
|
||||
}
|
||||
return &rosettatypes.Operation{
|
||||
OperationIdentifier: &rosettatypes.OperationIdentifier{
|
||||
Index: int64(index),
|
||||
},
|
||||
Type: proto.MessageName(msg),
|
||||
Status: status,
|
||||
Account: account,
|
||||
Amount: &rosettatypes.Amount{
|
||||
Value: amount,
|
||||
Currency: &rosettatypes.Currency{
|
||||
Symbol: coin.Denom,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
delAcc := &rosettatypes.AccountIdentifier{
|
||||
Address: delAddr,
|
||||
}
|
||||
valAcc := &rosettatypes.AccountIdentifier{
|
||||
Address: "staking_account",
|
||||
SubAccount: &rosettatypes.SubAccountIdentifier{
|
||||
Address: valAddr,
|
||||
},
|
||||
}
|
||||
operations = append(operations,
|
||||
delOp(delAcc, "-"+coin.Amount.String(), 0),
|
||||
delOp(valAcc, coin.Amount.String(), 1),
|
||||
)
|
||||
return operations
|
||||
}
|
||||
|
||||
func (msg *MsgDelegate) FromOperations(ops []*rosettatypes.Operation) (sdk.Msg, error) {
|
||||
var (
|
||||
delAddr sdk.AccAddress
|
||||
valAddr sdk.ValAddress
|
||||
sendAmt sdk.Coin
|
||||
err error
|
||||
)
|
||||
|
||||
for _, op := range ops {
|
||||
if strings.HasPrefix(op.Amount.Value, "-") {
|
||||
if op.Account == nil {
|
||||
return nil, fmt.Errorf("account identifier must be specified")
|
||||
}
|
||||
delAddr, err = sdk.AccAddressFromBech32(op.Account.Address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if op.Account.SubAccount == nil {
|
||||
return nil, fmt.Errorf("account identifier subaccount must be specified")
|
||||
}
|
||||
valAddr, err = sdk.ValAddressFromBech32(op.Account.SubAccount.Address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
amount, err := strconv.ParseInt(op.Amount.Value, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid amount: %w", err)
|
||||
}
|
||||
|
||||
sendAmt = sdk.NewCoin(op.Amount.Currency.Symbol, sdk.NewInt(amount))
|
||||
}
|
||||
|
||||
return NewMsgDelegate(delAddr, valAddr, sendAmt), nil
|
||||
}
|
||||
|
||||
// NewMsgBeginRedelegate creates a new MsgBeginRedelegate instance.
|
||||
//nolint:interfacer
|
||||
func NewMsgBeginRedelegate(
|
||||
@ -304,6 +396,103 @@ func (msg MsgBeginRedelegate) ValidateBasic() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Rosetta Msg interface.
|
||||
func (msg *MsgBeginRedelegate) ToOperations(withStatus bool, hasError bool) []*rosettatypes.Operation {
|
||||
var operations []*rosettatypes.Operation
|
||||
delAddr := msg.DelegatorAddress
|
||||
srcValAddr := msg.ValidatorSrcAddress
|
||||
destValAddr := msg.ValidatorDstAddress
|
||||
coin := msg.Amount
|
||||
delOp := func(account *rosettatypes.AccountIdentifier, amount string, index int) *rosettatypes.Operation {
|
||||
var status string
|
||||
if withStatus {
|
||||
status = rosetta.StatusSuccess
|
||||
if hasError {
|
||||
status = rosetta.StatusReverted
|
||||
}
|
||||
}
|
||||
return &rosettatypes.Operation{
|
||||
OperationIdentifier: &rosettatypes.OperationIdentifier{
|
||||
Index: int64(index),
|
||||
},
|
||||
Type: proto.MessageName(msg),
|
||||
Status: status,
|
||||
Account: account,
|
||||
Amount: &rosettatypes.Amount{
|
||||
Value: amount,
|
||||
Currency: &rosettatypes.Currency{
|
||||
Symbol: coin.Denom,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
srcValAcc := &rosettatypes.AccountIdentifier{
|
||||
Address: delAddr,
|
||||
SubAccount: &rosettatypes.SubAccountIdentifier{
|
||||
Address: srcValAddr,
|
||||
},
|
||||
}
|
||||
destValAcc := &rosettatypes.AccountIdentifier{
|
||||
Address: "staking_account",
|
||||
SubAccount: &rosettatypes.SubAccountIdentifier{
|
||||
Address: destValAddr,
|
||||
},
|
||||
}
|
||||
operations = append(operations,
|
||||
delOp(srcValAcc, "-"+coin.Amount.String(), 0),
|
||||
delOp(destValAcc, coin.Amount.String(), 1),
|
||||
)
|
||||
return operations
|
||||
}
|
||||
|
||||
func (msg *MsgBeginRedelegate) FromOperations(ops []*rosettatypes.Operation) (sdk.Msg, error) {
|
||||
var (
|
||||
delAddr sdk.AccAddress
|
||||
srcValAddr sdk.ValAddress
|
||||
destValAddr sdk.ValAddress
|
||||
sendAmt sdk.Coin
|
||||
err error
|
||||
)
|
||||
|
||||
for _, op := range ops {
|
||||
if strings.HasPrefix(op.Amount.Value, "-") {
|
||||
if op.Account == nil {
|
||||
return nil, fmt.Errorf("account identifier must be specified")
|
||||
}
|
||||
delAddr, err = sdk.AccAddressFromBech32(op.Account.Address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if op.Account.SubAccount == nil {
|
||||
return nil, fmt.Errorf("account identifier subaccount must be specified")
|
||||
}
|
||||
srcValAddr, err = sdk.ValAddressFromBech32(op.Account.SubAccount.Address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if op.Account.SubAccount == nil {
|
||||
return nil, fmt.Errorf("account identifier subaccount must be specified")
|
||||
}
|
||||
destValAddr, err = sdk.ValAddressFromBech32(op.Account.SubAccount.Address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
amount, err := strconv.ParseInt(op.Amount.Value, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid amount: %w", err)
|
||||
}
|
||||
|
||||
sendAmt = sdk.NewCoin(op.Amount.Currency.Symbol, sdk.NewInt(amount))
|
||||
}
|
||||
|
||||
return NewMsgBeginRedelegate(delAddr, srcValAddr, destValAddr, sendAmt), nil
|
||||
}
|
||||
|
||||
// NewMsgUndelegate creates a new MsgUndelegate instance.
|
||||
//nolint:interfacer
|
||||
func NewMsgUndelegate(delAddr sdk.AccAddress, valAddr sdk.ValAddress, amount sdk.Coin) *MsgUndelegate {
|
||||
@ -351,3 +540,88 @@ func (msg MsgUndelegate) ValidateBasic() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Rosetta Msg interface.
|
||||
func (msg *MsgUndelegate) ToOperations(withStatus bool, hasError bool) []*rosettatypes.Operation {
|
||||
var operations []*rosettatypes.Operation
|
||||
delAddr := msg.DelegatorAddress
|
||||
valAddr := msg.ValidatorAddress
|
||||
coin := msg.Amount
|
||||
delOp := func(account *rosettatypes.AccountIdentifier, amount string, index int) *rosettatypes.Operation {
|
||||
var status string
|
||||
if withStatus {
|
||||
status = rosetta.StatusSuccess
|
||||
if hasError {
|
||||
status = rosetta.StatusReverted
|
||||
}
|
||||
}
|
||||
return &rosettatypes.Operation{
|
||||
OperationIdentifier: &rosettatypes.OperationIdentifier{
|
||||
Index: int64(index),
|
||||
},
|
||||
Type: proto.MessageName(msg),
|
||||
Status: status,
|
||||
Account: account,
|
||||
Amount: &rosettatypes.Amount{
|
||||
Value: amount,
|
||||
Currency: &rosettatypes.Currency{
|
||||
Symbol: coin.Denom,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
delAcc := &rosettatypes.AccountIdentifier{
|
||||
Address: delAddr,
|
||||
}
|
||||
valAcc := &rosettatypes.AccountIdentifier{
|
||||
Address: "staking_account",
|
||||
SubAccount: &rosettatypes.SubAccountIdentifier{
|
||||
Address: valAddr,
|
||||
},
|
||||
}
|
||||
operations = append(operations,
|
||||
delOp(valAcc, "-"+coin.Amount.String(), 0),
|
||||
delOp(delAcc, coin.Amount.String(), 1),
|
||||
)
|
||||
return operations
|
||||
}
|
||||
|
||||
func (msg *MsgUndelegate) FromOperations(ops []*rosettatypes.Operation) (sdk.Msg, error) {
|
||||
var (
|
||||
delAddr sdk.AccAddress
|
||||
valAddr sdk.ValAddress
|
||||
undelAmt sdk.Coin
|
||||
err error
|
||||
)
|
||||
|
||||
for _, op := range ops {
|
||||
if strings.HasPrefix(op.Amount.Value, "-") {
|
||||
if op.Account.SubAccount == nil {
|
||||
return nil, fmt.Errorf("account identifier subaccount must be specified")
|
||||
}
|
||||
valAddr, err = sdk.ValAddressFromBech32(op.Account.SubAccount.Address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if op.Account == nil {
|
||||
return nil, fmt.Errorf("account identifier must be specified")
|
||||
}
|
||||
|
||||
delAddr, err = sdk.AccAddressFromBech32(op.Account.Address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
amount, err := strconv.ParseInt(op.Amount.Value, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid amount")
|
||||
}
|
||||
|
||||
undelAmt = sdk.NewCoin(op.Amount.Currency.Symbol, sdk.NewInt(amount))
|
||||
}
|
||||
|
||||
return NewMsgUndelegate(delAddr, valAddr, undelAmt), nil
|
||||
}
|
||||
|
||||
@ -6,6 +6,11 @@ package types
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
io "io"
|
||||
math "math"
|
||||
math_bits "math/bits"
|
||||
time "time"
|
||||
|
||||
types "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types"
|
||||
types1 "github.com/cosmos/cosmos-sdk/types"
|
||||
@ -18,10 +23,6 @@ import (
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
io "io"
|
||||
math "math"
|
||||
math_bits "math/bits"
|
||||
time "time"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
|
||||
@ -3,9 +3,8 @@ package rest
|
||||
import (
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/rest"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/rest"
|
||||
)
|
||||
|
||||
// RegisterRoutes registers REST routes for the upgrade module under the path specified by routeName.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user