Skip to content

Commit

Permalink
Merge pull request #285 from althea-net/christianborst/oracle-event-e…
Browse files Browse the repository at this point in the history
…ndpoint

Adding oracle attestations endpoint
  • Loading branch information
ChristianBorst authored Aug 30, 2021
2 parents 0b66ebb + 87a431f commit 19a4cfe
Show file tree
Hide file tree
Showing 20 changed files with 955 additions and 162 deletions.
11 changes: 11 additions & 0 deletions module/proto/gravity/v1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import "gravity/v1/types.proto";
import "gravity/v1/msgs.proto";
import "gravity/v1/pool.proto";
import "gravity/v1/batch.proto";
import "gravity/v1/attestation.proto";
import "google/api/annotations.proto";
import "gogoproto/gogo.proto";

Expand Down Expand Up @@ -70,6 +71,9 @@ service Query {
option (google.api.http).get = "/gravity/v1beta/cosmos_originated/denom_to_erc20";
}

rpc GetAttestations(QueryAttestationsRequest) returns (QueryAttestationsResponse) {
option (google.api.http).get = "/gravity/v1beta/query_attestations";
}
rpc GetDelegateKeyByValidator(QueryDelegateKeysByValidatorAddress) returns (QueryDelegateKeysByValidatorAddressResponse) {
option (google.api.http).get = "/gravity/v1beta/query_delegate_keys_by_validator";
}
Expand Down Expand Up @@ -205,6 +209,13 @@ message QueryDenomToERC20Response {
bool cosmos_originated = 2;
}

message QueryAttestationsRequest {
uint64 limit = 1;
}
message QueryAttestationsResponse {
repeated Attestation attestations = 1;
}

message QueryDelegateKeysByValidatorAddress {
string validator_address = 1;
}
Expand Down
16 changes: 14 additions & 2 deletions module/x/gravity/abci.go
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,18 @@ func pruneAttestations(ctx sdk.Context, k keeper.Keeper) {
// Then we sort it
sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] })

// we delete all attestations earlier than the current event nonce
// minus some buffer value. This buffer value is purely to allow
// frontends and other UI components to view recent oracle history
const eventsToKeep = 1000
lastNonce := uint64(k.GetLastObservedEventNonce(ctx))
var cutoff uint64
if lastNonce <= eventsToKeep {
return
} else {
cutoff = lastNonce - eventsToKeep
}

