diff --git a/rpc/core/1 b/rpc/core/1 new file mode 100644 index 0000000000000000000000000000000000000000..58483e40f7c5d8f22bcf0544344cbb2663b2586e --- /dev/null +++ b/rpc/core/1 @@ -0,0 +1,55 @@ +package core + +import ( + stypes "github.com/eris-ltd/eris-db/state/types" + "github.com/eris-ltd/eris-db/tmsp" + "github.com/eris-ltd/eris-db/txs" + + bc "github.com/tendermint/tendermint/blockchain" + "github.com/tendermint/tendermint/consensus" + mempl "github.com/tendermint/tendermint/mempool" + tmtypes "github.com/tendermint/tendermint/types" + + "github.com/tendermint/go-p2p" +) + +var blockStore *bc.BlockStore +var consensusState *consensus.ConsensusState +var consensusReactor *consensus.ConsensusReactor +var mempoolReactor *mempl.MempoolReactor +var p2pSwitch *p2p.Switch +var privValidator *types.PrivValidator +var genDoc *stypes.GenesisDoc // cache the genesis structure +var erisdbApp *tmsp.ErisDBApp + +func SetErisDBApp(edbApp *tmsp.ErisDBApp) { + erisdbApp = edbApp +} + +func SetBlockStore(bs *bc.BlockStore) { + blockStore = bs +} + +func SetConsensusState(cs *consensus.ConsensusState) { + consensusState = cs +} + +func SetConsensusReactor(cr *consensus.ConsensusReactor) { + consensusReactor = cr +} + +func SetMempoolReactor(mr *mempl.MempoolReactor) { + mempoolReactor = mr +} + +func SetSwitch(sw *p2p.Switch) { + p2pSwitch = sw +} + +func SetPrivValidator(pv *tmtypes.PrivValidator) { + privValidator = pv +} + +func SetGenDoc(doc *stypes.GenesisDoc) { + genDoc = doc +} diff --git a/rpc/core/accounts.go b/rpc/core/accounts.go new file mode 100644 index 0000000000000000000000000000000000000000..161a14af67a8e19f392ed1e191f4e4d113cdebe3 --- /dev/null +++ b/rpc/core/accounts.go @@ -0,0 +1,68 @@ +package core + +import ( + "fmt" + acm "github.com/eris-ltd/eris-db/account" + ctypes "github.com/eris-ltd/eris-db/rpc/core/types" + . "github.com/tendermint/go-common" +) + +func GenPrivAccount() (*ctypes.ResultGenPrivAccount, error) { + return &ctypes.ResultGenPrivAccount{acm.GenPrivAccount()}, nil +} + +// If the account is not known, returns nil, nil. +func GetAccount(address []byte) (*ctypes.ResultGetAccount, error) { + cache := erisdbApp.GetCheckCache() + // cache := mempoolReactor.Mempool.GetCache() + account := cache.GetAccount(address) + if account == nil { + return nil, nil + } + return &ctypes.ResultGetAccount{account}, nil +} + +func GetStorage(address, key []byte) (*ctypes.ResultGetStorage, error) { + state := erisdbApp.GetState() + // state := consensusState.GetState() + account := state.GetAccount(address) + if account == nil { + return nil, fmt.Errorf("UnknownAddress: %X", address) + } + storageRoot := account.StorageRoot + storageTree := state.LoadStorage(storageRoot) + + _, value, exists := storageTree.Get(LeftPadWord256(key).Bytes()) + if !exists { // value == nil { + return &ctypes.ResultGetStorage{key, nil}, nil + } + return &ctypes.ResultGetStorage{key, value}, nil +} + +func ListAccounts() (*ctypes.ResultListAccounts, error) { + var blockHeight int + var accounts []*acm.Account + state := erisdbApp.GetState() + blockHeight = state.LastBlockHeight + state.GetAccounts().Iterate(func(key []byte, value []byte) bool { + accounts = append(accounts, acm.DecodeAccount(value)) + return false + }) + return &ctypes.ResultListAccounts{blockHeight, accounts}, nil +} + +func DumpStorage(address []byte) (*ctypes.ResultDumpStorage, error) { + state := erisdbApp.GetState() + account := state.GetAccount(address) + if account == nil { + return nil, fmt.Errorf("UnknownAddress: %X", address) + } + storageRoot := account.StorageRoot + storageTree := state.LoadStorage(storageRoot) + storageItems := []ctypes.StorageItem{} + storageTree.Iterate(func(key []byte, value []byte) bool { + storageItems = append(storageItems, ctypes.StorageItem{key, value}) + return false + }) + return &ctypes.ResultDumpStorage{storageRoot, storageItems}, nil +} diff --git a/rpc/core/config.go b/rpc/core/config.go new file mode 100644 index 0000000000000000000000000000000000000000..7f51b66625ecc9483046f5edcaad851edf89f86b --- /dev/null +++ b/rpc/core/config.go @@ -0,0 +1,13 @@ +package core + +import ( + cfg "github.com/tendermint/go-config" +) + +var config cfg.Config = nil + +func init() { + cfg.OnConfig(func(newConfig cfg.Config) { + config = newConfig + }) +} diff --git a/rpc/core/log.go b/rpc/core/log.go new file mode 100644 index 0000000000000000000000000000000000000000..d359bee26498b53017a9aec516a65f6e2472d726 --- /dev/null +++ b/rpc/core/log.go @@ -0,0 +1,7 @@ +package core + +import ( + "github.com/tendermint/log15" +) + +var log = log15.New("module", "rpc") diff --git a/rpc/core/names.go b/rpc/core/names.go new file mode 100644 index 0000000000000000000000000000000000000000..4dcd891e89d626d0407dfbc114a12ddbd463494e --- /dev/null +++ b/rpc/core/names.go @@ -0,0 +1,30 @@ +package core + +import ( + "fmt" + + ctypes "github.com/eris-ltd/eris-db/rpc/core/types" + sm "github.com/eris-ltd/eris-db/state" + "github.com/eris-ltd/eris-db/txs" +) + +func GetName(name string) (*ctypes.ResultGetName, error) { + st := erisdbApp.GetState() + entry := st.GetNameRegEntry(name) + if entry == nil { + return nil, fmt.Errorf("Name %s not found", name) + } + return &ctypes.ResultGetName{entry}, nil +} + +func ListNames() (*ctypes.ResultListNames, error) { + var blockHeight int + var names []*types.NameRegEntry + state := erisdbApp.GetState() + blockHeight = state.LastBlockHeight + state.GetNames().Iterate(func(key []byte, value []byte) bool { + names = append(names, sm.DecodeNameRegEntry(value)) + return false + }) + return &ctypes.ResultListNames{blockHeight, names}, nil +} diff --git a/rpc/core/net.go b/rpc/core/net.go new file mode 100644 index 0000000000000000000000000000000000000000..2198731c5c4b6d3d5d7cf5d61aa3cbf7f6229e8f --- /dev/null +++ b/rpc/core/net.go @@ -0,0 +1,69 @@ +package core + +import ( + ctypes "github.com/eris-ltd/eris-db/rpc/core/types" + sm "github.com/eris-ltd/eris-db/state" + "github.com/tendermint/tendermint/types" + + dbm "github.com/tendermint/go-db" +) + +//----------------------------------------------------------------------------- + +// cache the genesis state +var genesisState *sm.State + +func Status() (*ctypes.ResultStatus, error) { + db := dbm.NewMemDB() + if genesisState == nil { + genesisState = sm.MakeGenesisState(db, genDoc) + } + genesisHash := genesisState.Hash() + latestHeight := blockStore.Height() + var ( + latestBlockMeta *types.BlockMeta + latestBlockHash []byte + latestBlockTime int64 + ) + if latestHeight != 0 { + latestBlockMeta = blockStore.LoadBlockMeta(latestHeight) + latestBlockHash = latestBlockMeta.Hash + latestBlockTime = latestBlockMeta.Header.Time.UnixNano() + } + + return &ctypes.ResultStatus{ + NodeInfo: p2pSwitch.NodeInfo(), + GenesisHash: genesisHash, + PubKey: privValidator.PubKey, + LatestBlockHash: latestBlockHash, + LatestBlockHeight: latestHeight, + LatestBlockTime: latestBlockTime}, nil +} + +//----------------------------------------------------------------------------- + +func NetInfo() (*ctypes.ResultNetInfo, error) { + listening := p2pSwitch.IsListening() + listeners := []string{} + for _, listener := range p2pSwitch.Listeners() { + listeners = append(listeners, listener.String()) + } + peers := []ctypes.Peer{} + for _, peer := range p2pSwitch.Peers().List() { + peers = append(peers, ctypes.Peer{ + NodeInfo: *peer.NodeInfo, + IsOutbound: peer.IsOutbound(), + }) + } + return &ctypes.ResultNetInfo{ + Listening: listening, + Listeners: listeners, + Peers: peers, + }, nil +} + +//----------------------------------------------------------------------------- + +func Genesis() (*ctypes.ResultGenesis, error) { + return &ctypes.ResultGenesis{genDoc}, nil +} diff --git a/rpc/core/pipe.go b/rpc/core/pipe.go new file mode 100644 index 0000000000000000000000000000000000000000..c7f3fa4abf6d590e6186b029a267b520f456a6e1 --- /dev/null +++ b/rpc/core/pipe.go @@ -0,0 +1,54 @@ +package core + +import ( + stypes "github.com/eris-ltd/eris-db/state/types" + "github.com/eris-ltd/eris-db/tmsp" + + bc "github.com/tendermint/tendermint/blockchain" + "github.com/tendermint/tendermint/consensus" + mempl "github.com/tendermint/tendermint/mempool" + tmtypes "github.com/tendermint/tendermint/types" + + "github.com/tendermint/go-p2p" +) + +var blockStore *bc.BlockStore +var consensusState *consensus.ConsensusState +var consensusReactor *consensus.ConsensusReactor +var mempoolReactor *mempl.MempoolReactor +var p2pSwitch *p2p.Switch +var privValidator *tmtypes.PrivValidator +var genDoc *stypes.GenesisDoc // cache the genesis structure +var erisdbApp *tmsp.ErisDBApp + +func SetErisDBApp(edbApp *tmsp.ErisDBApp) { + erisdbApp = edbApp +} + +func SetBlockStore(bs *bc.BlockStore) { + blockStore = bs +} + +func SetConsensusState(cs *consensus.ConsensusState) { + consensusState = cs +} + +func SetConsensusReactor(cr *consensus.ConsensusReactor) { + consensusReactor = cr +} + +func SetMempoolReactor(mr *mempl.MempoolReactor) { + mempoolReactor = mr +} + +func SetSwitch(sw *p2p.Switch) { + p2pSwitch = sw +} + +func SetPrivValidator(pv *tmtypes.PrivValidator) { + privValidator = pv +} + +func SetGenDoc(doc *stypes.GenesisDoc) { + genDoc = doc +} diff --git a/rpc/core/routes.go b/rpc/core/routes.go new file mode 100644 index 0000000000000000000000000000000000000000..9d8bf8f556d78a7deb73b28c04812380b5454861 --- /dev/null +++ b/rpc/core/routes.go @@ -0,0 +1,29 @@ +package core + +import ( + rpc "github.com/tendermint/go-rpc/server" +) + +// TODO: eliminate redundancy between here and reading code from core/ +var Routes = map[string]*rpc.RPCFunc{ + "status": rpc.NewRPCFunc(Status, ""), + "net_info": rpc.NewRPCFunc(NetInfo, ""), + // "blockchain": rpc.NewRPCFunc(BlockchainInfo, "minHeight,maxHeight"), + "genesis": rpc.NewRPCFunc(Genesis, ""), + // "get_block": rpc.NewRPCFunc(GetBlock, "height"), + "get_account": rpc.NewRPCFunc(GetAccount, "address"), + "get_storage": rpc.NewRPCFunc(GetStorage, "address,key"), + "call": rpc.NewRPCFunc(Call, "fromAddress,toAddress,data"), + "call_code": rpc.NewRPCFunc(CallCode, "fromAddress,code,data"), + //"list_validators": rpc.NewRPCFunc(ListValidators, ""), + // "dump_consensus_state": rpc.NewRPCFunc(DumpConsensusState, ""), + "dump_storage": rpc.NewRPCFunc(DumpStorage, "address"), + // "broadcast_tx": rpc.NewRPCFunc(BroadcastTx, "tx"), + // "list_unconfirmed_txs": rpc.NewRPCFunc(ListUnconfirmedTxs, ""), + "list_accounts": rpc.NewRPCFunc(ListAccounts, ""), + "get_name": rpc.NewRPCFunc(GetName, "name"), + "list_names": rpc.NewRPCFunc(ListNames, ""), + "unsafe/gen_priv_account": rpc.NewRPCFunc(GenPrivAccount, ""), + "unsafe/sign_tx": rpc.NewRPCFunc(SignTx, "tx,privAccounts"), + // subscribe/unsubscribe are reserved for websocket events. +} diff --git a/rpc/core/txs.go b/rpc/core/txs.go new file mode 100644 index 0000000000000000000000000000000000000000..7a93dec44768ea93cb80f59ca3234831baa268eb --- /dev/null +++ b/rpc/core/txs.go @@ -0,0 +1,118 @@ +package core + +import ( + "fmt" + acm "github.com/eris-ltd/eris-db/account" + "github.com/eris-ltd/eris-db/evm" + ctypes "github.com/eris-ltd/eris-db/rpc/core/types" + "github.com/eris-ltd/eris-db/state" + "github.com/eris-ltd/eris-db/txs" + + . "github.com/tendermint/go-common" + "github.com/tendermint/go-crypto" +) + +func toVMAccount(acc *acm.Account) *vm.Account { + return &vm.Account{ + Address: LeftPadWord256(acc.Address), + Balance: acc.Balance, + Code: acc.Code, // This is crazy. + Nonce: int64(acc.Sequence), + Other: acc.PubKey, + } +} + +//----------------------------------------------------------------------------- + +// Run a contract's code on an isolated and unpersisted state +// Cannot be used to create new contracts +func Call(fromAddress, toAddress, data []byte) (*ctypes.ResultCall, error) { + st := erisdbApp.GetState() + cache := state.NewBlockCache(st) + outAcc := cache.GetAccount(toAddress) + if outAcc == nil { + return nil, fmt.Errorf("Account %x does not exist", toAddress) + } + callee := toVMAccount(outAcc) + caller := &vm.Account{Address: LeftPadWord256(fromAddress)} + txCache := state.NewTxCache(cache) + params := vm.Params{ + BlockHeight: int64(st.LastBlockHeight), + BlockHash: LeftPadWord256(st.LastBlockHash), + BlockTime: st.LastBlockTime.Unix(), + GasLimit: st.GetGasLimit(), + } + + vmach := vm.NewVM(txCache, params, caller.Address, nil) + gas := st.GetGasLimit() + ret, err := vmach.Call(caller, callee, callee.Code, data, 0, &gas) + if err != nil { + return nil, err + } + return &ctypes.ResultCall{Return: ret}, nil +} + +// Run the given code on an isolated and unpersisted state +// Cannot be used to create new contracts +func CallCode(fromAddress, code, data []byte) (*ctypes.ResultCall, error) { + + st := erisdbApp.GetState() + cache := erisdbApp.GetCheckCache() + callee := &vm.Account{Address: LeftPadWord256(fromAddress)} + caller := &vm.Account{Address: LeftPadWord256(fromAddress)} + txCache := state.NewTxCache(cache) + params := vm.Params{ + BlockHeight: int64(st.LastBlockHeight), + BlockHash: LeftPadWord256(st.LastBlockHash), + BlockTime: st.LastBlockTime.Unix(), + GasLimit: st.GetGasLimit(), + } + + vmach := vm.NewVM(txCache, params, caller.Address, nil) + gas := st.GetGasLimit() + ret, err := vmach.Call(caller, callee, code, data, 0, &gas) + if err != nil { + return nil, err + } + return &ctypes.ResultCall{Return: ret}, nil +} + +//----------------------------------------------------------------------------- + +func SignTx(tx types.Tx, privAccounts []*acm.PrivAccount) (*ctypes.ResultSignTx, error) { + // more checks? + + for i, privAccount := range privAccounts { + if privAccount == nil || privAccount.PrivKey == nil { + return nil, fmt.Errorf("Invalid (empty) privAccount @%v", i) + } + } + switch tx.(type) { + case *types.SendTx: + sendTx := tx.(*types.SendTx) + for i, input := range sendTx.Inputs { + input.PubKey = privAccounts[i].PubKey + input.Signature = privAccounts[i].Sign(config.GetString("erisdb.chain_id"), sendTx) + } + case *types.CallTx: + callTx := tx.(*types.CallTx) + callTx.Input.PubKey = privAccounts[0].PubKey + callTx.Input.Signature = privAccounts[0].Sign(config.GetString("erisdb.chain_id"), callTx) + case *types.BondTx: + bondTx := tx.(*types.BondTx) + // the first privaccount corresponds to the BondTx pub key. + // the rest to the inputs + bondTx.Signature = privAccounts[0].Sign(config.GetString("erisdb.chain_id"), bondTx).(crypto.SignatureEd25519) + for i, input := range bondTx.Inputs { + input.PubKey = privAccounts[i+1].PubKey + input.Signature = privAccounts[i+1].Sign(config.GetString("erisdb.chain_id"), bondTx) + } + case *types.UnbondTx: + unbondTx := tx.(*types.UnbondTx) + unbondTx.Signature = privAccounts[0].Sign(config.GetString("erisdb.chain_id"), unbondTx).(crypto.SignatureEd25519) + case *types.RebondTx: + rebondTx := tx.(*types.RebondTx) + rebondTx.Signature = privAccounts[0].Sign(config.GetString("erisdb.chain_id"), rebondTx).(crypto.SignatureEd25519) + } + return &ctypes.ResultSignTx{tx}, nil +} diff --git a/rpc/core/types/responses.go b/rpc/core/types/responses.go new file mode 100644 index 0000000000000000000000000000000000000000..c1718dbee8d0012463b7fa8b0637929dab816dfe --- /dev/null +++ b/rpc/core/types/responses.go @@ -0,0 +1,182 @@ +package core_types + +import ( + acm "github.com/eris-ltd/eris-db/account" + stypes "github.com/eris-ltd/eris-db/state/types" + txtypes "github.com/eris-ltd/eris-db/txs" + "github.com/tendermint/tendermint/types" + + "github.com/tendermint/go-crypto" + "github.com/tendermint/go-p2p" + "github.com/tendermint/go-wire" +) + +type ResultGetStorage struct { + Key []byte `json:"key"` + Value []byte `json:"value"` +} + +type ResultCall struct { + Return []byte `json:"return"` + GasUsed int64 `json:"gas_used"` + // TODO ... +} + +type ResultListAccounts struct { + BlockHeight int `json:"block_height"` + Accounts []*acm.Account `json:"accounts"` +} + +type ResultDumpStorage struct { + StorageRoot []byte `json:"storage_root"` + StorageItems []StorageItem `json:"storage_items"` +} + +type StorageItem struct { + Key []byte `json:"key"` + Value []byte `json:"value"` +} + +type ResultBlockchainInfo struct { + LastHeight int `json:"last_height"` + BlockMetas []*types.BlockMeta `json:"block_metas"` +} + +type ResultGetBlock struct { + BlockMeta *types.BlockMeta `json:"block_meta"` + Block *types.Block `json:"block"` +} + +type ResultStatus struct { + NodeInfo *p2p.NodeInfo `json:"node_info"` + GenesisHash []byte `json:"genesis_hash"` + PubKey crypto.PubKey `json:"pub_key"` + LatestBlockHash []byte `json:"latest_block_hash"` + LatestBlockHeight int `json:"latest_block_height"` + LatestBlockTime int64 `json:"latest_block_time"` // nano +} + +type ResultNetInfo struct { + Listening bool `json:"listening"` + Listeners []string `json:"listeners"` + Peers []Peer `json:"peers"` +} + +type Peer struct { + p2p.NodeInfo `json:"node_info"` + IsOutbound bool `json:"is_outbound"` +} + +type ResultListValidators struct { + BlockHeight int `json:"block_height"` + BondedValidators []*types.Validator `json:"bonded_validators"` + UnbondingValidators []*types.Validator `json:"unbonding_validators"` +} + +type ResultDumpConsensusState struct { + RoundState string `json:"round_state"` + PeerRoundStates []string `json:"peer_round_states"` +} + +type ResultListNames struct { + BlockHeight int `json:"block_height"` + Names []*txtypes.NameRegEntry `json:"names"` +} + +type ResultGenPrivAccount struct { + PrivAccount *acm.PrivAccount `json:"priv_account"` +} + +type ResultGetAccount struct { + Account *acm.Account `json:"account"` +} + +type ResultBroadcastTx struct { + Receipt Receipt `json:"receipt"` +} + +type Receipt struct { + TxHash []byte `json:"tx_hash"` + CreatesContract uint8 `json:"creates_contract"` + ContractAddr []byte `json:"contract_addr"` +} + +type ResultListUnconfirmedTxs struct { + N int `json:"n_txs"` + Txs []txtypes.Tx `json:"txs"` +} + +type ResultGetName struct { + Entry *txtypes.NameRegEntry `json:"entry"` +} + +type ResultGenesis struct { + Genesis *stypes.GenesisDoc `json:"genesis"` +} + +type ResultSignTx struct { + Tx txtypes.Tx `json:"tx"` +} + +type ResultEvent struct { + Event string `json:"event"` + Data types.TMEventData `json:"data"` +} + +//---------------------------------------- +// response & result types + +type Response struct { + JSONRPC string `json:"jsonrpc"` + ID string `json:"id"` + Result Result `json:"result"` + Error string `json:"error"` +} + +const ( + ResultTypeGetStorage = byte(0x01) + ResultTypeCall = byte(0x02) + ResultTypeListAccounts = byte(0x03) + ResultTypeDumpStorage = byte(0x04) + ResultTypeBlockchainInfo = byte(0x05) + ResultTypeGetBlock = byte(0x06) + ResultTypeStatus = byte(0x07) + ResultTypeNetInfo = byte(0x08) + ResultTypeListValidators = byte(0x09) + ResultTypeDumpConsensusState = byte(0x0A) + ResultTypeListNames = byte(0x0B) + ResultTypeGenPrivAccount = byte(0x0C) + ResultTypeGetAccount = byte(0x0D) + ResultTypeBroadcastTx = byte(0x0E) + ResultTypeListUnconfirmedTxs = byte(0x0F) + ResultTypeGetName = byte(0x10) + ResultTypeGenesis = byte(0x11) + ResultTypeSignTx = byte(0x12) + ResultTypeEvent = byte(0x13) // so websockets can respond to rpc functions +) + +type Result interface{} + +// for wire.readReflect +var _ = wire.RegisterInterface( + struct{ Result }{}, + wire.ConcreteType{&ResultGetStorage{}, ResultTypeGetStorage}, + wire.ConcreteType{&ResultCall{}, ResultTypeCall}, + wire.ConcreteType{&ResultListAccounts{}, ResultTypeListAccounts}, + wire.ConcreteType{&ResultDumpStorage{}, ResultTypeDumpStorage}, + wire.ConcreteType{&ResultBlockchainInfo{}, ResultTypeBlockchainInfo}, + wire.ConcreteType{&ResultGetBlock{}, ResultTypeGetBlock}, + wire.ConcreteType{&ResultStatus{}, ResultTypeStatus}, + wire.ConcreteType{&ResultNetInfo{}, ResultTypeNetInfo}, + wire.ConcreteType{&ResultListValidators{}, ResultTypeListValidators}, + wire.ConcreteType{&ResultDumpConsensusState{}, ResultTypeDumpConsensusState}, + wire.ConcreteType{&ResultListNames{}, ResultTypeListNames}, + wire.ConcreteType{&ResultGenPrivAccount{}, ResultTypeGenPrivAccount}, + wire.ConcreteType{&ResultGetAccount{}, ResultTypeGetAccount}, + wire.ConcreteType{&ResultBroadcastTx{}, ResultTypeBroadcastTx}, + wire.ConcreteType{&ResultListUnconfirmedTxs{}, ResultTypeListUnconfirmedTxs}, + wire.ConcreteType{&ResultGetName{}, ResultTypeGetName}, + wire.ConcreteType{&ResultGenesis{}, ResultTypeGenesis}, + wire.ConcreteType{&ResultSignTx{}, ResultTypeSignTx}, + wire.ConcreteType{&ResultEvent{}, ResultTypeEvent}, +) diff --git a/rpc/core_client/client.go b/rpc/core_client/client.go new file mode 100644 index 0000000000000000000000000000000000000000..f95eadfb6335d126ccdcbbec7dbc8f1c632d31a3 --- /dev/null +++ b/rpc/core_client/client.go @@ -0,0 +1,236 @@ +package core_client + +import ( + "bytes" + "fmt" + "io/ioutil" + "net/http" + "net/url" + + ctypes "github.com/eris-ltd/eris-db/rpc/core/types" + rpctypes "github.com/tendermint/go-rpc/types" + "github.com/tendermint/go-wire" + //"reflect" + // Uncomment to use go:generate + // _ "github.com/tendermint/go-rpc-gen" +) + +// maps camel-case function names to lower case rpc version +var reverseFuncMap = map[string]string{ + "Status": "status", + "NetInfo": "net_info", + "BlockchainInfo": "blockchain", + "Genesis": "genesis", + "GetBlock": "get_block", + "GetAccount": "get_account", + "GetStorage": "get_storage", + "Call": "call", + "CallCode": "call_code", + "ListValidators": "list_validators", + "DumpConsensusState": "dump_consensus_state", + "DumpStorage": "dump_storage", + "BroadcastTx": "broadcast_tx", + "ListUnconfirmedTxs": "list_unconfirmed_txs", + "ListAccounts": "list_accounts", + "GetName": "get_name", + "ListNames": "list_names", + "GenPrivAccount": "unsafe/gen_priv_account", + "SignTx": "unsafe/sign_tx", +} + +/* +// fill the map from camelcase to lowercase +func fillReverseFuncMap() map[string]string { + fMap := make(map[string]string) + for name, f := range core.Routes { + camelName := runtime.FuncForPC(f.f.Pointer()).Name() + spl := strings.Split(camelName, ".") + if len(spl) > 1 { + camelName = spl[len(spl)-1] + } + fMap[camelName] = name + } + return fMap +} +*/ + +type Response struct { + Status string + Data interface{} + Error string +} + +//go:generate go-rpc-gen -interface Client -dir ../core -pkg core -type *ClientHTTP,*ClientJSON -exclude pipe.go -out-pkg core_client + +type ClientJSON struct { + addr string +} + +type ClientHTTP struct { + addr string +} + +func NewClient(addr, typ string) Client { + switch typ { + case "HTTP": + return &ClientHTTP{addr} + case "JSONRPC": + return &ClientJSON{addr} + default: + panic("Unknown client type " + typ + ". Select HTTP or JSONRPC") + } + return nil +} + +func argsToJson(args ...interface{}) ([]string, error) { + l := len(args) + jsons := make([]string, l) + n, err := new(int), new(error) + for i, a := range args { + buf := new(bytes.Buffer) + wire.WriteJSON(a, buf, n, err) + if *err != nil { + return nil, *err + } + jsons[i] = string(buf.Bytes()) + } + return jsons, nil +} + +func (c *ClientJSON) RequestResponse(s rpctypes.RPCRequest) (b []byte, err error) { + b = wire.JSONBytes(s) + buf := bytes.NewBuffer(b) + resp, err := http.Post(c.addr, "text/json", buf) + if err != nil { + return nil, err + } + defer resp.Body.Close() + return ioutil.ReadAll(resp.Body) +} + +/* + What follows is used by `rpc-gen` when `go generate` is called + to populate the rpc client methods +*/ + +// first we define the base interface, which rpc-gen will further populate with generated methods + +/*rpc-gen:define-interface Client +type Client interface { + Address() string // returns the remote address +} +*/ + +// encoding functions + +func binaryWriter(args ...interface{}) ([]interface{}, error) { + list := []interface{}{} + for _, a := range args { + buf, n, err := new(bytes.Buffer), new(int), new(error) + wire.WriteJSON(a, buf, n, err) + if *err != nil { + return nil, *err + } + list = append(list, buf.Bytes()) + + } + return list, nil +} + +func argsToURLValues(argNames []string, args ...interface{}) (url.Values, error) { + values := make(url.Values) + if len(argNames) == 0 { + return values, nil + } + if len(argNames) != len(args) { + return nil, fmt.Errorf("argNames and args have different lengths: %d, %d", len(argNames), len(args)) + } + slice, err := argsToJson(args...) + if err != nil { + return nil, err + } + for i, name := range argNames { + s := slice[i] + values.Set(name, s) // s[0] + /*for j := 1; j < len(s); j++ { + values.Add(name, s[j]) + }*/ + } + return values, nil +} + +func unmarshalCheckResponse(body []byte) (response *ctypes.Response, err error) { + response = new(ctypes.Response) + wire.ReadJSON(response, body, &err) + if err != nil { + return nil, err + } + if response.Error != "" { + return nil, fmt.Errorf(response.Error) + } + return response, nil +} + +// import statements we will need for the templates + +/*rpc-gen:imports: +rpctypes github.com/tendermint/tendermint/rpc/types +net/http +io/ioutil +fmt +*/ + +// Template functions to be filled in + +/*rpc-gen:template:*ClientJSON func (c *ClientJSON) {{name}}({{args.def}}) ({{response}}) { + request := rpctypes.RPCRequest{ + JSONRPC: "2.0", + Method: reverseFuncMap["{{name}}"], + Params: []interface{}{ {{args.ident}} }, + ID: "", + } + body, err := c.RequestResponse(request) + if err != nil{ + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil{ + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.({{response.0}}) + if !ok{ + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +}*/ + +/*rpc-gen:template:*ClientHTTP func (c *ClientHTTP) {{name}}({{args.def}}) ({{response}}){ + values, err := argsToURLValues({{args.name}}, {{args.ident}}) + if err != nil{ + return nil, err + } + resp, err := http.PostForm(c.addr+reverseFuncMap["{{name}}"], values) + if err != nil { + return nil, err + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil{ + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.({{response.0}}) + if !ok{ + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +}*/ diff --git a/rpc/core_client/client_methods.go b/rpc/core_client/client_methods.go new file mode 100644 index 0000000000000000000000000000000000000000..dd1848c4b7df25373f2f2818f32dc1170e128502 --- /dev/null +++ b/rpc/core_client/client_methods.go @@ -0,0 +1,1042 @@ +// File generated by github.com/ebuchman/rpc-gen + +package core_client + +import ( + "fmt" + acm "github.com/eris-ltd/eris-db/account" + ctypes "github.com/eris-ltd/eris-db/rpc/core/types" + "github.com/eris-ltd/eris-db/txs" + rpctypes "github.com/tendermint/go-rpc/types" + "io/ioutil" + "net/http" +) + +type Client interface { + BlockchainInfo(minHeight int, maxHeight int) (*ctypes.ResultBlockchainInfo, error) + BroadcastTx(tx types.Tx) (*ctypes.ResultBroadcastTx, error) + Call(fromAddress []byte, toAddress []byte, data []byte) (*ctypes.ResultCall, error) + CallCode(fromAddress []byte, code []byte, data []byte) (*ctypes.ResultCall, error) + DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) + DumpStorage(address []byte) (*ctypes.ResultDumpStorage, error) + GenPrivAccount() (*ctypes.ResultGenPrivAccount, error) + Genesis() (*ctypes.ResultGenesis, error) + GetAccount(address []byte) (*ctypes.ResultGetAccount, error) + GetBlock(height int) (*ctypes.ResultGetBlock, error) + GetName(name string) (*ctypes.ResultGetName, error) + GetStorage(address []byte, key []byte) (*ctypes.ResultGetStorage, error) + ListAccounts() (*ctypes.ResultListAccounts, error) + ListNames() (*ctypes.ResultListNames, error) + ListUnconfirmedTxs() (*ctypes.ResultListUnconfirmedTxs, error) + ListValidators() (*ctypes.ResultListValidators, error) + NetInfo() (*ctypes.ResultNetInfo, error) + SignTx(tx types.Tx, privAccounts []*acm.PrivAccount) (*ctypes.ResultSignTx, error) + Status() (*ctypes.ResultStatus, error) +} + +func (c *ClientHTTP) BlockchainInfo(minHeight int, maxHeight int) (*ctypes.ResultBlockchainInfo, error) { + values, err := argsToURLValues([]string{"minHeight", "maxHeight"}, minHeight, maxHeight) + if err != nil { + return nil, err + } + resp, err := http.PostForm(c.addr+reverseFuncMap["BlockchainInfo"], values) + if err != nil { + return nil, err + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultBlockchainInfo) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientHTTP) BroadcastTx(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { + values, err := argsToURLValues([]string{"tx"}, tx) + if err != nil { + return nil, err + } + resp, err := http.PostForm(c.addr+reverseFuncMap["BroadcastTx"], values) + if err != nil { + return nil, err + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultBroadcastTx) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientHTTP) Call(fromAddress []byte, toAddress []byte, data []byte) (*ctypes.ResultCall, error) { + values, err := argsToURLValues([]string{"fromAddress", "toAddress", "data"}, fromAddress, toAddress, data) + if err != nil { + return nil, err + } + resp, err := http.PostForm(c.addr+reverseFuncMap["Call"], values) + if err != nil { + return nil, err + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultCall) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientHTTP) CallCode(fromAddress []byte, code []byte, data []byte) (*ctypes.ResultCall, error) { + values, err := argsToURLValues([]string{"fromAddress", "code", "data"}, fromAddress, code, data) + if err != nil { + return nil, err + } + resp, err := http.PostForm(c.addr+reverseFuncMap["CallCode"], values) + if err != nil { + return nil, err + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultCall) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientHTTP) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) { + values, err := argsToURLValues(nil) + if err != nil { + return nil, err + } + resp, err := http.PostForm(c.addr+reverseFuncMap["DumpConsensusState"], values) + if err != nil { + return nil, err + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultDumpConsensusState) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientHTTP) DumpStorage(address []byte) (*ctypes.ResultDumpStorage, error) { + values, err := argsToURLValues([]string{"address"}, address) + if err != nil { + return nil, err + } + resp, err := http.PostForm(c.addr+reverseFuncMap["DumpStorage"], values) + if err != nil { + return nil, err + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultDumpStorage) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientHTTP) GenPrivAccount() (*ctypes.ResultGenPrivAccount, error) { + values, err := argsToURLValues(nil) + if err != nil { + return nil, err + } + resp, err := http.PostForm(c.addr+reverseFuncMap["GenPrivAccount"], values) + if err != nil { + return nil, err + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultGenPrivAccount) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientHTTP) Genesis() (*ctypes.ResultGenesis, error) { + values, err := argsToURLValues(nil) + if err != nil { + return nil, err + } + resp, err := http.PostForm(c.addr+reverseFuncMap["Genesis"], values) + if err != nil { + return nil, err + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultGenesis) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientHTTP) GetAccount(address []byte) (*ctypes.ResultGetAccount, error) { + values, err := argsToURLValues([]string{"address"}, address) + if err != nil { + return nil, err + } + resp, err := http.PostForm(c.addr+reverseFuncMap["GetAccount"], values) + if err != nil { + return nil, err + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultGetAccount) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientHTTP) GetBlock(height int) (*ctypes.ResultGetBlock, error) { + values, err := argsToURLValues([]string{"height"}, height) + if err != nil { + return nil, err + } + resp, err := http.PostForm(c.addr+reverseFuncMap["GetBlock"], values) + if err != nil { + return nil, err + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultGetBlock) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientHTTP) GetName(name string) (*ctypes.ResultGetName, error) { + values, err := argsToURLValues([]string{"name"}, name) + if err != nil { + return nil, err + } + resp, err := http.PostForm(c.addr+reverseFuncMap["GetName"], values) + if err != nil { + return nil, err + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultGetName) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientHTTP) GetStorage(address []byte, key []byte) (*ctypes.ResultGetStorage, error) { + values, err := argsToURLValues([]string{"address", "key"}, address, key) + if err != nil { + return nil, err + } + resp, err := http.PostForm(c.addr+reverseFuncMap["GetStorage"], values) + if err != nil { + return nil, err + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultGetStorage) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientHTTP) ListAccounts() (*ctypes.ResultListAccounts, error) { + values, err := argsToURLValues(nil) + if err != nil { + return nil, err + } + resp, err := http.PostForm(c.addr+reverseFuncMap["ListAccounts"], values) + if err != nil { + return nil, err + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultListAccounts) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientHTTP) ListNames() (*ctypes.ResultListNames, error) { + values, err := argsToURLValues(nil) + if err != nil { + return nil, err + } + resp, err := http.PostForm(c.addr+reverseFuncMap["ListNames"], values) + if err != nil { + return nil, err + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultListNames) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientHTTP) ListUnconfirmedTxs() (*ctypes.ResultListUnconfirmedTxs, error) { + values, err := argsToURLValues(nil) + if err != nil { + return nil, err + } + resp, err := http.PostForm(c.addr+reverseFuncMap["ListUnconfirmedTxs"], values) + if err != nil { + return nil, err + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultListUnconfirmedTxs) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientHTTP) ListValidators() (*ctypes.ResultListValidators, error) { + values, err := argsToURLValues(nil) + if err != nil { + return nil, err + } + resp, err := http.PostForm(c.addr+reverseFuncMap["ListValidators"], values) + if err != nil { + return nil, err + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultListValidators) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientHTTP) NetInfo() (*ctypes.ResultNetInfo, error) { + values, err := argsToURLValues(nil) + if err != nil { + return nil, err + } + resp, err := http.PostForm(c.addr+reverseFuncMap["NetInfo"], values) + if err != nil { + return nil, err + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultNetInfo) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientHTTP) SignTx(tx types.Tx, privAccounts []*acm.PrivAccount) (*ctypes.ResultSignTx, error) { + values, err := argsToURLValues([]string{"tx", "privAccounts"}, tx, privAccounts) + if err != nil { + return nil, err + } + resp, err := http.PostForm(c.addr+reverseFuncMap["SignTx"], values) + if err != nil { + return nil, err + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultSignTx) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientHTTP) Status() (*ctypes.ResultStatus, error) { + values, err := argsToURLValues(nil) + if err != nil { + return nil, err + } + resp, err := http.PostForm(c.addr+reverseFuncMap["Status"], values) + if err != nil { + return nil, err + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultStatus) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientJSON) BlockchainInfo(minHeight int, maxHeight int) (*ctypes.ResultBlockchainInfo, error) { + request := rpctypes.RPCRequest{ + JSONRPC: "2.0", + Method: reverseFuncMap["BlockchainInfo"], + Params: []interface{}{minHeight, maxHeight}, + ID: "", + } + body, err := c.RequestResponse(request) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultBlockchainInfo) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientJSON) BroadcastTx(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { + request := rpctypes.RPCRequest{ + JSONRPC: "2.0", + Method: reverseFuncMap["BroadcastTx"], + Params: []interface{}{tx}, + ID: "", + } + body, err := c.RequestResponse(request) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultBroadcastTx) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientJSON) Call(fromAddress []byte, toAddress []byte, data []byte) (*ctypes.ResultCall, error) { + request := rpctypes.RPCRequest{ + JSONRPC: "2.0", + Method: reverseFuncMap["Call"], + Params: []interface{}{fromAddress, toAddress, data}, + ID: "", + } + body, err := c.RequestResponse(request) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultCall) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientJSON) CallCode(fromAddress []byte, code []byte, data []byte) (*ctypes.ResultCall, error) { + request := rpctypes.RPCRequest{ + JSONRPC: "2.0", + Method: reverseFuncMap["CallCode"], + Params: []interface{}{fromAddress, code, data}, + ID: "", + } + body, err := c.RequestResponse(request) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultCall) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientJSON) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) { + request := rpctypes.RPCRequest{ + JSONRPC: "2.0", + Method: reverseFuncMap["DumpConsensusState"], + Params: []interface{}{}, + ID: "", + } + body, err := c.RequestResponse(request) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultDumpConsensusState) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientJSON) DumpStorage(address []byte) (*ctypes.ResultDumpStorage, error) { + request := rpctypes.RPCRequest{ + JSONRPC: "2.0", + Method: reverseFuncMap["DumpStorage"], + Params: []interface{}{address}, + ID: "", + } + body, err := c.RequestResponse(request) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultDumpStorage) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientJSON) GenPrivAccount() (*ctypes.ResultGenPrivAccount, error) { + request := rpctypes.RPCRequest{ + JSONRPC: "2.0", + Method: reverseFuncMap["GenPrivAccount"], + Params: []interface{}{}, + ID: "", + } + body, err := c.RequestResponse(request) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultGenPrivAccount) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientJSON) Genesis() (*ctypes.ResultGenesis, error) { + request := rpctypes.RPCRequest{ + JSONRPC: "2.0", + Method: reverseFuncMap["Genesis"], + Params: []interface{}{}, + ID: "", + } + body, err := c.RequestResponse(request) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultGenesis) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientJSON) GetAccount(address []byte) (*ctypes.ResultGetAccount, error) { + request := rpctypes.RPCRequest{ + JSONRPC: "2.0", + Method: reverseFuncMap["GetAccount"], + Params: []interface{}{address}, + ID: "", + } + body, err := c.RequestResponse(request) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultGetAccount) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientJSON) GetBlock(height int) (*ctypes.ResultGetBlock, error) { + request := rpctypes.RPCRequest{ + JSONRPC: "2.0", + Method: reverseFuncMap["GetBlock"], + Params: []interface{}{height}, + ID: "", + } + body, err := c.RequestResponse(request) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultGetBlock) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientJSON) GetName(name string) (*ctypes.ResultGetName, error) { + request := rpctypes.RPCRequest{ + JSONRPC: "2.0", + Method: reverseFuncMap["GetName"], + Params: []interface{}{name}, + ID: "", + } + body, err := c.RequestResponse(request) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultGetName) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientJSON) GetStorage(address []byte, key []byte) (*ctypes.ResultGetStorage, error) { + request := rpctypes.RPCRequest{ + JSONRPC: "2.0", + Method: reverseFuncMap["GetStorage"], + Params: []interface{}{address, key}, + ID: "", + } + body, err := c.RequestResponse(request) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultGetStorage) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientJSON) ListAccounts() (*ctypes.ResultListAccounts, error) { + request := rpctypes.RPCRequest{ + JSONRPC: "2.0", + Method: reverseFuncMap["ListAccounts"], + Params: []interface{}{}, + ID: "", + } + body, err := c.RequestResponse(request) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultListAccounts) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientJSON) ListNames() (*ctypes.ResultListNames, error) { + request := rpctypes.RPCRequest{ + JSONRPC: "2.0", + Method: reverseFuncMap["ListNames"], + Params: []interface{}{}, + ID: "", + } + body, err := c.RequestResponse(request) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultListNames) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientJSON) ListUnconfirmedTxs() (*ctypes.ResultListUnconfirmedTxs, error) { + request := rpctypes.RPCRequest{ + JSONRPC: "2.0", + Method: reverseFuncMap["ListUnconfirmedTxs"], + Params: []interface{}{}, + ID: "", + } + body, err := c.RequestResponse(request) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultListUnconfirmedTxs) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientJSON) ListValidators() (*ctypes.ResultListValidators, error) { + request := rpctypes.RPCRequest{ + JSONRPC: "2.0", + Method: reverseFuncMap["ListValidators"], + Params: []interface{}{}, + ID: "", + } + body, err := c.RequestResponse(request) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultListValidators) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientJSON) NetInfo() (*ctypes.ResultNetInfo, error) { + request := rpctypes.RPCRequest{ + JSONRPC: "2.0", + Method: reverseFuncMap["NetInfo"], + Params: []interface{}{}, + ID: "", + } + body, err := c.RequestResponse(request) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultNetInfo) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientJSON) SignTx(tx types.Tx, privAccounts []*acm.PrivAccount) (*ctypes.ResultSignTx, error) { + request := rpctypes.RPCRequest{ + JSONRPC: "2.0", + Method: reverseFuncMap["SignTx"], + Params: []interface{}{tx, privAccounts}, + ID: "", + } + body, err := c.RequestResponse(request) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultSignTx) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} + +func (c *ClientJSON) Status() (*ctypes.ResultStatus, error) { + request := rpctypes.RPCRequest{ + JSONRPC: "2.0", + Method: reverseFuncMap["Status"], + Params: []interface{}{}, + ID: "", + } + body, err := c.RequestResponse(request) + if err != nil { + return nil, err + } + response, err := unmarshalCheckResponse(body) + if err != nil { + return nil, err + } + if response.Result == nil { + return nil, nil + } + result, ok := response.Result.(*ctypes.ResultStatus) + if !ok { + return nil, fmt.Errorf("response result was wrong type") + } + return result, nil +} diff --git a/rpc/core_client/log.go b/rpc/core_client/log.go new file mode 100644 index 0000000000000000000000000000000000000000..54b638dc1eafc6095f18e05e47ee64dc6bbd9fd8 --- /dev/null +++ b/rpc/core_client/log.go @@ -0,0 +1,7 @@ +package core_client + +import ( + "github.com/tendermint/log15" +) + +var log = log15.New("module", "core_client") diff --git a/rpc/core_client/ws_client.go b/rpc/core_client/ws_client.go new file mode 100644 index 0000000000000000000000000000000000000000..fbd3619b402910d856a7b038f0610302b662b517 --- /dev/null +++ b/rpc/core_client/ws_client.go @@ -0,0 +1,119 @@ +package core_client + +import ( + "net/http" + "strings" + "time" + + ctypes "github.com/eris-ltd/eris-db/rpc/core/types" + "github.com/gorilla/websocket" + . "github.com/tendermint/go-common" + "github.com/tendermint/go-rpc/types" + "github.com/tendermint/go-wire" +) + +const ( + wsEventsChannelCapacity = 10 + wsResultsChannelCapacity = 10 + wsWriteTimeoutSeconds = 10 +) + +type WSClient struct { + QuitService + Address string + *websocket.Conn + EventsCh chan ctypes.ResultEvent + ResultsCh chan ctypes.Result +} + +// create a new connection +func NewWSClient(addr string) *WSClient { + wsClient := &WSClient{ + Address: addr, + Conn: nil, + EventsCh: make(chan ctypes.ResultEvent, wsEventsChannelCapacity), + ResultsCh: make(chan ctypes.Result, wsResultsChannelCapacity), + } + wsClient.QuitService = *NewQuitService(log, "WSClient", wsClient) + return wsClient +} + +func (wsc *WSClient) OnStart() error { + wsc.QuitService.OnStart() + err := wsc.dial() + if err != nil { + return err + } + go wsc.receiveEventsRoutine() + return nil +} + +func (wsc *WSClient) dial() error { + // Dial + dialer := websocket.DefaultDialer + rHeader := http.Header{} + con, _, err := dialer.Dial(wsc.Address, rHeader) + if err != nil { + return err + } + // Set the ping/pong handlers + con.SetPingHandler(func(m string) error { + con.WriteControl(websocket.PongMessage, []byte(m), time.Now().Add(time.Second*wsWriteTimeoutSeconds)) + return nil + }) + con.SetPongHandler(func(m string) error { + return nil + }) + wsc.Conn = con + return nil +} + +func (wsc *WSClient) OnStop() { + wsc.QuitService.OnStop() +} + +func (wsc *WSClient) receiveEventsRoutine() { + for { + _, data, err := wsc.ReadMessage() + if err != nil { + log.Info("WSClient failed to read message", "error", err, "data", string(data)) + wsc.Stop() + break + } else { + var response ctypes.Response + wire.ReadJSON(&response, data, &err) + if err != nil { + log.Info("WSClient failed to parse message", "error", err) + wsc.Stop() + break + } + if strings.HasSuffix(response.ID, "#event") { + wsc.EventsCh <- *response.Result.(*ctypes.ResultEvent) + } else { + wsc.ResultsCh <- response.Result + } + } + } +} + +// subscribe to an event +func (wsc *WSClient) Subscribe(eventid string) error { + err := wsc.WriteJSON(rpctypes.RPCRequest{ + JSONRPC: "2.0", + ID: "", + Method: "subscribe", + Params: []interface{}{eventid}, + }) + return err +} + +// unsubscribe from an event +func (wsc *WSClient) Unsubscribe(eventid string) error { + err := wsc.WriteJSON(rpctypes.RPCRequest{ + JSONRPC: "2.0", + ID: "", + Method: "unsubscribe", + Params: []interface{}{eventid}, + }) + return err +}