Basic RPC and CLI Queries (#77)
- Adds ethermint query command (`emintcli query ethermint <query>`) - Supports block number, storage, code, balance lookups - Implements RPC API methods `eth_blockNumber`, `eth_getStorageAt`, `eth_getBalance`, and `eth_getCode` - Adds tester utility for RPC calls - Adheres to go test format, but should not be run with regular suite - Requires daemon and RPC server to be running - Excluded from `make test`, available with `make test-rpc` - Implemented AppModule interface and added EVM module to app - Required for routing - Implements `InitGenesis` (`x/evm/genesis.go`) and stubs `ExportGenesis` - Modifies GenesisAccount to match expected format
This commit is contained in:
parent
d9d45b48b9
commit
92dc7d9a59
5
Makefile
5
Makefile
@ -12,7 +12,7 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
PACKAGES=$(shell go list ./... | grep -Ev 'vendor|importer')
|
PACKAGES=$(shell go list ./... | grep -Ev 'vendor|importer|rpc/tester')
|
||||||
COMMIT_HASH := $(shell git rev-parse --short HEAD)
|
COMMIT_HASH := $(shell git rev-parse --short HEAD)
|
||||||
BUILD_FLAGS = -tags netgo -ldflags "-X github.com/cosmos/ethermint/version.GitCommit=${COMMIT_HASH}"
|
BUILD_FLAGS = -tags netgo -ldflags "-X github.com/cosmos/ethermint/version.GitCommit=${COMMIT_HASH}"
|
||||||
DOCKER_TAG = unstable
|
DOCKER_TAG = unstable
|
||||||
@ -146,6 +146,9 @@ test-import:
|
|||||||
--blockchain blockchain --timeout=5m
|
--blockchain blockchain --timeout=5m
|
||||||
# TODO: remove tmp directory after test run to avoid subsequent errors
|
# TODO: remove tmp directory after test run to avoid subsequent errors
|
||||||
|
|
||||||
|
test-rpc:
|
||||||
|
@${GO_MOD} go test -v --vet=off ./rpc/tester
|
||||||
|
|
||||||
godocs:
|
godocs:
|
||||||
@echo "--> Wait a few seconds and visit http://localhost:6060/pkg/github.com/cosmos/ethermint"
|
@echo "--> Wait a few seconds and visit http://localhost:6060/pkg/github.com/cosmos/ethermint"
|
||||||
godoc -http=:6060
|
godoc -http=:6060
|
||||||
|
@ -59,8 +59,7 @@ var (
|
|||||||
crisis.AppModuleBasic{},
|
crisis.AppModuleBasic{},
|
||||||
slashing.AppModuleBasic{},
|
slashing.AppModuleBasic{},
|
||||||
supply.AppModuleBasic{},
|
supply.AppModuleBasic{},
|
||||||
// TODO: Enable EVM AppModuleBasic
|
evm.AppModuleBasic{},
|
||||||
//evm.AppModuleBasic{},
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -185,7 +184,7 @@ func NewEthermintApp(logger tmlog.Logger, db dbm.DB, loadLatest bool,
|
|||||||
app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, &stakingKeeper,
|
app.slashingKeeper = slashing.NewKeeper(app.cdc, app.keySlashing, &stakingKeeper,
|
||||||
slashingSubspace, slashing.DefaultCodespace)
|
slashingSubspace, slashing.DefaultCodespace)
|
||||||
app.crisisKeeper = crisis.NewKeeper(crisisSubspace, invCheckPeriod, app.supplyKeeper, auth.FeeCollectorName)
|
app.crisisKeeper = crisis.NewKeeper(crisisSubspace, invCheckPeriod, app.supplyKeeper, auth.FeeCollectorName)
|
||||||
app.evmKeeper = evm.NewKeeper(app.accountKeeper, app.evmStoreKey, app.evmCodeKey)
|
app.evmKeeper = evm.NewKeeper(app.accountKeeper, app.evmStoreKey, app.evmCodeKey, cdc)
|
||||||
|
|
||||||
// register the proposal types
|
// register the proposal types
|
||||||
govRouter := gov.NewRouter()
|
govRouter := gov.NewRouter()
|
||||||
@ -212,6 +211,7 @@ func NewEthermintApp(logger tmlog.Logger, db dbm.DB, loadLatest bool,
|
|||||||
mint.NewAppModule(app.mintKeeper),
|
mint.NewAppModule(app.mintKeeper),
|
||||||
slashing.NewAppModule(app.slashingKeeper, app.stakingKeeper),
|
slashing.NewAppModule(app.slashingKeeper, app.stakingKeeper),
|
||||||
staking.NewAppModule(app.stakingKeeper, app.distrKeeper, app.accountKeeper, app.supplyKeeper),
|
staking.NewAppModule(app.stakingKeeper, app.distrKeeper, app.accountKeeper, app.supplyKeeper),
|
||||||
|
evm.NewAppModule(app.evmKeeper),
|
||||||
)
|
)
|
||||||
|
|
||||||
// During begin block slashing happens after distr.BeginBlocker so that
|
// During begin block slashing happens after distr.BeginBlocker so that
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/cosmos/ethermint/rpc"
|
||||||
|
"github.com/tendermint/go-amino"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
@ -10,7 +12,6 @@ import (
|
|||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
|
||||||
emintapp "github.com/cosmos/ethermint/app"
|
emintapp "github.com/cosmos/ethermint/app"
|
||||||
"github.com/cosmos/ethermint/rpc"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"github.com/tendermint/tendermint/libs/cli"
|
"github.com/tendermint/tendermint/libs/cli"
|
||||||
@ -43,7 +44,7 @@ func main() {
|
|||||||
rootCmd.AddCommand(
|
rootCmd.AddCommand(
|
||||||
sdkrpc.StatusCommand(),
|
sdkrpc.StatusCommand(),
|
||||||
client.ConfigCmd(emintapp.DefaultCLIHome),
|
client.ConfigCmd(emintapp.DefaultCLIHome),
|
||||||
// TODO: Set up query command
|
queryCmd(cdc),
|
||||||
// TODO: Set up tx command
|
// TODO: Set up tx command
|
||||||
// TODO: Set up rest routes (if included, different from web3 api)
|
// TODO: Set up rest routes (if included, different from web3 api)
|
||||||
rpc.Web3RpcCmd(cdc),
|
rpc.Web3RpcCmd(cdc),
|
||||||
@ -59,6 +60,24 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func queryCmd(cdc *amino.Codec) *cobra.Command {
|
||||||
|
queryCmd := &cobra.Command{
|
||||||
|
Use: "query",
|
||||||
|
Aliases: []string{"q"},
|
||||||
|
Short: "Querying subcommands",
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Possibly add these query commands from other modules
|
||||||
|
//queryCmd.AddCommand(
|
||||||
|
// ...
|
||||||
|
//)
|
||||||
|
|
||||||
|
// add modules' query commands
|
||||||
|
emintapp.ModuleBasics.AddQueryCommands(queryCmd, cdc)
|
||||||
|
|
||||||
|
return queryCmd
|
||||||
|
}
|
||||||
|
|
||||||
func initConfig(cmd *cobra.Command) error {
|
func initConfig(cmd *cobra.Command) error {
|
||||||
home, err := cmd.PersistentFlags().GetString(cli.HomeFlag)
|
home, err := cmd.PersistentFlags().GetString(cli.HomeFlag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
1
go.mod
1
go.mod
@ -46,6 +46,7 @@ require (
|
|||||||
github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 // indirect
|
github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3 // indirect
|
||||||
github.com/stretchr/testify v1.3.0
|
github.com/stretchr/testify v1.3.0
|
||||||
github.com/syndtr/goleveldb v1.0.0 // indirect
|
github.com/syndtr/goleveldb v1.0.0 // indirect
|
||||||
|
github.com/tendermint/go-amino v0.15.0
|
||||||
github.com/tendermint/tendermint v0.32.0
|
github.com/tendermint/tendermint v0.32.0
|
||||||
github.com/tyler-smith/go-bip39 v1.0.0 // indirect
|
github.com/tyler-smith/go-bip39 v1.0.0 // indirect
|
||||||
github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208 // indirect
|
github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208 // indirect
|
||||||
|
10
go.sum
10
go.sum
@ -39,15 +39,11 @@ github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW
|
|||||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||||
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
|
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
|
||||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||||
github.com/cosmos/cosmos-sdk v0.28.2-0.20190709220430-3f519832a7a5 h1:gakqjbZrqlUB1/rx8r/s86SVcRatOafVDfJF99yBcng=
|
|
||||||
github.com/cosmos/cosmos-sdk v0.28.2-0.20190709220430-3f519832a7a5/go.mod h1:qzvnGkt2+ynMpjmf9/dws/94/qM87awRbuyvF7r2R8Q=
|
|
||||||
github.com/cosmos/cosmos-sdk v0.28.2-0.20190711105643-280734d0e37f h1:jmVM19bsHZRVVe8rugzfILuL3VPgCj5b6941I20Naw0=
|
github.com/cosmos/cosmos-sdk v0.28.2-0.20190711105643-280734d0e37f h1:jmVM19bsHZRVVe8rugzfILuL3VPgCj5b6941I20Naw0=
|
||||||
github.com/cosmos/cosmos-sdk v0.28.2-0.20190711105643-280734d0e37f/go.mod h1:qzvnGkt2+ynMpjmf9/dws/94/qM87awRbuyvF7r2R8Q=
|
github.com/cosmos/cosmos-sdk v0.28.2-0.20190711105643-280734d0e37f/go.mod h1:qzvnGkt2+ynMpjmf9/dws/94/qM87awRbuyvF7r2R8Q=
|
||||||
github.com/cosmos/cosmos-sdk v0.35.0 h1:EPeie1aKHwnXtTzKggvabG7aAPN+DDmju2xquvjFwao=
|
|
||||||
github.com/cosmos/go-bip39 v0.0.0-20180618194314-52158e4697b8 h1:Iwin12wRQtyZhH6FV3ykFcdGNlYEzoeR0jN8Vn+JWsI=
|
github.com/cosmos/go-bip39 v0.0.0-20180618194314-52158e4697b8 h1:Iwin12wRQtyZhH6FV3ykFcdGNlYEzoeR0jN8Vn+JWsI=
|
||||||
github.com/cosmos/go-bip39 v0.0.0-20180618194314-52158e4697b8/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y=
|
github.com/cosmos/go-bip39 v0.0.0-20180618194314-52158e4697b8/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y=
|
||||||
github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d h1:49RLWk1j44Xu4fjHb6JFYmeUnDORVwHNkDxaQ0ctCVU=
|
github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d h1:49RLWk1j44Xu4fjHb6JFYmeUnDORVwHNkDxaQ0ctCVU=
|
||||||
@ -124,8 +120,6 @@ github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA=
|
|||||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/gorilla/mux v1.7.0 h1:tOSd0UKHQd6urX6ApfOn4XdBMY6Sh1MfxV3kmaazO+U=
|
github.com/gorilla/mux v1.7.0 h1:tOSd0UKHQd6urX6ApfOn4XdBMY6Sh1MfxV3kmaazO+U=
|
||||||
github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
|
|
||||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
|
||||||
github.com/gorilla/websocket v1.2.0 h1:VJtLvh6VQym50czpZzx07z/kw9EgAxI3x1ZB8taTMQQ=
|
github.com/gorilla/websocket v1.2.0 h1:VJtLvh6VQym50czpZzx07z/kw9EgAxI3x1ZB8taTMQQ=
|
||||||
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
||||||
@ -146,7 +140,6 @@ github.com/jackpal/go-nat-pmp v1.0.1/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+
|
|||||||
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U=
|
github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U=
|
||||||
github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ=
|
github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ=
|
||||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
|
||||||
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
|
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
@ -212,7 +205,6 @@ github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:
|
|||||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
|
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE=
|
||||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
|
||||||
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||||
github.com/prometheus/common v0.2.0 h1:kUZDBDTdBVBYBj5Tmh2NZLlF60mfjA27rM34b+cVwNU=
|
github.com/prometheus/common v0.2.0 h1:kUZDBDTdBVBYBj5Tmh2NZLlF60mfjA27rM34b+cVwNU=
|
||||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
@ -301,7 +293,6 @@ github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+m
|
|||||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||||
github.com/zondax/hid v0.9.0 h1:eiT3P6vNxAEVxXMw66eZUAAnU2zD33JBkfG/EnfAKl8=
|
github.com/zondax/hid v0.9.0 h1:eiT3P6vNxAEVxXMw66eZUAAnU2zD33JBkfG/EnfAKl8=
|
||||||
github.com/zondax/hid v0.9.0/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM=
|
github.com/zondax/hid v0.9.0/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM=
|
||||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
|
||||||
go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk=
|
go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk=
|
||||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||||
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
@ -340,7 +331,6 @@ golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7 h1:bit1t3mgdR35yN0cX0G8orgLtOuyL9Wqxa1mccLB0ig=
|
golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7 h1:bit1t3mgdR35yN0cX0G8orgLtOuyL9Wqxa1mccLB0ig=
|
||||||
golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
@ -1,77 +0,0 @@
|
|||||||
package rpc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
sdkcontext"github.com/cosmos/cosmos-sdk/client/context"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/cosmos/ethermint/version"
|
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
"github.com/stretchr/testify/suite"
|
|
||||||
)
|
|
||||||
|
|
||||||
type apisTestSuite struct {
|
|
||||||
suite.Suite
|
|
||||||
Stop context.CancelFunc
|
|
||||||
Port int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *apisTestSuite) SetupSuite() {
|
|
||||||
stop, port, err := startAPIServer()
|
|
||||||
require.Nil(s.T(), err, "unexpected error")
|
|
||||||
s.Stop = stop
|
|
||||||
s.Port = port
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *apisTestSuite) TearDownSuite() {
|
|
||||||
s.Stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *apisTestSuite) TestPublicWeb3APIClientVersion() {
|
|
||||||
res, err := rpcCall(s.Port, "web3_clientVersion", []string{})
|
|
||||||
require.Nil(s.T(), err, "unexpected error")
|
|
||||||
require.Equal(s.T(), version.ClientVersion(), res)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *apisTestSuite) TestPublicWeb3APISha3() {
|
|
||||||
res, err := rpcCall(s.Port, "web3_sha3", []string{"0x67656c6c6f20776f726c64"})
|
|
||||||
require.Nil(s.T(), err, "unexpected error")
|
|
||||||
require.Equal(s.T(), "0x1b84adea42d5b7d192fd8a61a85b25abe0757e9a65cab1da470258914053823f", res)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *apisTestSuite) TestMiningAPIs() {
|
|
||||||
res, err := rpcCall(s.Port, "eth_mining", nil)
|
|
||||||
require.Nil(s.T(), err, "unexpected error")
|
|
||||||
require.Equal(s.T(), false, res)
|
|
||||||
|
|
||||||
res, err = rpcCall(s.Port, "eth_hashrate", nil)
|
|
||||||
require.Nil(s.T(), err, "unexpected error")
|
|
||||||
require.Equal(s.T(), "0x0", res)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAPIsTestSuite(t *testing.T) {
|
|
||||||
suite.Run(t, new(apisTestSuite))
|
|
||||||
}
|
|
||||||
|
|
||||||
func startAPIServer() (context.CancelFunc, int, error) {
|
|
||||||
config := &Config{
|
|
||||||
RPCAddr: "127.0.0.1",
|
|
||||||
RPCPort: randomPort(),
|
|
||||||
}
|
|
||||||
timeouts := rpc.HTTPTimeouts{
|
|
||||||
ReadTimeout: 5 * time.Second,
|
|
||||||
WriteTimeout: 5 * time.Second,
|
|
||||||
IdleTimeout: 5 * time.Second,
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
|
|
||||||
_, err := StartHTTPEndpoint(ctx, config, GetRPCAPIs(sdkcontext.NewCLIContext()), timeouts)
|
|
||||||
if err != nil {
|
|
||||||
return cancel, 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return cancel, config.RPCPort, nil
|
|
||||||
}
|
|
@ -1,8 +1,10 @@
|
|||||||
package rpc
|
package rpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/cosmos/cosmos-sdk/client/context"
|
"github.com/cosmos/cosmos-sdk/client/context"
|
||||||
"github.com/cosmos/ethermint/version"
|
"github.com/cosmos/ethermint/version"
|
||||||
|
"github.com/cosmos/ethermint/x/evm/types"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
@ -11,7 +13,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// PublicEthAPI is the eth_ prefixed set of APIs in the Web3 JSON-RPC spec.
|
// PublicEthAPI is the eth_ prefixed set of APIs in the Web3 JSON-RPC spec.
|
||||||
type PublicEthAPI struct{
|
type PublicEthAPI struct {
|
||||||
cliCtx context.CLIContext
|
cliCtx context.CLIContext
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,18 +63,41 @@ func (e *PublicEthAPI) Accounts() []common.Address {
|
|||||||
|
|
||||||
// BlockNumber returns the current block number.
|
// BlockNumber returns the current block number.
|
||||||
func (e *PublicEthAPI) BlockNumber() *big.Int {
|
func (e *PublicEthAPI) BlockNumber() *big.Int {
|
||||||
return big.NewInt(0)
|
res, _, err := e.cliCtx.QueryWithData(fmt.Sprintf("custom/%s/blockNumber", types.ModuleName), nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("could not resolve: %s\n", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var out types.QueryResBlockNumber
|
||||||
|
e.cliCtx.Codec.MustUnmarshalJSON(res, &out)
|
||||||
|
return out.Number
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBalance returns the provided account's balance up to the provided block number.
|
// GetBalance returns the provided account's balance up to the provided block number.
|
||||||
func (e *PublicEthAPI) GetBalance(address common.Address, blockNum rpc.BlockNumber) *hexutil.Big {
|
func (e *PublicEthAPI) GetBalance(address common.Address, blockNum rpc.BlockNumber) *hexutil.Big {
|
||||||
out := big.NewInt(0)
|
res, _, err := e.cliCtx.QueryWithData(fmt.Sprintf("custom/%s/balance/%s", types.ModuleName, address), nil)
|
||||||
return (*hexutil.Big)(out)
|
if err != nil {
|
||||||
|
fmt.Printf("could not resolve: %s\n", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var out types.QueryResBalance
|
||||||
|
e.cliCtx.Codec.MustUnmarshalJSON(res, &out)
|
||||||
|
return (*hexutil.Big)(out.Balance)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStorageAt returns the contract storage at the given address, block number, and key.
|
// GetStorageAt returns the contract storage at the given address, block number, and key.
|
||||||
func (e *PublicEthAPI) GetStorageAt(address common.Address, key string, blockNum rpc.BlockNumber) hexutil.Bytes {
|
func (e *PublicEthAPI) GetStorageAt(address common.Address, key string, blockNum rpc.BlockNumber) hexutil.Bytes {
|
||||||
return nil
|
res, _, err := e.cliCtx.QueryWithData(fmt.Sprintf("custom/%s/storage/%s/%s", types.ModuleName, address, key), nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("could not resolve: %s\n", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var out types.QueryResStorage
|
||||||
|
e.cliCtx.Codec.MustUnmarshalJSON(res, &out)
|
||||||
|
return out.Value[:]
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTransactionCount returns the number of transactions at the given address up to the given block number.
|
// GetTransactionCount returns the number of transactions at the given address up to the given block number.
|
||||||
@ -102,7 +127,15 @@ func (e *PublicEthAPI) GetUncleCountByBlockNumber(blockNum rpc.BlockNumber) hexu
|
|||||||
|
|
||||||
// GetCode returns the contract code at the given address and block number.
|
// GetCode returns the contract code at the given address and block number.
|
||||||
func (e *PublicEthAPI) GetCode(address common.Address, blockNumber rpc.BlockNumber) hexutil.Bytes {
|
func (e *PublicEthAPI) GetCode(address common.Address, blockNumber rpc.BlockNumber) hexutil.Bytes {
|
||||||
return nil
|
res, _, err := e.cliCtx.QueryWithData(fmt.Sprintf("custom/%s/code/%s", types.ModuleName, address), nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("could not resolve: %s\n", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var out types.QueryResCode
|
||||||
|
e.cliCtx.Codec.MustUnmarshalJSON(res, &out)
|
||||||
|
return out.Code
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sign signs the provided data using the private key of address via Geth's signature standard.
|
// Sign signs the provided data using the private key of address via Geth's signature standard.
|
||||||
|
38
rpc/rpc.go
38
rpc/rpc.go
@ -1,38 +0,0 @@
|
|||||||
package rpc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
|
||||||
)
|
|
||||||
|
|
||||||
// StartHTTPEndpoint starts the Tendermint Web3-compatible RPC layer. Consumes
|
|
||||||
// a Context for cancellation, a config struct, and a list of rpc.API interfaces
|
|
||||||
// that will be automatically wired into a JSON-RPC webserver.
|
|
||||||
func StartHTTPEndpoint(ctx context.Context, config *Config, apis []rpc.API, timeouts rpc.HTTPTimeouts) (*rpc.Server, error) {
|
|
||||||
uniqModules := make(map[string]string)
|
|
||||||
for _, api := range apis {
|
|
||||||
uniqModules[api.Namespace] = api.Namespace
|
|
||||||
}
|
|
||||||
|
|
||||||
modules := make([]string, len(uniqModules))
|
|
||||||
i := 0
|
|
||||||
for k := range uniqModules {
|
|
||||||
modules[i] = k
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
|
|
||||||
endpoint := fmt.Sprintf("%s:%d", config.RPCAddr, config.RPCPort)
|
|
||||||
_, server, err := rpc.StartHTTPEndpoint(
|
|
||||||
endpoint, apis, modules, config.RPCCORSDomains, config.RPCVHosts, timeouts,
|
|
||||||
)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
<-ctx.Done()
|
|
||||||
fmt.Println("Shutting down server.")
|
|
||||||
server.Stop()
|
|
||||||
}()
|
|
||||||
|
|
||||||
return server, err
|
|
||||||
}
|
|
@ -1,94 +0,0 @@
|
|||||||
package rpc
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"math/rand"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TestService struct{}
|
|
||||||
|
|
||||||
func (s *TestService) Foo(arg string) string {
|
|
||||||
return arg
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStartHTTPEndpointStartStop(t *testing.T) {
|
|
||||||
config := &Config{
|
|
||||||
RPCAddr: "127.0.0.1",
|
|
||||||
RPCPort: randomPort(),
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
|
|
||||||
_, err := StartHTTPEndpoint(
|
|
||||||
ctx, config, []rpc.API{
|
|
||||||
{
|
|
||||||
Namespace: "test",
|
|
||||||
Version: "1.0",
|
|
||||||
Service: &TestService{},
|
|
||||||
Public: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
rpc.HTTPTimeouts{
|
|
||||||
ReadTimeout: 5 * time.Second,
|
|
||||||
WriteTimeout: 5 * time.Second,
|
|
||||||
IdleTimeout: 5 * time.Second,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
require.Nil(t, err, "unexpected error")
|
|
||||||
|
|
||||||
res, err := rpcCall(config.RPCPort, "test_foo", []string{"baz"})
|
|
||||||
require.Nil(t, err, "unexpected error")
|
|
||||||
|
|
||||||
resStr := res.(string)
|
|
||||||
require.Equal(t, "baz", resStr)
|
|
||||||
|
|
||||||
cancel()
|
|
||||||
|
|
||||||
_, err = rpcCall(config.RPCPort, "test_foo", []string{"baz"})
|
|
||||||
require.NotNil(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func rpcCall(port int, method string, params []string) (interface{}, error) {
|
|
||||||
parsedParams, err := json.Marshal(params)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
fullBody := fmt.Sprintf(
|
|
||||||
`{ "id": 1, "jsonrpc": "2.0", "method": "%s", "params": %s }`,
|
|
||||||
method, string(parsedParams),
|
|
||||||
)
|
|
||||||
|
|
||||||
res, err := http.Post(fmt.Sprintf("http://127.0.0.1:%d", port), "application/json", strings.NewReader(fullBody))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := ioutil.ReadAll(res.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var out map[string]interface{}
|
|
||||||
err = json.Unmarshal(data, &out)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
result := out["result"].(interface{})
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func randomPort() int {
|
|
||||||
return rand.Intn(65535-1025) + 1025
|
|
||||||
}
|
|
129
rpc/tester/tester_test.go
Normal file
129
rpc/tester/tester_test.go
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
// This is a test utility for Ethermint's Web3 JSON-RPC services.
|
||||||
|
//
|
||||||
|
// To run these tests please first ensure you have the emintd running
|
||||||
|
// and have started the RPC service with `emintcl rest-server`.
|
||||||
|
//
|
||||||
|
// You can configure the desired port (or host) below.
|
||||||
|
|
||||||
|
package tester
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/cosmos/ethermint/version"
|
||||||
|
"github.com/cosmos/ethermint/x/evm/types"
|
||||||
|
"io/ioutil"
|
||||||
|
"math/big"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
host = "127.0.0.1"
|
||||||
|
port = 1317
|
||||||
|
addrA = "0xc94770007dda54cF92009BFF0dE90c06F603a09f"
|
||||||
|
addrAStoreKey = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
var addr = fmt.Sprintf("http://%s:%d/rpc", host, port)
|
||||||
|
|
||||||
|
type Request struct {
|
||||||
|
Version string `json:"jsonrpc"`
|
||||||
|
Method string `json:"method"`
|
||||||
|
Params []string `json:"params"`
|
||||||
|
Id int `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func createRequest(method string, params []string) Request {
|
||||||
|
return Request{
|
||||||
|
Version: "2.0",
|
||||||
|
Method: method,
|
||||||
|
Params: params,
|
||||||
|
Id: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func call(t *testing.T, method string, params []string, resp interface{}) {
|
||||||
|
req, err := json.Marshal(createRequest(method, params))
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := http.Post(addr, "application/json", bytes.NewBuffer(req))
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(body, resp)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEth_protocolVersion(t *testing.T) {
|
||||||
|
expectedRes := version.ProtocolVersion
|
||||||
|
|
||||||
|
res := &types.QueryResProtocolVersion{}
|
||||||
|
call(t, "eth_protocolVersion", []string{}, res)
|
||||||
|
|
||||||
|
t.Logf("Got protocol version: %s\n", res.Version)
|
||||||
|
|
||||||
|
if res.Version != expectedRes {
|
||||||
|
t.Errorf("expected: %s got: %s\n", expectedRes, res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEth_blockNumber(t *testing.T) {
|
||||||
|
res := &types.QueryResBlockNumber{}
|
||||||
|
call(t, "eth_blockNumber", []string{}, res)
|
||||||
|
|
||||||
|
t.Logf("Got block number: %s\n", res.Number.String())
|
||||||
|
|
||||||
|
// -1 if x < y, 0 if x == y; where x is res, y is 0
|
||||||
|
if res.Number.Cmp(big.NewInt(0)) < 1 {
|
||||||
|
t.Errorf("Invalid block number got: %v", res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEth_GetBalance(t *testing.T) {
|
||||||
|
//expectedRes := types.QueryResBalance{Balance:}
|
||||||
|
res := &types.QueryResBalance{}
|
||||||
|
call(t, "eth_getBalance", []string{addrA, "latest"}, res)
|
||||||
|
|
||||||
|
t.Logf("Got balance %s for %s\n", res.Balance.String(), addrA)
|
||||||
|
|
||||||
|
// 0 if x == y; where x is res, y is 0
|
||||||
|
if res.Balance.ToInt().Cmp(big.NewInt(0)) != 0 {
|
||||||
|
t.Errorf("expected balance: %d, got: %s", 0, res.Balance.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEth_GetStorageAt(t *testing.T) {
|
||||||
|
expectedRes := types.QueryResStorage{Value: []byte{}}
|
||||||
|
res := &types.QueryResStorage{}
|
||||||
|
call(t, "eth_getStorageAt", []string{addrA, string(addrAStoreKey), "latest"}, res)
|
||||||
|
|
||||||
|
t.Logf("Got value [%X] for %s with key %X\n", res.Value, addrA, addrAStoreKey)
|
||||||
|
|
||||||
|
if !bytes.Equal(res.Value, expectedRes.Value) {
|
||||||
|
t.Errorf("expected: %X got: %X", expectedRes.Value, res.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEth_GetCode(t *testing.T) {
|
||||||
|
expectedRes := types.QueryResCode{Code: []byte{}}
|
||||||
|
res := &types.QueryResCode{}
|
||||||
|
call(t, "eth_getCode", []string{addrA, "latest"}, res)
|
||||||
|
|
||||||
|
t.Logf("Got code [%X] for %s\n", res.Code, addrA)
|
||||||
|
if !bytes.Equal(expectedRes.Code, res.Code) {
|
||||||
|
t.Errorf("expected: %X got: %X", expectedRes.Code, res.Code)
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// PublicWeb3API is the web3_ prefixed set of APIs in the Web3 JSON-RPC spec.
|
// PublicWeb3API is the web3_ prefixed set of APIs in the Web3 JSON-RPC spec.
|
||||||
type PublicWeb3API struct {}
|
type PublicWeb3API struct{}
|
||||||
|
|
||||||
// NewPublicWeb3API creates an instance of the Web3 API.
|
// NewPublicWeb3API creates an instance of the Web3 API.
|
||||||
func NewPublicWeb3API() *PublicWeb3API {
|
func NewPublicWeb3API() *PublicWeb3API {
|
||||||
|
96
x/evm/client/cli/query.go
Normal file
96
x/evm/client/cli/query.go
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/cosmos/cosmos-sdk/client"
|
||||||
|
"github.com/cosmos/cosmos-sdk/client/context"
|
||||||
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
"github.com/cosmos/ethermint/x/evm/types"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetQueryCmd(moduleName string, cdc *codec.Codec) *cobra.Command {
|
||||||
|
evmQueryCmd := &cobra.Command{
|
||||||
|
Use: types.ModuleName,
|
||||||
|
Short: "Querying commands for the evm module",
|
||||||
|
DisableFlagParsing: true,
|
||||||
|
SuggestionsMinimumDistance: 2,
|
||||||
|
RunE: client.ValidateCmd,
|
||||||
|
}
|
||||||
|
evmQueryCmd.AddCommand(client.GetCommands(
|
||||||
|
GetCmdGetBlockNumber(moduleName, cdc),
|
||||||
|
GetCmdGetStorageAt(moduleName, cdc),
|
||||||
|
GetCmdGetCode(moduleName, cdc),
|
||||||
|
)...)
|
||||||
|
return evmQueryCmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCmdGetBlockNumber queries information about the current block number
|
||||||
|
func GetCmdGetBlockNumber(queryRoute string, cdc *codec.Codec) *cobra.Command {
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: "block-number",
|
||||||
|
Short: "Gets block number (block height)",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
cliCtx := context.NewCLIContext().WithCodec(cdc)
|
||||||
|
|
||||||
|
res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/blockNumber", queryRoute), nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("could not resolve: %s\n", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var out types.QueryResBlockNumber
|
||||||
|
cdc.MustUnmarshalJSON(res, &out)
|
||||||
|
return cliCtx.PrintOutput(out)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCmdGetStorageAt queries a key in an accounts storage
|
||||||
|
func GetCmdGetStorageAt(queryRoute string, cdc *codec.Codec) *cobra.Command {
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: "storage [account] [key]",
|
||||||
|
Short: "Gets storage for an account at a given key",
|
||||||
|
Args: cobra.ExactArgs(2),
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
cliCtx := context.NewCLIContext().WithCodec(cdc)
|
||||||
|
|
||||||
|
// TODO: Validate args
|
||||||
|
account := args[0]
|
||||||
|
key := args[1]
|
||||||
|
|
||||||
|
res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/storage/%s/%s", queryRoute, account, key), nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("could not resolve: %s\n", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var out types.QueryResStorage
|
||||||
|
cdc.MustUnmarshalJSON(res, &out)
|
||||||
|
return cliCtx.PrintOutput(out)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCmdGetCode queries the code field of a given address
|
||||||
|
func GetCmdGetCode(queryRoute string, cdc *codec.Codec) *cobra.Command {
|
||||||
|
return &cobra.Command{
|
||||||
|
Use: "code [account]",
|
||||||
|
Short: "Gets code from an account",
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
cliCtx := context.NewCLIContext().WithCodec(cdc)
|
||||||
|
|
||||||
|
// TODO: Validate args
|
||||||
|
account := args[0]
|
||||||
|
|
||||||
|
res, _, err := cliCtx.QueryWithData(fmt.Sprintf("custom/%s/code/%s", queryRoute, account), nil)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("could not resolve: %s\n", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var out types.QueryResCode
|
||||||
|
cdc.MustUnmarshalJSON(res, &out)
|
||||||
|
return cliCtx.PrintOutput(out)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,9 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/ethermint/types"
|
"github.com/cosmos/ethermint/types"
|
||||||
|
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||||
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
|
"math/big"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
@ -15,8 +18,8 @@ type (
|
|||||||
|
|
||||||
// GenesisAccount defines an account to be initialized in the genesis state.
|
// GenesisAccount defines an account to be initialized in the genesis state.
|
||||||
GenesisAccount struct {
|
GenesisAccount struct {
|
||||||
Address sdk.AccAddress `json:"address"`
|
Address ethcmn.Address `json:"address"`
|
||||||
Coins sdk.Coins `json:"coins"`
|
Balance *big.Int `json:"balance"`
|
||||||
Code []byte `json:"code,omitempty"`
|
Code []byte `json:"code,omitempty"`
|
||||||
Storage types.Storage `json:"storage,omitempty"`
|
Storage types.Storage `json:"storage,omitempty"`
|
||||||
}
|
}
|
||||||
@ -24,11 +27,11 @@ type (
|
|||||||
|
|
||||||
func ValidateGenesis(data GenesisState) error {
|
func ValidateGenesis(data GenesisState) error {
|
||||||
for _, acct := range data.Accounts {
|
for _, acct := range data.Accounts {
|
||||||
if acct.Address == nil {
|
if len(acct.Address.Bytes()) == 0 {
|
||||||
return fmt.Errorf("Invalid GenesisAccount Error: Missing Address")
|
return fmt.Errorf("Invalid GenesisAccount Error: Missing Address")
|
||||||
}
|
}
|
||||||
if acct.Coins == nil {
|
if acct.Balance == nil {
|
||||||
return fmt.Errorf("Invalid GenesisAccount Error: Missing Coins")
|
return fmt.Errorf("Invalid GenesisAccount Error: Missing Balance")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -40,14 +43,15 @@ func DefaultGenesisState() GenesisState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Implement these once keeper is established
|
func InitGenesis(ctx sdk.Context, keeper Keeper, data GenesisState) []abci.ValidatorUpdate {
|
||||||
//func InitGenesis(ctx sdk.Context, keeper Keeper, data GenesisState) []abci.ValidatorUpdate {
|
for _, record := range data.Accounts {
|
||||||
// for _, record := range data.Accounts {
|
keeper.SetCode(ctx, record.Address, record.Code)
|
||||||
// // TODO: Add to keeper
|
keeper.CreateGenesisAccount(ctx, record)
|
||||||
// }
|
}
|
||||||
// return []abci.ValidatorUpdate{}
|
return []abci.ValidatorUpdate{}
|
||||||
//}
|
}
|
||||||
//
|
|
||||||
//func ExportGenesis(ctx sdk.Context, k Keeper) GenesisState {
|
// TODO: Implement
|
||||||
// return GenesisState{Accounts: nil}
|
func ExportGenesis(ctx sdk.Context, k Keeper) GenesisState {
|
||||||
//}
|
return GenesisState{Accounts: nil}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package evm
|
package evm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
ethcmn "github.com/ethereum/go-ethereum/common"
|
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||||
ethvm "github.com/ethereum/go-ethereum/core/vm"
|
ethvm "github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
|
||||||
@ -17,14 +18,31 @@ import (
|
|||||||
// to the StateDB interface
|
// to the StateDB interface
|
||||||
type Keeper struct {
|
type Keeper struct {
|
||||||
csdb *types.CommitStateDB
|
csdb *types.CommitStateDB
|
||||||
|
cdc *codec.Codec
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewKeeper(ak auth.AccountKeeper, storageKey, codeKey sdk.StoreKey) Keeper {
|
func NewKeeper(ak auth.AccountKeeper, storageKey, codeKey sdk.StoreKey, cdc *codec.Codec) Keeper {
|
||||||
return Keeper{
|
return Keeper{
|
||||||
csdb: types.NewCommitStateDB(sdk.Context{}, ak, storageKey, codeKey),
|
csdb: types.NewCommitStateDB(sdk.Context{}, ak, storageKey, codeKey),
|
||||||
|
cdc: cdc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Genesis
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// CreateGenesisAccount initializes an account and its balance, code, and storage
|
||||||
|
func (k *Keeper) CreateGenesisAccount(ctx sdk.Context, account GenesisAccount) {
|
||||||
|
csdb := k.csdb.WithContext(ctx)
|
||||||
|
csdb.SetBalance(account.Address, account.Balance)
|
||||||
|
csdb.SetCode(account.Address, account.Code)
|
||||||
|
for _, key := range account.Storage {
|
||||||
|
csdb.SetState(account.Address, key, account.Storage[key])
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Setters
|
// Setters
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
@ -4,11 +4,18 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/cosmos/cosmos-sdk/client/context"
|
"github.com/cosmos/cosmos-sdk/client/context"
|
||||||
"github.com/cosmos/cosmos-sdk/codec"
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/types/module"
|
||||||
|
"github.com/cosmos/ethermint/x/evm/client/cli"
|
||||||
"github.com/cosmos/ethermint/x/evm/types"
|
"github.com/cosmos/ethermint/x/evm/types"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var _ module.AppModuleBasic = AppModuleBasic{}
|
||||||
|
var _ module.AppModule = AppModule{}
|
||||||
|
|
||||||
// app module Basics object
|
// app module Basics object
|
||||||
type AppModuleBasic struct{}
|
type AppModuleBasic struct{}
|
||||||
|
|
||||||
@ -42,10 +49,61 @@ func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router
|
|||||||
|
|
||||||
// Get the root query command of this module
|
// Get the root query command of this module
|
||||||
func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command {
|
func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command {
|
||||||
return nil // cli.GetQueryCmd(StoreKey, cdc)
|
return cli.GetQueryCmd(types.ModuleName, cdc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the root tx command of this module
|
// Get the root tx command of this module
|
||||||
func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command {
|
func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command {
|
||||||
return nil // cli.GetTxCmd(StoreKey, cdc)
|
return nil // cli.GetTxCmd(StoreKey, cdc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AppModule struct {
|
||||||
|
AppModuleBasic
|
||||||
|
keeper Keeper
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAppModule creates a new AppModule Object
|
||||||
|
func NewAppModule(keeper Keeper) AppModule {
|
||||||
|
return AppModule{
|
||||||
|
AppModuleBasic: AppModuleBasic{},
|
||||||
|
keeper: keeper,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (AppModule) Name() string {
|
||||||
|
return types.ModuleName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {}
|
||||||
|
|
||||||
|
func (am AppModule) Route() string {
|
||||||
|
return types.RouterKey
|
||||||
|
}
|
||||||
|
|
||||||
|
func (am AppModule) NewHandler() sdk.Handler {
|
||||||
|
return nil // NewHandler(am.keeper)
|
||||||
|
}
|
||||||
|
func (am AppModule) QuerierRoute() string {
|
||||||
|
return types.ModuleName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (am AppModule) NewQuerierHandler() sdk.Querier {
|
||||||
|
return NewQuerier(am.keeper)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {}
|
||||||
|
|
||||||
|
func (am AppModule) EndBlock(sdk.Context, abci.RequestEndBlock) []abci.ValidatorUpdate {
|
||||||
|
return []abci.ValidatorUpdate{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate {
|
||||||
|
var genesisState GenesisState
|
||||||
|
types.ModuleCdc.MustUnmarshalJSON(data, &genesisState)
|
||||||
|
return InitGenesis(ctx, am.keeper, genesisState)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage {
|
||||||
|
gs := ExportGenesis(ctx, am.keeper)
|
||||||
|
return types.ModuleCdc.MustMarshalJSON(gs)
|
||||||
|
}
|
||||||
|
105
x/evm/querier.go
Normal file
105
x/evm/querier.go
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
package evm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/cosmos/cosmos-sdk/codec"
|
||||||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
"github.com/cosmos/ethermint/version"
|
||||||
|
"github.com/cosmos/ethermint/x/evm/types"
|
||||||
|
ethcmn "github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
abci "github.com/tendermint/tendermint/abci/types"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Supported endpoints
|
||||||
|
const (
|
||||||
|
QueryProtocolVersion = "protocolVersion"
|
||||||
|
QueryBalance = "balance"
|
||||||
|
QueryBlockNumber = "blockNumber"
|
||||||
|
QueryStorage = "storage"
|
||||||
|
QueryCode = "code"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewQuerier is the module level router for state queries
|
||||||
|
func NewQuerier(keeper Keeper) sdk.Querier {
|
||||||
|
return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err sdk.Error) {
|
||||||
|
switch path[0] {
|
||||||
|
case QueryProtocolVersion:
|
||||||
|
return queryProtocolVersion(keeper)
|
||||||
|
case QueryBalance:
|
||||||
|
return queryBalance(ctx, path, keeper)
|
||||||
|
case QueryBlockNumber:
|
||||||
|
return queryBlockNumber(ctx, keeper)
|
||||||
|
case QueryStorage:
|
||||||
|
return queryStorage(ctx, path, keeper)
|
||||||
|
case QueryCode:
|
||||||
|
return queryCode(ctx, path, keeper)
|
||||||
|
default:
|
||||||
|
return nil, sdk.ErrUnknownRequest("unknown query endpoint")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func queryProtocolVersion(keeper Keeper) ([]byte, sdk.Error) {
|
||||||
|
vers := version.ProtocolVersion
|
||||||
|
|
||||||
|
res, err := codec.MarshalJSONIndent(keeper.cdc, vers)
|
||||||
|
if err != nil {
|
||||||
|
panic("could not marshal result to JSON")
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func queryBalance(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) {
|
||||||
|
addr := ethcmn.BytesToAddress([]byte(path[1]))
|
||||||
|
balance := keeper.GetBalance(ctx, addr)
|
||||||
|
hBalance := &hexutil.Big{}
|
||||||
|
err := hBalance.UnmarshalText(balance.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
panic("could not marshal big.Int to hexutil.Big")
|
||||||
|
}
|
||||||
|
|
||||||
|
bRes := types.QueryResBalance{Balance: hBalance}
|
||||||
|
res, err := codec.MarshalJSONIndent(keeper.cdc, bRes)
|
||||||
|
if err != nil {
|
||||||
|
panic("could not marshal result to JSON")
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func queryBlockNumber(ctx sdk.Context, keeper Keeper) ([]byte, sdk.Error) {
|
||||||
|
num := ctx.BlockHeight()
|
||||||
|
bnRes := types.QueryResBlockNumber{Number: big.NewInt(num)}
|
||||||
|
res, err := codec.MarshalJSONIndent(keeper.cdc, bnRes)
|
||||||
|
if err != nil {
|
||||||
|
panic("could not marshal result to JSON")
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func queryStorage(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) {
|
||||||
|
addr := ethcmn.BytesToAddress([]byte(path[1]))
|
||||||
|
key := ethcmn.BytesToHash([]byte(path[2]))
|
||||||
|
val := keeper.GetState(ctx, addr, key)
|
||||||
|
bRes := types.QueryResStorage{Value: val.Bytes()}
|
||||||
|
res, err := codec.MarshalJSONIndent(keeper.cdc, bRes)
|
||||||
|
if err != nil {
|
||||||
|
panic("could not marshal result to JSON")
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func queryCode(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) {
|
||||||
|
addr := ethcmn.BytesToAddress([]byte(path[1]))
|
||||||
|
code := keeper.GetCode(ctx, addr)
|
||||||
|
cRes := types.QueryResCode{Code: code}
|
||||||
|
res, err := codec.MarshalJSONIndent(keeper.cdc, cRes)
|
||||||
|
if err != nil {
|
||||||
|
panic("could not marshal result to JSON")
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
@ -6,4 +6,6 @@ const (
|
|||||||
|
|
||||||
EvmStoreKey = "evmstore"
|
EvmStoreKey = "evmstore"
|
||||||
EvmCodeKey = "evmcode"
|
EvmCodeKey = "evmcode"
|
||||||
|
|
||||||
|
RouterKey = ModuleName
|
||||||
)
|
)
|
||||||
|
46
x/evm/types/querier.go
Normal file
46
x/evm/types/querier.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
type QueryResProtocolVersion struct {
|
||||||
|
Version string `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q QueryResProtocolVersion) String() string {
|
||||||
|
return q.Version
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryResBalance struct {
|
||||||
|
Balance *hexutil.Big `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q QueryResBalance) String() string {
|
||||||
|
return q.Balance.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryResBlockNumber struct {
|
||||||
|
Number *big.Int `json:"result"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q QueryResBlockNumber) String() string {
|
||||||
|
return q.Number.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryResStorage struct {
|
||||||
|
Value []byte `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q QueryResStorage) String() string {
|
||||||
|
return string(q.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryResCode struct {
|
||||||
|
Code []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q QueryResCode) String() string {
|
||||||
|
return string(q.Code)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user