// This iterates over all keys (event nonces) in the attestation mapping. Each value contains
// a slice with one or more attestations at that event nonce. There can be multiple attestations
// at one event nonce when validators disagree about what event happened at that nonce.
Expand All @@ -369,8 +381,8 @@ func pruneAttestations(ctx sdk.Context, k keeper.Keeper) {
// They are ordered by when the first attestation at the event nonce was received.
// This order is not important.
for _, att := range attmap[nonce] {
// we delete all attestations earlier than the current event nonce
if nonce < uint64(k.GetLastObservedEventNonce(ctx)) {
// delete all before the cutoff
if nonce < cutoff {
k.DeleteAttestation(ctx, att)
}
}
Expand Down
34 changes: 33 additions & 1 deletion module/x/gravity/keeper/attestation.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package keeper

import (
"fmt"
"sort"
"strconv"

codectypes "github.com/cosmos/cosmos-sdk/codec/types"
Expand Down Expand Up @@ -162,7 +163,7 @@ func (k Keeper) GetAttestation(ctx sdk.Context, eventNonce uint64, claimHash []b
return &att
}

// DeleteAttestation deletes an attestation given an event nonce and claim
// DeleteAttestation deletes the given attestation
func (k Keeper) DeleteAttestation(ctx sdk.Context, att types.Attestation) {
claim, err := k.UnpackAttestationClaim(&att)
if err != nil {
Expand Down Expand Up @@ -219,6 +220,37 @@ func (k Keeper) IterateAttestaions(ctx sdk.Context, cb func([]byte, types.Attest
}
}

// GetMostRecentAttestations returns sorted (by nonce) attestations up to a provided limit number of attestations
// Note: calls GetAttestationMapping in the hopes that there are potentially many attestations
// which are distributed between few nonces to minimize sorting time
func (k Keeper) GetMostRecentAttestations(ctx sdk.Context, limit uint64) []*types.Attestation {
attestationMapping := k.GetAttestationMapping(ctx)
attestations := make([]*types.Attestation, 0, limit)

keys := make([]uint64, 0, len(attestationMapping))
for k := range attestationMapping {
keys = append(keys, k)
}
sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] })

// Iterate the nonces and collect the attestations
count := 0
for _, nonce := range keys {
if count >= int(limit) {
break
}
for _, att := range attestationMapping[nonce] {
if count >= int(limit) {
break
}
attestations = append(attestations, &att)
count++
}
}

return attestations
}

// GetLastObservedEventNonce returns the latest observed event nonce
func (k Keeper) GetLastObservedEventNonce(ctx sdk.Context) uint64 {
store := ctx.KVStore(k.storeKey)
Expand Down
51 changes: 51 additions & 0 deletions module/x/gravity/keeper/attestation_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package keeper

import (
"testing"

"github.com/althea-net/cosmos-gravity-bridge/module/x/gravity/types"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdktypes "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"
)

// Sets up 10 attestations and checks that they are returned in the correct order
func TestGetMostRecentAttestations(t *testing.T) {
input := CreateTestEnv(t)
k := input.GravityKeeper
ctx := input.Context

lenth := 10
msgs := make([]types.MsgSendToCosmosClaim, 0, lenth)
anys := make([]codectypes.Any, 0, lenth)
for i := 0; i < lenth; i++ {
nonce := uint64(1 + i)
msg := types.MsgSendToCosmosClaim{
EventNonce: nonce,
BlockHeight: 1,
TokenContract: "0x00000000000000000001",
Amount: sdktypes.NewInt(10000000000 + int64(i)),
EthereumSender: "0x00000000000000000002",
CosmosReceiver: "0x00000000000000000003",
Orchestrator: "0x00000000000000000004",
}
msgs = append(msgs, msg)

any, _ := codectypes.NewAnyWithValue(&msg)
anys = append(anys, *any)
att := &types.Attestation{
Observed: false,
Height: uint64(ctx.BlockHeight()),
Claim: any,
}
k.SetAttestation(ctx, nonce, msg.ClaimHash(), att)
}

recentAttestations := k.GetMostRecentAttestations(ctx, uint64(10))
require.True(t, len(recentAttestations) == lenth,
"recentAttestations should have len %v but instead has %v", lenth, len(recentAttestations))
for n, attest := range recentAttestations {
require.Equal(t, attest.Claim.GetCachedValue(), anys[n].GetCachedValue(),
"The %vth claim does not match our message: claim %v\n message %v", n, attest.Claim, msgs[n])
}
}
16 changes: 16 additions & 0 deletions module/x/gravity/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ var _ types.QueryServer = Keeper{
AttestationHandler: nil,
}

const QUERY_ATTESTATIONS_LIMIT uint64 = 1000

// Params queries the params of the gravity module
func (k Keeper) Params(c context.Context, req *types.QueryParamsRequest) (*types.QueryParamsResponse, error) {
var params types.Params
Expand Down Expand Up @@ -270,6 +272,20 @@ func (k Keeper) ERC20ToDenom(
return &ret, nil
}

// GetAttestations queries the attestation map
func (k Keeper) GetAttestations(
c context.Context,
req *types.QueryAttestationsRequest) (*types.QueryAttestationsResponse, error) {
ctx := sdk.UnwrapSDKContext(c)
limit := req.Limit
if limit > QUERY_ATTESTATIONS_LIMIT {
limit = QUERY_ATTESTATIONS_LIMIT
}
attestations := k.GetMostRecentAttestations(ctx, limit)

return &types.QueryAttestationsResponse{Attestations: attestations}, nil
}

func (k Keeper) GetDelegateKeyByValidator(
c context.Context,
req *types.QueryDelegateKeysByValidatorAddress) (*types.QueryDelegateKeysByValidatorAddressResponse, error) {
Expand Down
Loading

0 comments on commit 19a4cfe

Please sign in to comment.