Skip to content

Commit

Permalink
Merge pull request #2889 from oasislabs/andrej/feature/expose-event-h…
Browse files Browse the repository at this point in the history
…ashes-and-genesis-doc

Expose staking event hashes + add GetGenesisDocument to consensus client API
  • Loading branch information
abukosek authored May 6, 2020
2 parents b916601 + 886fc48 commit 5094a27
Show file tree
Hide file tree
Showing 12 changed files with 143 additions and 32 deletions.
5 changes: 5 additions & 0 deletions .changelog/2889.feature.1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
go/staking: Add event hashes

Staking events now have a new `TxHash` field, which contains
the hash of the transaction that caused the event (or the empty
hash in case of block events).
4 changes: 4 additions & 0 deletions .changelog/2889.feature.2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
go/consensus: Add GetGenesisDocument

The consensus client now has a new method to return the original
genesis document.
3 changes: 3 additions & 0 deletions go/consensus/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ type ClientBackend interface {
// WatchBlocks returns a channel that produces a stream of consensus
// blocks as they are being finalized.
WatchBlocks(ctx context.Context) (<-chan *Block, pubsub.ClosableSubscription, error)

// GetGenesisDocument returns the original genesis document.
GetGenesisDocument(ctx context.Context) (*genesis.Document, error)
}

