diff --git a/common/math/integral/integral_math.go b/common/math/integral/integral_math.go new file mode 100644 index 0000000000000000000000000000000000000000..8cdc6ee18d3a4a1e8ed777527d9605d325a4a61c --- /dev/null +++ b/common/math/integral/integral_math.go @@ -0,0 +1,157 @@ +package integral + +func MaxInt8(a, b int8) int8 { + if a > b { + return a + } + return b +} + +func MaxUint8(a, b uint8) uint8 { + if a > b { + return a + } + return b +} + +func MaxInt16(a, b int16) int16 { + if a > b { + return a + } + return b +} + +func MaxUint16(a, b uint16) uint16 { + if a > b { + return a + } + return b +} + +func MaxInt32(a, b int32) int32 { + if a > b { + return a + } + return b +} + +func MaxUint32(a, b uint32) uint32 { + if a > b { + return a + } + return b +} + +func MaxInt64(a, b int64) int64 { + if a > b { + return a + } + return b +} + +func MaxUint64(a, b uint64) uint64 { + if a > b { + return a + } + return b +} + +func MaxInt(a, b int) int { + if a > b { + return a + } + return b +} + +func MaxUint(a, b uint) uint { + if a > b { + return a + } + return b +} + +//----------------------------------------------------------------------------- + +func MinInt8(a, b int8) int8 { + if a < b { + return a + } + return b +} + +func MinUint8(a, b uint8) uint8 { + if a < b { + return a + } + return b +} + +func MinInt16(a, b int16) int16 { + if a < b { + return a + } + return b +} + +func MinUint16(a, b uint16) uint16 { + if a < b { + return a + } + return b +} + +func MinInt32(a, b int32) int32 { + if a < b { + return a + } + return b +} + +func MinUint32(a, b uint32) uint32 { + if a < b { + return a + } + return b +} + +func MinInt64(a, b int64) int64 { + if a < b { + return a + } + return b +} + +func MinUint64(a, b uint64) uint64 { + if a < b { + return a + } + return b +} + +func MinInt(a, b int) int { + if a < b { + return a + } + return b +} + +func MinUint(a, b uint) uint { + if a < b { + return a + } + return b +} + +//----------------------------------------------------------------------------- + +func ExpUint64(a, b uint64) uint64 { + accum := uint64(1) + for b > 0 { + if b&1 == 1 { + accum *= a + } + a *= a + b >>= 1 + } + return accum +} diff --git a/definitions/pipe.go b/definitions/pipe.go index f2f7ba8057650c8ad21bdf2b1bf3d244b6ccd524..f143e8a6465567c41da0c88d298773431b04f3cf 100644 --- a/definitions/pipe.go +++ b/definitions/pipe.go @@ -103,6 +103,5 @@ type Transactor interface { TransactNameReg(privKey []byte, name, data string, amount, fee int64) (*txs.Receipt, error) UnconfirmedTxs() (*txs.UnconfirmedTxs, error) - SignTx(tx txs.Tx, - privAccounts []*account.PrivAccount) (txs.Tx, error) + SignTx(tx txs.Tx, privAccounts []*account.PrivAccount) (txs.Tx, error) } diff --git a/definitions/tendermint_pipe.go b/definitions/tendermint_pipe.go index 01d5d849827ae233d70302012ce69e41cf1fb2bd..76e7a2a617d8ed39b865f435d5acf2d532a450d5 100644 --- a/definitions/tendermint_pipe.go +++ b/definitions/tendermint_pipe.go @@ -17,8 +17,8 @@ package definitions import ( - account "github.com/eris-ltd/eris-db/account" - rpc_tendermint_types "github.com/eris-ltd/eris-db/rpc/tendermint/core/types" + "github.com/eris-ltd/eris-db/account" + rpc_tm_types "github.com/eris-ltd/eris-db/rpc/tendermint/core/types" "github.com/eris-ltd/eris-db/txs" ) @@ -28,48 +28,45 @@ import ( // collection of RPC routes for Eris-DB-1.0.0 type TendermintPipe interface { - // Net - Status() (*rpc_tendermint_types.ResultStatus, error) - - NetInfo() (*rpc_tendermint_types.ResultNetInfo, error) + Pipe + // Events + // Subscribe attempts to subscribe the listener identified by listenerId to + // the event named event. The Event result is written to rpcResponseWriter + // which must be non-blocking + Subscribe(listenerId, event string, + rpcResponseWriter func(result rpc_tm_types.ErisDBResult)) (*rpc_tm_types.ResultSubscribe, error) + Unsubscribe(listenerId, event string) (*rpc_tm_types.ResultUnsubscribe, error) - Genesis() (*rpc_tendermint_types.ResultGenesis, error) + // Net + Status() (*rpc_tm_types.ResultStatus, error) + NetInfo() (*rpc_tm_types.ResultNetInfo, error) + Genesis() (*rpc_tm_types.ResultGenesis, error) // Accounts - GetAccount(address []byte) (*rpc_tendermint_types.ResultGetAccount, - error) - - ListAccounts() (*rpc_tendermint_types.ResultListAccounts, error) - - GetStorage(address, key []byte) (*rpc_tendermint_types.ResultGetStorage, - error) - - DumpStorage(address []byte) (*rpc_tendermint_types.ResultDumpStorage, - error) + GetAccount(address []byte) (*rpc_tm_types.ResultGetAccount, error) + ListAccounts() (*rpc_tm_types.ResultListAccounts, error) + GetStorage(address, key []byte) (*rpc_tm_types.ResultGetStorage, error) + DumpStorage(address []byte) (*rpc_tm_types.ResultDumpStorage, error) // Call - Call(fromAddress, toAddress, data []byte) (*rpc_tendermint_types.ResultCall, - error) - - CallCode(fromAddress, code, data []byte) (*rpc_tendermint_types.ResultCall, - error) + Call(fromAddress, toAddress, data []byte) (*rpc_tm_types.ResultCall, error) + CallCode(fromAddress, code, data []byte) (*rpc_tm_types.ResultCall, error) // TODO: [ben] deprecate as we should not allow unsafe behaviour // where a user is allowed to send a private key over the wire, // especially unencrypted. SignTransaction(tx txs.Tx, - privAccounts []*account.PrivAccount) (*rpc_tendermint_types.ResultSignTx, + privAccounts []*account.PrivAccount) (*rpc_tm_types.ResultSignTx, error) // Name registry - GetName(name string) (*rpc_tendermint_types.ResultGetName, error) - - ListNames() (*rpc_tendermint_types.ResultListNames, error) + GetName(name string) (*rpc_tm_types.ResultGetName, error) + ListNames() (*rpc_tm_types.ResultListNames, error) // Memory pool - BroadcastTxAsync(transaction txs.Tx) (*rpc_tendermint_types.ResultBroadcastTx, - error) + BroadcastTxAsync(transaction txs.Tx) (*rpc_tm_types.ResultBroadcastTx, error) + BroadcastTxSync(transaction txs.Tx) (*rpc_tm_types.ResultBroadcastTx, error) - BroadcastTxSync(transaction txs.Tx) (*rpc_tendermint_types.ResultBroadcastTx, - error) + // Blockchain + BlockchainInfo(minHeight, maxHeight, maxBlockLookback int) (*rpc_tm_types.ResultBlockchainInfo, error) } diff --git a/event/event_cache.go b/event/event_cache.go index 5b196e16eb3d12eeb9c672a6e3965c79a138a72f..326cf9b1c56b7792e952c225f5d0beb23606e1ff 100644 --- a/event/event_cache.go +++ b/event/event_cache.go @@ -80,7 +80,7 @@ func reap(es *EventSubscriptions) { } // Add a subscription and return the generated id. Note event dispatcher -// has to call func which involves aquiring a mutex lock, so might be +// has to call func which involves acquiring a mutex lock, so might be // a delay - though a conflict is practically impossible, and if it does // happen it's for an insignificant amount of time (the time it takes to // carry out EventCache.poll() ). diff --git a/event/events.go b/event/events.go index 8861056cb40f4bb501c6325870a33c344d6e6494..dd5f47f6a41e9ee1ffbdcf0b1a96db2f113c7b84 100644 --- a/event/events.go +++ b/event/events.go @@ -39,7 +39,7 @@ type events struct { eventSwitch *evts.EventSwitch } -func newEvents(eventSwitch *evts.EventSwitch) *events { +func NewEvents(eventSwitch *evts.EventSwitch) *events { return &events{eventSwitch} } diff --git a/manager/eris-mint/events.go b/manager/eris-mint/events.go deleted file mode 100644 index c7d582aecb6fc42c2d11114b695c7cbabcbb1f22..0000000000000000000000000000000000000000 --- a/manager/eris-mint/events.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see <http://www.gnu.org/licenses/>. - -// Events is part of the pipe for ErisMint and provides the implementation -// for the pipe to call into the ErisMint application -package erismint - -import ( - evts "github.com/tendermint/go-events" -) - -// TODO improve - -// The events struct has methods for working with events. -type events struct { - eventSwitch *evts.EventSwitch -} - -func newEvents(eventSwitch *evts.EventSwitch) *events { - return &events{eventSwitch} -} - -// Subscribe to an event. -func (this *events) Subscribe(subId, event string, callback func(evts.EventData)) (bool, error) { - this.eventSwitch.AddListenerForEvent(subId, event, callback) - return true, nil -} - -// Un-subscribe from an event. -func (this *events) Unsubscribe(subId string) (bool, error) { - this.eventSwitch.RemoveListener(subId) - return true, nil -} diff --git a/manager/eris-mint/pipe.go b/manager/eris-mint/pipe.go index 5741617145937691d8b0579c2e561703d7e0cb47..46e5e57fad0c8e5168c7da17f814c176e752d0b4 100644 --- a/manager/eris-mint/pipe.go +++ b/manager/eris-mint/pipe.go @@ -20,38 +20,39 @@ import ( "bytes" "fmt" - tendermint_common "github.com/tendermint/go-common" + tm_common "github.com/tendermint/go-common" crypto "github.com/tendermint/go-crypto" db "github.com/tendermint/go-db" - tendermint_events "github.com/tendermint/go-events" + go_events "github.com/tendermint/go-events" wire "github.com/tendermint/go-wire" - tendermint_types "github.com/tendermint/tendermint/types" + tm_types "github.com/tendermint/tendermint/types" tmsp_types "github.com/tendermint/tmsp/types" log "github.com/eris-ltd/eris-logger" account "github.com/eris-ltd/eris-db/account" + imath "github.com/eris-ltd/eris-db/common/math/integral" config "github.com/eris-ltd/eris-db/config" core_types "github.com/eris-ltd/eris-db/core/types" definitions "github.com/eris-ltd/eris-db/definitions" - event "github.com/eris-ltd/eris-db/event" + edb_event "github.com/eris-ltd/eris-db/event" vm "github.com/eris-ltd/eris-db/manager/eris-mint/evm" state "github.com/eris-ltd/eris-db/manager/eris-mint/state" state_types "github.com/eris-ltd/eris-db/manager/eris-mint/state/types" manager_types "github.com/eris-ltd/eris-db/manager/types" - rpc_tendermint_types "github.com/eris-ltd/eris-db/rpc/tendermint/core/types" + rpc_tm_types "github.com/eris-ltd/eris-db/rpc/tendermint/core/types" "github.com/eris-ltd/eris-db/txs" ) type ErisMintPipe struct { erisMintState *state.State - eventSwitch *tendermint_events.EventSwitch + eventSwitch *go_events.EventSwitch erisMint *ErisMint // Pipe implementations accounts *accounts blockchain *blockchain consensus *consensus - events event.EventEmitter + events edb_event.EventEmitter namereg *namereg network *network transactor *transactor @@ -71,7 +72,7 @@ var _ definitions.Pipe = (*ErisMintPipe)(nil) var _ definitions.TendermintPipe = (*ErisMintPipe)(nil) func NewErisMintPipe(moduleConfig *config.ModuleConfig, - eventSwitch *tendermint_events.EventSwitch) (*ErisMintPipe, error) { + eventSwitch *go_events.EventSwitch) (*ErisMintPipe, error) { startedState, genesisDoc, err := startState(moduleConfig.DataDir, moduleConfig.Config.GetString("db_backend"), moduleConfig.GenesisFile, @@ -95,7 +96,7 @@ func NewErisMintPipe(moduleConfig *config.ModuleConfig, erisMint.SetHostAddress(tendermintHost) // initialise the components of the pipe - events := newEvents(eventSwitch) + events := edb_event.NewEvents(eventSwitch) accounts := newAccounts(erisMint) namereg := newNameReg(erisMint) transactor := newTransactor(moduleConfig.ChainId, eventSwitch, erisMint, @@ -186,7 +187,7 @@ func (pipe *ErisMintPipe) Consensus() definitions.Consensus { return pipe.consensus } -func (pipe *ErisMintPipe) Events() event.EventEmitter { +func (pipe *ErisMintPipe) Events() edb_event.EventEmitter { return pipe.events } @@ -227,8 +228,33 @@ func (pipe *ErisMintPipe) GetTendermintPipe() (definitions.TendermintPipe, //------------------------------------------------------------------------------ // Implement definitions.TendermintPipe for ErisMintPipe +func (pipe *ErisMintPipe) Subscribe(listenerId, event string, + rpcResponseWriter func(result rpc_tm_types.ErisDBResult)) (*rpc_tm_types.ResultSubscribe, error) { + log.WithFields(log.Fields{"listenerId": listenerId, "event": event}). + Info("Subscribing to event") + pipe.events.Subscribe(subscriptionId(listenerId, event), event, + func(eventData go_events.EventData) { + result := rpc_tm_types.ErisDBResult(&rpc_tm_types.ResultEvent{event, + tm_types.TMEventData(eventData)}) + // NOTE: EventSwitch callbacks must be nonblocking + rpcResponseWriter(result) + }) + return &rpc_tm_types.ResultSubscribe{}, nil +} + +func (pipe *ErisMintPipe) Unsubscribe(listenerId, + event string) (*rpc_tm_types.ResultUnsubscribe, error) { + log.WithFields(log.Fields{"listenerId": listenerId, "event": event}). + Info("Unsubsribing from event") + pipe.events.Unsubscribe(subscriptionId(listenerId, event)) + return &rpc_tm_types.ResultUnsubscribe{}, nil +} + +func subscriptionId(listenerId, event string) string { + return fmt.Sprintf("%s#%s", listenerId, event) +} -func (pipe *ErisMintPipe) Status() (*rpc_tendermint_types.ResultStatus, error) { +func (pipe *ErisMintPipe) Status() (*rpc_tm_types.ResultStatus, error) { memoryDatabase := db.NewMemDB() if pipe.genesisState == nil { pipe.genesisState = state.MakeGenesisState(memoryDatabase, pipe.genesisDoc) @@ -239,7 +265,7 @@ func (pipe *ErisMintPipe) Status() (*rpc_tendermint_types.ResultStatus, error) { } latestHeight := pipe.consensusEngine.Height() var ( - latestBlockMeta *tendermint_types.BlockMeta + latestBlockMeta *tm_types.BlockMeta latestBlockHash []byte latestBlockTime int64 ) @@ -248,7 +274,7 @@ func (pipe *ErisMintPipe) Status() (*rpc_tendermint_types.ResultStatus, error) { latestBlockHash = latestBlockMeta.Hash latestBlockTime = latestBlockMeta.Header.Time.UnixNano() } - return &rpc_tendermint_types.ResultStatus{ + return &rpc_tm_types.ResultStatus{ NodeInfo: pipe.consensusEngine.NodeInfo(), GenesisHash: genesisHash, PubKey: pipe.consensusEngine.PublicValidatorKey(), @@ -257,41 +283,41 @@ func (pipe *ErisMintPipe) Status() (*rpc_tendermint_types.ResultStatus, error) { LatestBlockTime: latestBlockTime}, nil } -func (pipe *ErisMintPipe) NetInfo() (*rpc_tendermint_types.ResultNetInfo, error) { +func (pipe *ErisMintPipe) NetInfo() (*rpc_tm_types.ResultNetInfo, error) { listening := pipe.consensusEngine.IsListening() listeners := []string{} for _, listener := range pipe.consensusEngine.Listeners() { listeners = append(listeners, listener.String()) } peers := pipe.consensusEngine.Peers() - return &rpc_tendermint_types.ResultNetInfo{ + return &rpc_tm_types.ResultNetInfo{ Listening: listening, Listeners: listeners, Peers: peers, }, nil } -func (pipe *ErisMintPipe) Genesis() (*rpc_tendermint_types.ResultGenesis, error) { - return &rpc_tendermint_types.ResultGenesis{ +func (pipe *ErisMintPipe) Genesis() (*rpc_tm_types.ResultGenesis, error) { + return &rpc_tm_types.ResultGenesis{ // TODO: [ben] sharing pointer to unmutated GenesisDoc, but is not immutable Genesis: pipe.genesisDoc, }, nil } // Accounts -func (pipe *ErisMintPipe) GetAccount(address []byte) (*rpc_tendermint_types.ResultGetAccount, +func (pipe *ErisMintPipe) GetAccount(address []byte) (*rpc_tm_types.ResultGetAccount, error) { cache := pipe.erisMint.GetCheckCache() // cache := mempoolReactor.Mempool.GetCache() account := cache.GetAccount(address) if account == nil { log.Warn("Nil Account") - return &rpc_tendermint_types.ResultGetAccount{nil}, nil + return &rpc_tm_types.ResultGetAccount{nil}, nil } - return &rpc_tendermint_types.ResultGetAccount{account}, nil + return &rpc_tm_types.ResultGetAccount{account}, nil } -func (pipe *ErisMintPipe) ListAccounts() (*rpc_tendermint_types.ResultListAccounts, error) { +func (pipe *ErisMintPipe) ListAccounts() (*rpc_tm_types.ResultListAccounts, error) { var blockHeight int var accounts []*account.Account state := pipe.erisMint.GetState() @@ -300,10 +326,10 @@ func (pipe *ErisMintPipe) ListAccounts() (*rpc_tendermint_types.ResultListAccoun accounts = append(accounts, account.DecodeAccount(value)) return false }) - return &rpc_tendermint_types.ResultListAccounts{blockHeight, accounts}, nil + return &rpc_tm_types.ResultListAccounts{blockHeight, accounts}, nil } -func (pipe *ErisMintPipe) GetStorage(address, key []byte) (*rpc_tendermint_types.ResultGetStorage, +func (pipe *ErisMintPipe) GetStorage(address, key []byte) (*rpc_tm_types.ResultGetStorage, error) { state := pipe.erisMint.GetState() // state := consensusState.GetState() @@ -315,14 +341,15 @@ func (pipe *ErisMintPipe) GetStorage(address, key []byte) (*rpc_tendermint_types storageTree := state.LoadStorage(storageRoot) _, value, exists := storageTree.Get( - tendermint_common.LeftPadWord256(key).Bytes()) - if !exists { // value == nil { - return &rpc_tendermint_types.ResultGetStorage{key, nil}, nil + tm_common.LeftPadWord256(key).Bytes()) + if !exists { + // value == nil { + return &rpc_tm_types.ResultGetStorage{key, nil}, nil } - return &rpc_tendermint_types.ResultGetStorage{key, value}, nil + return &rpc_tm_types.ResultGetStorage{key, value}, nil } -func (pipe *ErisMintPipe) DumpStorage(address []byte) (*rpc_tendermint_types.ResultDumpStorage, +func (pipe *ErisMintPipe) DumpStorage(address []byte) (*rpc_tm_types.ResultDumpStorage, error) { state := pipe.erisMint.GetState() account := state.GetAccount(address) @@ -331,17 +358,17 @@ func (pipe *ErisMintPipe) DumpStorage(address []byte) (*rpc_tendermint_types.Res } storageRoot := account.StorageRoot storageTree := state.LoadStorage(storageRoot) - storageItems := []rpc_tendermint_types.StorageItem{} + storageItems := []rpc_tm_types.StorageItem{} storageTree.Iterate(func(key []byte, value []byte) bool { - storageItems = append(storageItems, rpc_tendermint_types.StorageItem{key, + storageItems = append(storageItems, rpc_tm_types.StorageItem{key, value}) return false }) - return &rpc_tendermint_types.ResultDumpStorage{storageRoot, storageItems}, nil + return &rpc_tm_types.ResultDumpStorage{storageRoot, storageItems}, nil } // Call -func (pipe *ErisMintPipe) Call(fromAddress, toAddress, data []byte) (*rpc_tendermint_types.ResultCall, +func (pipe *ErisMintPipe) Call(fromAddress, toAddress, data []byte) (*rpc_tm_types.ResultCall, error) { st := pipe.erisMint.GetState() cache := state.NewBlockCache(st) @@ -350,11 +377,11 @@ func (pipe *ErisMintPipe) Call(fromAddress, toAddress, data []byte) (*rpc_tender return nil, fmt.Errorf("Account %x does not exist", toAddress) } callee := toVMAccount(outAcc) - caller := &vm.Account{Address: tendermint_common.LeftPadWord256(fromAddress)} + caller := &vm.Account{Address: tm_common.LeftPadWord256(fromAddress)} txCache := state.NewTxCache(cache) params := vm.Params{ BlockHeight: int64(st.LastBlockHeight), - BlockHash: tendermint_common.LeftPadWord256(st.LastBlockHash), + BlockHash: tm_common.LeftPadWord256(st.LastBlockHash), BlockTime: st.LastBlockTime.Unix(), GasLimit: st.GetGasLimit(), } @@ -365,19 +392,19 @@ func (pipe *ErisMintPipe) Call(fromAddress, toAddress, data []byte) (*rpc_tender if err != nil { return nil, err } - return &rpc_tendermint_types.ResultCall{Return: ret}, nil + return &rpc_tm_types.ResultCall{Return: ret}, nil } -func (pipe *ErisMintPipe) CallCode(fromAddress, code, data []byte) (*rpc_tendermint_types.ResultCall, +func (pipe *ErisMintPipe) CallCode(fromAddress, code, data []byte) (*rpc_tm_types.ResultCall, error) { st := pipe.erisMint.GetState() cache := pipe.erisMint.GetCheckCache() - callee := &vm.Account{Address: tendermint_common.LeftPadWord256(fromAddress)} - caller := &vm.Account{Address: tendermint_common.LeftPadWord256(fromAddress)} + callee := &vm.Account{Address: tm_common.LeftPadWord256(fromAddress)} + caller := &vm.Account{Address: tm_common.LeftPadWord256(fromAddress)} txCache := state.NewTxCache(cache) params := vm.Params{ BlockHeight: int64(st.LastBlockHeight), - BlockHash: tendermint_common.LeftPadWord256(st.LastBlockHash), + BlockHash: tm_common.LeftPadWord256(st.LastBlockHash), BlockTime: st.LastBlockTime.Unix(), GasLimit: st.GetGasLimit(), } @@ -388,14 +415,14 @@ func (pipe *ErisMintPipe) CallCode(fromAddress, code, data []byte) (*rpc_tenderm if err != nil { return nil, err } - return &rpc_tendermint_types.ResultCall{Return: ret}, nil + return &rpc_tm_types.ResultCall{Return: ret}, nil } // TODO: [ben] deprecate as we should not allow unsafe behaviour // where a user is allowed to send a private key over the wire, // especially unencrypted. func (pipe *ErisMintPipe) SignTransaction(tx txs.Tx, - privAccounts []*account.PrivAccount) (*rpc_tendermint_types.ResultSignTx, + privAccounts []*account.PrivAccount) (*rpc_tm_types.ResultSignTx, error) { for i, privAccount := range privAccounts { @@ -430,20 +457,20 @@ func (pipe *ErisMintPipe) SignTransaction(tx txs.Tx, rebondTx := tx.(*txs.RebondTx) rebondTx.Signature = privAccounts[0].Sign(pipe.transactor.chainID, rebondTx).(crypto.SignatureEd25519) } - return &rpc_tendermint_types.ResultSignTx{tx}, nil + return &rpc_tm_types.ResultSignTx{tx}, nil } // Name registry -func (pipe *ErisMintPipe) GetName(name string) (*rpc_tendermint_types.ResultGetName, error) { +func (pipe *ErisMintPipe) GetName(name string) (*rpc_tm_types.ResultGetName, error) { currentState := pipe.erisMint.GetState() entry := currentState.GetNameRegEntry(name) if entry == nil { return nil, fmt.Errorf("Name %s not found", name) } - return &rpc_tendermint_types.ResultGetName{entry}, nil + return &rpc_tm_types.ResultGetName{entry}, nil } -func (pipe *ErisMintPipe) ListNames() (*rpc_tendermint_types.ResultListNames, error) { +func (pipe *ErisMintPipe) ListNames() (*rpc_tm_types.ResultListNames, error) { var blockHeight int var names []*core_types.NameRegEntry currentState := pipe.erisMint.GetState() @@ -452,25 +479,27 @@ func (pipe *ErisMintPipe) ListNames() (*rpc_tendermint_types.ResultListNames, er names = append(names, state.DecodeNameRegEntry(value)) return false }) - return &rpc_tendermint_types.ResultListNames{blockHeight, names}, nil + return &rpc_tm_types.ResultListNames{blockHeight, names}, nil } // Memory pool // NOTE: txs must be signed func (pipe *ErisMintPipe) BroadcastTxAsync(tx txs.Tx) ( - *rpc_tendermint_types.ResultBroadcastTx, error) { + *rpc_tm_types.ResultBroadcastTx, error) { err := pipe.consensusEngine.BroadcastTransaction(txs.EncodeTx(tx), nil) if err != nil { return nil, fmt.Errorf("Error broadcasting txs: %v", err) } - return &rpc_tendermint_types.ResultBroadcastTx{}, nil + return &rpc_tm_types.ResultBroadcastTx{}, nil } -func (pipe *ErisMintPipe) BroadcastTxSync(tx txs.Tx) (*rpc_tendermint_types.ResultBroadcastTx, +func (pipe *ErisMintPipe) BroadcastTxSync(tx txs.Tx) (*rpc_tm_types.ResultBroadcastTx, error) { responseChannel := make(chan *tmsp_types.Response, 1) err := pipe.consensusEngine.BroadcastTransaction(txs.EncodeTx(tx), - func(res *tmsp_types.Response) { responseChannel <- res }) + func(res *tmsp_types.Response) { + responseChannel <- res + }) if err != nil { return nil, fmt.Errorf("Error broadcasting txs: %v", err) } @@ -484,7 +513,7 @@ func (pipe *ErisMintPipe) BroadcastTxSync(tx txs.Tx) (*rpc_tendermint_types.Resu if responseCheckTx == nil { return nil, fmt.Errorf("Error, application did not return CheckTx response.") } - resultBroadCastTx := &rpc_tendermint_types.ResultBroadcastTx{ + resultBroadCastTx := &rpc_tm_types.ResultBroadcastTx{ Code: responseCheckTx.Code, Data: responseCheckTx.Data, Log: responseCheckTx.Log, @@ -505,3 +534,31 @@ func (pipe *ErisMintPipe) BroadcastTxSync(tx txs.Tx) (*rpc_tendermint_types.Resu return resultBroadCastTx, fmt.Errorf("Unknown error returned: " + responseCheckTx.Log) } } + +// Returns the current blockchain height and metadata for a range of blocks +// between minHeight and maxHeight. Only returns maxBlockLookback block metadata +// from the top of the range of blocks. +// Passing 0 for maxHeight sets the upper height of the range to the current +// blockchain height. +func (pipe *ErisMintPipe) BlockchainInfo(minHeight, maxHeight, + maxBlockLookback int) (*rpc_tm_types.ResultBlockchainInfo, error) { + + blockStore := pipe.blockchain.blockStore + + if maxHeight < 1 { + maxHeight = blockStore.Height() + } else { + maxHeight = imath.MinInt(blockStore.Height(), maxHeight) + } + if minHeight < 1 { + minHeight = imath.MaxInt(1, maxHeight-maxBlockLookback) + } + + blockMetas := []*tm_types.BlockMeta{} + for height := maxHeight; height >= minHeight; height-- { + blockMeta := blockStore.LoadBlockMeta(height) + blockMetas = append(blockMetas, blockMeta) + } + + return &rpc_tm_types.ResultBlockchainInfo{blockStore.Height(), blockMetas}, nil +} diff --git a/rpc/tendermint/core/routes.go b/rpc/tendermint/core/routes.go index ea041d305f0e0a3b6258a2cd2c04555db6f806e2..c00d3f5579cb4d11a87935c65daafe3cb208c20c 100644 --- a/rpc/tendermint/core/routes.go +++ b/rpc/tendermint/core/routes.go @@ -8,59 +8,41 @@ import ( ctypes "github.com/eris-ltd/eris-db/rpc/tendermint/core/types" "github.com/eris-ltd/eris-db/txs" rpc "github.com/tendermint/go-rpc/server" + rpctypes "github.com/tendermint/go-rpc/types" ) // TODO: [ben] encapsulate Routes into a struct for a given TendermintPipe -// TODO: eliminate redundancy between here and reading code from core/ -// var Routes = map[string]*rpc.RPCFunc{ -// "status": rpc.NewRPCFunc(StatusResult, ""), -// "net_info": rpc.NewRPCFunc(NetInfoResult, ""), -// "genesis": rpc.NewRPCFunc(GenesisResult, ""), -// "get_account": rpc.NewRPCFunc(GetAccountResult, "address"), -// "get_storage": rpc.NewRPCFunc(GetStorageResult, "address,key"), -// "call": rpc.NewRPCFunc(CallResult, "fromAddress,toAddress,data"), -// "call_code": rpc.NewRPCFunc(CallCodeResult, "fromAddress,code,data"), -// "dump_storage": rpc.NewRPCFunc(DumpStorageResult, "address"), -// "list_accounts": rpc.NewRPCFunc(ListAccountsResult, ""), -// "get_name": rpc.NewRPCFunc(GetNameResult, "name"), -// "list_names": rpc.NewRPCFunc(ListNamesResult, ""), -// "broadcast_tx": rpc.NewRPCFunc(BroadcastTxResult, "tx"), -// "unsafe/gen_priv_account": rpc.NewRPCFunc(GenPrivAccountResult, ""), -// "unsafe/sign_tx": rpc.NewRPCFunc(SignTxResult, "tx,privAccounts"), -// -// // TODO: hookup -// // "blockchain": rpc.NewRPCFunc(BlockchainInfo, "minHeight,maxHeight"), -// // "get_block": rpc.NewRPCFunc(GetBlock, "height"), -// //"list_validators": rpc.NewRPCFunc(ListValidators, ""), -// // "dump_consensus_state": rpc.NewRPCFunc(DumpConsensusState, ""), -// // "list_unconfirmed_txs": rpc.NewRPCFunc(ListUnconfirmedTxs, ""), -// // subscribe/unsubscribe are reserved for websocket events. -// } +// Magic! Should probably be configurable, but not shouldn't be so huge we +// end up DoSing ourselves. +const maxBlockLookback = 20 +// TODO: eliminate redundancy between here and reading code from core/ type TendermintRoutes struct { tendermintPipe definitions.TendermintPipe } -func (this *TendermintRoutes) GetRoutes() map[string]*rpc.RPCFunc { +func (tmRoutes *TendermintRoutes) GetRoutes() map[string]*rpc.RPCFunc { var routes = map[string]*rpc.RPCFunc{ - "status": rpc.NewRPCFunc(this.StatusResult, ""), - "net_info": rpc.NewRPCFunc(this.NetInfoResult, ""), - "genesis": rpc.NewRPCFunc(this.GenesisResult, ""), - "get_account": rpc.NewRPCFunc(this.GetAccountResult, "address"), - "get_storage": rpc.NewRPCFunc(this.GetStorageResult, "address,key"), - "call": rpc.NewRPCFunc(this.CallResult, "fromAddress,toAddress,data"), - "call_code": rpc.NewRPCFunc(this.CallCodeResult, "fromAddress,code,data"), - "dump_storage": rpc.NewRPCFunc(this.DumpStorageResult, "address"), - "list_accounts": rpc.NewRPCFunc(this.ListAccountsResult, ""), - "get_name": rpc.NewRPCFunc(this.GetNameResult, "name"), - "list_names": rpc.NewRPCFunc(this.ListNamesResult, ""), - "broadcast_tx": rpc.NewRPCFunc(this.BroadcastTxResult, "tx"), - "unsafe/gen_priv_account": rpc.NewRPCFunc(this.GenPrivAccountResult, ""), - "unsafe/sign_tx": rpc.NewRPCFunc(this.SignTxResult, "tx,privAccounts"), + "subscribe": rpc.NewWSRPCFunc(tmRoutes.Subscribe, "event"), + "unsubscribe": rpc.NewWSRPCFunc(tmRoutes.Unsubscribe, "event"), + "status": rpc.NewRPCFunc(tmRoutes.StatusResult, ""), + "net_info": rpc.NewRPCFunc(tmRoutes.NetInfoResult, ""), + "genesis": rpc.NewRPCFunc(tmRoutes.GenesisResult, ""), + "get_account": rpc.NewRPCFunc(tmRoutes.GetAccountResult, "address"), + "get_storage": rpc.NewRPCFunc(tmRoutes.GetStorageResult, "address,key"), + "call": rpc.NewRPCFunc(tmRoutes.CallResult, "fromAddress,toAddress,data"), + "call_code": rpc.NewRPCFunc(tmRoutes.CallCodeResult, "fromAddress,code,data"), + "dump_storage": rpc.NewRPCFunc(tmRoutes.DumpStorageResult, "address"), + "list_accounts": rpc.NewRPCFunc(tmRoutes.ListAccountsResult, ""), + "get_name": rpc.NewRPCFunc(tmRoutes.GetNameResult, "name"), + "list_names": rpc.NewRPCFunc(tmRoutes.ListNamesResult, ""), + "broadcast_tx": rpc.NewRPCFunc(tmRoutes.BroadcastTxResult, "tx"), + "unsafe/gen_priv_account": rpc.NewRPCFunc(tmRoutes.GenPrivAccountResult, ""), + "unsafe/sign_tx": rpc.NewRPCFunc(tmRoutes.SignTxResult, "tx,privAccounts"), // TODO: hookup - // "blockchain": rpc.NewRPCFunc(BlockchainInfo, "minHeight,maxHeight"), + "blockchain": rpc.NewRPCFunc(tmRoutes.BlockchainInfo, "minHeight,maxHeight"), // "get_block": rpc.NewRPCFunc(GetBlock, "height"), //"list_validators": rpc.NewRPCFunc(ListValidators, ""), // "dump_consensus_state": rpc.NewRPCFunc(DumpConsensusState, ""), @@ -70,96 +52,125 @@ func (this *TendermintRoutes) GetRoutes() map[string]*rpc.RPCFunc { return routes } -func (this *TendermintRoutes) StatusResult() (ctypes.ErisDBResult, error) { - if r, err := this.tendermintPipe.Status(); err != nil { +func (tmRoutes *TendermintRoutes) Subscribe(wsCtx rpctypes.WSRPCContext, + event string) (ctypes.ErisDBResult, error) { + // NOTE: RPCResponses of subscribed events have id suffix "#event" + result, err := tmRoutes.tendermintPipe.Subscribe(wsCtx.GetRemoteAddr(), event, + func(result ctypes.ErisDBResult) { + // NOTE: EventSwitch callbacks must be nonblocking + wsCtx.TryWriteRPCResponse( + rpctypes.NewRPCResponse(wsCtx.Request.ID+"#event", &result, "")) + }) + if err != nil { + return nil, err + } else { + return result, nil + } +} + +func (tmRoutes *TendermintRoutes) Unsubscribe(wsCtx rpctypes.WSRPCContext, + event string) (ctypes.ErisDBResult, error) { + result, err := tmRoutes.tendermintPipe.Unsubscribe(wsCtx.GetRemoteAddr(), + event) + if err != nil { + return nil, err + } else { + return result, nil + } +} + +func (tmRoutes *TendermintRoutes) StatusResult() (ctypes.ErisDBResult, error) { + if r, err := tmRoutes.tendermintPipe.Status(); err != nil { return nil, err } else { return r, nil } } -func (this *TendermintRoutes) NetInfoResult() (ctypes.ErisDBResult, error) { - if r, err := this.tendermintPipe.NetInfo(); err != nil { +func (tmRoutes *TendermintRoutes) NetInfoResult() (ctypes.ErisDBResult, error) { + if r, err := tmRoutes.tendermintPipe.NetInfo(); err != nil { return nil, err } else { return r, nil } } -func (this *TendermintRoutes) GenesisResult() (ctypes.ErisDBResult, error) { - if r, err := this.tendermintPipe.Genesis(); err != nil { +func (tmRoutes *TendermintRoutes) GenesisResult() (ctypes.ErisDBResult, error) { + if r, err := tmRoutes.tendermintPipe.Genesis(); err != nil { return nil, err } else { return r, nil } } -func (this *TendermintRoutes) GetAccountResult(address []byte) (ctypes.ErisDBResult, error) { - if r, err := this.tendermintPipe.GetAccount(address); err != nil { +func (tmRoutes *TendermintRoutes) GetAccountResult(address []byte) (ctypes.ErisDBResult, error) { + if r, err := tmRoutes.tendermintPipe.GetAccount(address); err != nil { return nil, err } else { return r, nil } } -func (this *TendermintRoutes) GetStorageResult(address, key []byte) (ctypes.ErisDBResult, error) { - if r, err := this.tendermintPipe.GetStorage(address, key); err != nil { +func (tmRoutes *TendermintRoutes) GetStorageResult(address, key []byte) (ctypes.ErisDBResult, error) { + if r, err := tmRoutes.tendermintPipe.GetStorage(address, key); err != nil { return nil, err } else { return r, nil } } -func (this *TendermintRoutes) CallResult(fromAddress, toAddress, data []byte) (ctypes.ErisDBResult, error) { - if r, err := this.tendermintPipe.Call(fromAddress, toAddress, data); err != nil { +func (tmRoutes *TendermintRoutes) CallResult(fromAddress, toAddress, + data []byte) (ctypes.ErisDBResult, error) { + if r, err := tmRoutes.tendermintPipe.Call(fromAddress, toAddress, data); err != nil { return nil, err } else { return r, nil } } -func (this *TendermintRoutes) CallCodeResult(fromAddress, code, data []byte) (ctypes.ErisDBResult, error) { - if r, err := this.tendermintPipe.CallCode(fromAddress, code, data); err != nil { +func (tmRoutes *TendermintRoutes) CallCodeResult(fromAddress, code, + data []byte) (ctypes.ErisDBResult, error) { + if r, err := tmRoutes.tendermintPipe.CallCode(fromAddress, code, data); err != nil { return nil, err } else { return r, nil } } -func (this *TendermintRoutes) DumpStorageResult(address []byte) (ctypes.ErisDBResult, error) { - if r, err := this.tendermintPipe.DumpStorage(address); err != nil { +func (tmRoutes *TendermintRoutes) DumpStorageResult(address []byte) (ctypes.ErisDBResult, error) { + if r, err := tmRoutes.tendermintPipe.DumpStorage(address); err != nil { return nil, err } else { return r, nil } } -func (this *TendermintRoutes) ListAccountsResult() (ctypes.ErisDBResult, error) { - if r, err := this.tendermintPipe.ListAccounts(); err != nil { +func (tmRoutes *TendermintRoutes) ListAccountsResult() (ctypes.ErisDBResult, error) { + if r, err := tmRoutes.tendermintPipe.ListAccounts(); err != nil { return nil, err } else { return r, nil } } -func (this *TendermintRoutes) GetNameResult(name string) (ctypes.ErisDBResult, error) { - if r, err := this.tendermintPipe.GetName(name); err != nil { +func (tmRoutes *TendermintRoutes) GetNameResult(name string) (ctypes.ErisDBResult, error) { + if r, err := tmRoutes.tendermintPipe.GetName(name); err != nil { return nil, err } else { return r, nil } } -func (this *TendermintRoutes) ListNamesResult() (ctypes.ErisDBResult, error) { - if r, err := this.tendermintPipe.ListNames(); err != nil { +func (tmRoutes *TendermintRoutes) ListNamesResult() (ctypes.ErisDBResult, error) { + if r, err := tmRoutes.tendermintPipe.ListNames(); err != nil { return nil, err } else { return r, nil } } -func (this *TendermintRoutes) GenPrivAccountResult() (ctypes.ErisDBResult, error) { - //if r, err := this.tendermintPipe.GenPrivAccount(); err != nil { +func (tmRoutes *TendermintRoutes) GenPrivAccountResult() (ctypes.ErisDBResult, error) { + //if r, err := tmRoutes.tendermintPipe.GenPrivAccount(); err != nil { // return nil, err //} else { // return r, nil @@ -167,8 +178,9 @@ func (this *TendermintRoutes) GenPrivAccountResult() (ctypes.ErisDBResult, error return nil, fmt.Errorf("Unimplemented as poor practice to generate private account over unencrypted RPC") } -func (this *TendermintRoutes) SignTxResult(tx txs.Tx, privAccounts []*acm.PrivAccount) (ctypes.ErisDBResult, error) { - // if r, err := this.tendermintPipe.SignTx(tx, privAccounts); err != nil { +func (tmRoutes *TendermintRoutes) SignTxResult(tx txs.Tx, + privAccounts []*acm.PrivAccount) (ctypes.ErisDBResult, error) { + // if r, err := tmRoutes.tendermintPipe.SignTx(tx, privAccounts); err != nil { // return nil, err // } else { // return r, nil @@ -176,10 +188,22 @@ func (this *TendermintRoutes) SignTxResult(tx txs.Tx, privAccounts []*acm.PrivAc return nil, fmt.Errorf("Unimplemented as poor practice to pass private account over unencrypted RPC") } -func (this *TendermintRoutes) BroadcastTxResult(tx txs.Tx) (ctypes.ErisDBResult, error) { - if r, err := this.tendermintPipe.BroadcastTxSync(tx); err != nil { +func (tmRoutes *TendermintRoutes) BroadcastTxResult(tx txs.Tx) (ctypes.ErisDBResult, error) { + if r, err := tmRoutes.tendermintPipe.BroadcastTxSync(tx); err != nil { return nil, err } else { return r, nil } } + +func (tmRoutes *TendermintRoutes) BlockchainInfo(minHeight, + maxHeight int) (ctypes.ErisDBResult, error) { + r, err := tmRoutes.tendermintPipe.BlockchainInfo(minHeight, maxHeight, + maxBlockLookback) + if err != nil { + return nil, err + } else { + return r, nil + } + +} diff --git a/rpc/tendermint/core/types/responses.go b/rpc/tendermint/core/types/responses.go index 4c48f7ab7a5410ef1dc6cb488218fa54e88895f8..77ab79af0b4a7063c7fb3c108b03cfde3de6d164 100644 --- a/rpc/tendermint/core/types/responses.go +++ b/rpc/tendermint/core/types/responses.go @@ -59,6 +59,12 @@ type ResultStatus struct { LatestBlockTime int64 `json:"latest_block_time"` // nano } +type ResultSubscribe struct { +} + +type ResultUnsubscribe struct { +} + type ResultNetInfo struct { Listening bool `json:"listening"` Listeners []string `json:"listeners"` @@ -145,6 +151,8 @@ const ( ResultTypeGenesis = byte(0x11) ResultTypeSignTx = byte(0x12) ResultTypeEvent = byte(0x13) // so websockets can respond to rpc functions + ResultTypeSubscribe = byte(0x14) + ResultTypeUnsubscribe = byte(0x15) ) type ErisDBResult interface { @@ -173,4 +181,6 @@ var _ = wire.RegisterInterface( wire.ConcreteType{&ResultGenesis{}, ResultTypeGenesis}, wire.ConcreteType{&ResultSignTx{}, ResultTypeSignTx}, wire.ConcreteType{&ResultEvent{}, ResultTypeEvent}, + wire.ConcreteType{&ResultSubscribe{}, ResultTypeSubscribe}, + wire.ConcreteType{&ResultUnsubscribe{}, ResultTypeUnsubscribe}, )