// Block is a consensus block.
Expand Down
33 changes: 33 additions & 0 deletions go/consensus/api/grpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ var (
methodGetBlock = serviceName.NewMethod("GetBlock", int64(0))
// methodGetTransactions is the GetTransactions method.
methodGetTransactions = serviceName.NewMethod("GetTransactions", int64(0))
// methodGetGenesisDocument is the GetGenesisDocument method.
methodGetGenesisDocument = serviceName.NewMethod("GetGenesisDocument", nil)

// methodWatchBlocks is the WatchBlocks method.
methodWatchBlocks = serviceName.NewMethod("WatchBlocks", nil)
Expand Down Expand Up @@ -82,6 +84,10 @@ var (
MethodName: methodGetTransactions.ShortName(),
Handler: handlerGetTransactions,
},
{
MethodName: methodGetGenesisDocument.ShortName(),
Handler: handlerGetGenesisDocument,
},
},
Streams: []grpc.StreamDesc{
{
Expand Down Expand Up @@ -297,6 +303,25 @@ func handlerGetTransactions( // nolint: golint
return interceptor(ctx, height, info, handler)
}

func handlerGetGenesisDocument( // nolint: golint
srv interface{},
ctx context.Context,
dec func(interface{}) error,
interceptor grpc.UnaryServerInterceptor,
) (interface{}, error) {
if interceptor == nil {
return srv.(Backend).GetGenesisDocument(ctx)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: methodGetGenesisDocument.FullName(),
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(Backend).GetGenesisDocument(ctx)
}
return interceptor(ctx, nil, info, handler)
}

func handlerWatchBlocks(srv interface{}, stream grpc.ServerStream) error {
if err := stream.RecvMsg(nil); err != nil {
return err
Expand Down Expand Up @@ -498,6 +523,14 @@ func (c *consensusClient) GetTransactions(ctx context.Context, height int64) ([]
return rsp, nil
}

func (c *consensusClient) GetGenesisDocument(ctx context.Context) (*genesis.Document, error) {
var rsp genesis.Document
if err := c.conn.Invoke(ctx, methodGetGenesisDocument.FullName(), nil, &rsp); err != nil {
return nil, err
}
return &rsp, nil
}

func (c *consensusClient) WatchBlocks(ctx context.Context) (<-chan *Block, pubsub.ClosableSubscription, error) {
ctx, sub := pubsub.NewContextSubscription(ctx)

Expand Down
7 changes: 6 additions & 1 deletion go/consensus/tendermint/epochtime/epochtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,12 @@ func (t *tendermintBackend) updateCached(ctx context.Context, block *tmtypes.Blo
// New constructs a new tendermint backed epochtime Backend instance,
// with the specified epoch interval.
func New(ctx context.Context, service service.TendermintService, interval int64) (api.Backend, error) {
base := service.GetGenesis().EpochTime.Base
genDoc, err := service.GetGenesisDocument(ctx)
if err != nil {
return nil, err
}

base := genDoc.EpochTime.Base
r := &tendermintBackend{
logger: logging.GetLogger("epochtime/tendermint"),
service: service,
Expand Down
7 changes: 6 additions & 1 deletion go/consensus/tendermint/epochtime_mock/epochtime_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,12 @@ func New(ctx context.Context, service service.TendermintService) (api.SetableBac
}
})

if base := service.GetGenesis().EpochTime.Base; base != 0 {
genDoc, err := service.GetGenesisDocument(ctx)
if err != nil {
return nil, err
}

if base := genDoc.EpochTime.Base; base != 0 {
r.logger.Warn("ignoring non-zero base genesis epoch",
"base", base,
)
Expand Down
4 changes: 0 additions & 4 deletions go/consensus/tendermint/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"github.com/oasislabs/oasis-core/go/common/service"
consensus "github.com/oasislabs/oasis-core/go/consensus/api"
"github.com/oasislabs/oasis-core/go/consensus/tendermint/abci"
genesis "github.com/oasislabs/oasis-core/go/genesis/api"
)

// TendermintService provides Tendermint access to Oasis core backends.
Expand All @@ -33,9 +32,6 @@ type TendermintService interface {
// ABCI multiplexer.
SetTransactionAuthHandler(abci.TransactionAuthHandler) error

// GetGenesis will return the oasis genesis document.
GetGenesis() *genesis.Document

// GetHeight returns the Tendermint block height.
GetHeight(ctx context.Context) (int64, error)

Expand Down
84 changes: 72 additions & 12 deletions go/consensus/tendermint/staking/staking.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
tmtypes "github.com/tendermint/tendermint/types"

"github.com/oasislabs/oasis-core/go/common/cbor"
"github.com/oasislabs/oasis-core/go/common/crypto/hash"
"github.com/oasislabs/oasis-core/go/common/crypto/signature"
"github.com/oasislabs/oasis-core/go/common/logging"
"github.com/oasislabs/oasis-core/go/common/pubsub"
Expand All @@ -37,6 +38,14 @@ type tendermintBackend struct {
closedCh chan struct{}
}

// Extend the abci Event struct with the transaction hash if the event was
// the result of a transaction. Block events have Hash set to the empty hash.
type abciEventWithHash struct {
abcitypes.Event

TxHash hash.Hash
}

func (tb *tendermintBackend) TotalSupply(ctx context.Context, height int64) (*quantity.Quantity, error) {
q, err := tb.querier.QueryAt(ctx, height)
if err != nil {
Expand Down Expand Up @@ -142,6 +151,23 @@ func (tb *tendermintBackend) StateToGenesis(ctx context.Context, height int64) (
return q.Genesis(ctx)
}

func convertTmBlockEvents(beginBlockEvents []abcitypes.Event, endBlockEvents []abcitypes.Event) []abciEventWithHash {
var tmEvents []abciEventWithHash
for _, bbe := range beginBlockEvents {
var ev abciEventWithHash
ev.Event = bbe
ev.TxHash.Empty()
tmEvents = append(tmEvents, ev)
}
for _, ebe := range endBlockEvents {
var ev abciEventWithHash
ev.Event = ebe
ev.TxHash.Empty()
tmEvents = append(tmEvents, ev)
}
return tmEvents
}

func (tb *tendermintBackend) GetEvents(ctx context.Context, height int64) ([]api.Event, error) {
// Get block results at given height.
var results *tmrpctypes.ResultBlockResults
Expand All @@ -154,10 +180,33 @@ func (tb *tendermintBackend) GetEvents(ctx context.Context, height int64) ([]api
return nil, err
}

// Get transactions at given height.
txns, err := tb.service.GetTransactions(ctx, height)
if err != nil {
tb.logger.Error("failed to get tendermint transactions",
"err", err,
"height", height,
)
return nil, err
}

// Decode events from block results.
tmEvents := append(results.BeginBlockEvents, results.EndBlockEvents...)
for _, txResults := range results.TxsResults {
tmEvents = append(tmEvents, txResults.Events...)
tmEvents := convertTmBlockEvents(results.BeginBlockEvents, results.EndBlockEvents)
for txIdx, txResults := range results.TxsResults {
// The order of transactions in txns and results.TxsResults is
// supposed to match, so the same index in both slices refers to the
// same transaction.

// Generate hash of transaction.
evHash := hash.NewFromBytes(txns[txIdx])

// Append hash to each event.
for _, tmEv := range txResults.Events {
var ev abciEventWithHash
ev.Event = tmEv
ev.TxHash = evHash
tmEvents = append(tmEvents, ev)
}
}
return tb.onABCIEvents(ctx, tmEvents, height, false)
}
Expand Down Expand Up @@ -210,24 +259,35 @@ func (tb *tendermintBackend) worker(ctx context.Context) {
}

func (tb *tendermintBackend) onEventDataNewBlock(ctx context.Context, ev tmtypes.EventDataNewBlock) {
events := append([]abcitypes.Event{}, ev.ResultBeginBlock.GetEvents()...)
events = append(events, ev.ResultEndBlock.GetEvents()...)
events := convertTmBlockEvents(ev.ResultBeginBlock.GetEvents(), ev.ResultEndBlock.GetEvents())

_, _ = tb.onABCIEvents(ctx, events, ev.Block.Header.Height, true)
}

func (tb *tendermintBackend) onEventDataTx(ctx context.Context, tx tmtypes.EventDataTx) {
_, _ = tb.onABCIEvents(ctx, tx.Result.Events, tx.Height, true)
evHash := hash.NewFromBytes(tx.Tx)

var events []abciEventWithHash
for _, tmEv := range tx.Result.Events {
var ev abciEventWithHash
ev.Event = tmEv
ev.TxHash = evHash
events = append(events, ev)
}

_, _ = tb.onABCIEvents(ctx, events, tx.Height, true)
}

func (tb *tendermintBackend) onABCIEvents(context context.Context, tmEvents []abcitypes.Event, height int64, doBroadcast bool) ([]api.Event, error) {
func (tb *tendermintBackend) onABCIEvents(context context.Context, tmEvents []abciEventWithHash, height int64, doBroadcast bool) ([]api.Event, error) {
var events []api.Event
for _, tmEv := range tmEvents {
// Ignore events that don't relate to the staking app.
if tmEv.GetType() != app.EventType {
continue
}

eh := tmEv.TxHash

for _, pair := range tmEv.GetAttributes() {
key := pair.GetKey()
val := pair.GetValue()
Expand All @@ -250,7 +310,7 @@ func (tb *tendermintBackend) onABCIEvents(context context.Context, tmEvents []ab
if doBroadcast {
tb.escrowNotifier.Broadcast(ee)
} else {
events = append(events, api.Event{EscrowEvent: ee})
events = append(events, api.Event{TxHash: eh, EscrowEvent: ee})
}
} else if bytes.Equal(key, app.KeyTransfer) {
// Transfer event.
Expand All @@ -269,7 +329,7 @@ func (tb *tendermintBackend) onABCIEvents(context context.Context, tmEvents []ab
if doBroadcast {
tb.transferNotifier.Broadcast(&e)
} else {
events = append(events, api.Event{TransferEvent: &e})
events = append(events, api.Event{TxHash: eh, TransferEvent: &e})
}
} else if bytes.Equal(key, app.KeyReclaimEscrow) {
// Reclaim escrow event.
Expand All @@ -290,7 +350,7 @@ func (tb *tendermintBackend) onABCIEvents(context context.Context, tmEvents []ab
if doBroadcast {
tb.escrowNotifier.Broadcast(ee)
} else {
events = append(events, api.Event{EscrowEvent: ee})
events = append(events, api.Event{TxHash: eh, EscrowEvent: ee})
}
} else if bytes.Equal(key, app.KeyAddEscrow) {
// Add escrow event.
Expand All @@ -311,7 +371,7 @@ func (tb *tendermintBackend) onABCIEvents(context context.Context, tmEvents []ab
if doBroadcast {
tb.escrowNotifier.Broadcast(ee)
} else {
events = append(events, api.Event{EscrowEvent: ee})
events = append(events, api.Event{TxHash: eh, EscrowEvent: ee})
}
} else if bytes.Equal(key, app.KeyBurn) {
// Burn event.
Expand All @@ -330,7 +390,7 @@ func (tb *tendermintBackend) onABCIEvents(context context.Context, tmEvents []ab
if doBroadcast {
tb.burnNotifier.Broadcast(&e)
} else {
events = append(events, api.Event{BurnEvent: &e})
events = append(events, api.Event{TxHash: eh, BurnEvent: &e})
}
}
}
Expand Down
20 changes: 6 additions & 14 deletions go/consensus/tendermint/tendermint.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ import (
tmstaking "github.com/oasislabs/oasis-core/go/consensus/tendermint/staking"
epochtimeAPI "github.com/oasislabs/oasis-core/go/epochtime/api"
genesisAPI "github.com/oasislabs/oasis-core/go/genesis/api"
"github.com/oasislabs/oasis-core/go/genesis/file"
keymanagerAPI "github.com/oasislabs/oasis-core/go/keymanager/api"
cmbackground "github.com/oasislabs/oasis-core/go/oasis-node/cmd/common/background"
cmflags "github.com/oasislabs/oasis-core/go/oasis-node/cmd/common/flags"
Expand Down Expand Up @@ -361,16 +360,9 @@ func (t *tendermintService) StateToGenesis(ctx context.Context, blockHeight int6
blockHeight = blk.Header.Height

// Get initial genesis doc.
genesisFileProvider, err := file.DefaultFileProvider()
genesisDoc, err := t.GetGenesisDocument(ctx)
if err != nil {
t.Logger.Error("failed getting genesis file provider",
"err", err,
)
return nil, err
}
genesisDoc, err := genesisFileProvider.GetGenesisDocument()
if err != nil {
t.Logger.Error("failed getting genesis document from file provider",
t.Logger.Error("failed getting genesis document",
"err", err,
)
return nil, err
Expand Down Expand Up @@ -449,6 +441,10 @@ func (t *tendermintService) StateToGenesis(ctx context.Context, blockHeight int6
}, nil
}

func (t *tendermintService) GetGenesisDocument(ctx context.Context) (*genesisAPI.Document, error) {
return t.genesis, nil
}

func (t *tendermintService) RegisterHaltHook(hook func(context.Context, int64, epochtimeAPI.EpochTime)) {
if !t.initialized() {
return
Expand Down Expand Up @@ -618,10 +614,6 @@ func (t *tendermintService) SetTransactionAuthHandler(handler abci.TransactionAu
return t.mux.SetTransactionAuthHandler(handler)
}

func (t *tendermintService) GetGenesis() *genesisAPI.Document {
return t.genesis
}

func (t *tendermintService) TransactionAuthHandler() consensusAPI.TransactionAuthHandler {
return t.mux.TransactionAuthHandler()
}
Expand Down
4 changes: 4 additions & 0 deletions go/consensus/tests/tester.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ func ConsensusImplementationTests(t *testing.T, backend consensus.ClientBackend)
ctx, cancel := context.WithTimeout(context.Background(), recvTimeout)
defer cancel()

genDoc, err := backend.GetGenesisDocument(ctx)
require.NoError(err, "GetGenesisDocument")
require.NotNil(genDoc, "returned genesis document should not be nil")

blk, err := backend.GetBlock(ctx, consensus.HeightLatest)
require.NoError(err, "GetBlock")
require.NotNil(blk, "returned block should not be nil")
Expand Down
3 changes: 3 additions & 0 deletions go/staking/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"context"
"fmt"

"github.com/oasislabs/oasis-core/go/common/crypto/hash"
"github.com/oasislabs/oasis-core/go/common/crypto/signature"
"github.com/oasislabs/oasis-core/go/common/errors"
"github.com/oasislabs/oasis-core/go/common/pubsub"
Expand Down Expand Up @@ -156,6 +157,8 @@ type EscrowEvent struct {

// Event signifies a staking event, returned via GetEvents.
type Event struct {
TxHash hash.Hash `json:"tx_hash,omitempty"`

TransferEvent *TransferEvent `json:"transfer,omitempty"`
BurnEvent *BurnEvent `json:"burn,omitempty"`
EscrowEvent *EscrowEvent `json:"escrow,omitempty"`
Expand Down
1 change: 1 addition & 0 deletions go/staking/tests/tester.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ func testTransfer(t *testing.T, state *stakingTestsState, backend api.Backend, c
if evt.TransferEvent != nil {
if evt.TransferEvent.From.Equal(ev.From) && evt.TransferEvent.To.Equal(ev.To) && evt.TransferEvent.Tokens.Cmp(&ev.Tokens) == 0 {
gotIt = true
require.True(!evt.TxHash.IsEmpty(), "GetEvents should return valid txn hash")
break
}
}
Expand Down

0 comments on commit 5094a27

Please sign in to comment.