diff --git a/module/proto/gravity/v1/attestation.proto b/module/proto/gravity/v1/attestation.proto index 803586d9b..63419cde4 100644 --- a/module/proto/gravity/v1/attestation.proto +++ b/module/proto/gravity/v1/attestation.proto @@ -18,6 +18,7 @@ enum ClaimType { CLAIM_TYPE_WITHDRAW = 2; CLAIM_TYPE_ERC20_DEPLOYED = 3; CLAIM_TYPE_LOGIC_CALL_EXECUTED = 4; + CLAIM_TYPE_VALSET_UPDATED = 5; } // Attestation is an aggregate of `claims` that eventually becomes `observed` by diff --git a/module/proto/gravity/v1/msgs.proto b/module/proto/gravity/v1/msgs.proto index db28de400..4cf113e05 100644 --- a/module/proto/gravity/v1/msgs.proto +++ b/module/proto/gravity/v1/msgs.proto @@ -3,6 +3,7 @@ package gravity.v1; import "cosmos/base/v1beta1/coin.proto"; import "gogoproto/gogo.proto"; import "google/api/annotations.proto"; +import "gravity/v1/types.proto"; option go_package = "github.com/cosmos/gravity-bridge/module/x/gravity/types"; // Msg defines the state transitions possible within gravity @@ -28,6 +29,9 @@ service Msg { rpc WithdrawClaim(MsgWithdrawClaim) returns (MsgWithdrawClaimResponse) { option (google.api.http).post = "/gravity/v1/withdraw_claim"; } + rpc ValsetUpdateClaim(MsgValsetUpdatedClaim) returns (MsgValsetUpdatedClaimResponse) { + option (google.api.http).post = "/gravity/v1/valset_updated_claim"; + } rpc ERC20DeployedClaim(MsgERC20DeployedClaim) returns (MsgERC20DeployedClaimResponse) { option (google.api.http).post = "/gravity/v1/erc20_deployed_claim"; @@ -226,6 +230,18 @@ message MsgLogicCallExecutedClaim { message MsgLogicCallExecutedClaimResponse {} +// This informs the Cosmos module that a validator +// set has been updated. +message MsgValsetUpdatedClaim { + uint64 event_nonce = 1; + uint64 valset_nonce = 2; + uint64 block_height = 3; + repeated BridgeValidator members = 4; + string orchestrator = 6; +} + +message MsgValsetUpdatedClaimResponse {} + // This call allows the sender (and only the sender) // to cancel a given MsgSendToEth and recieve a refund // of the tokens diff --git a/module/x/gravity/abci.go b/module/x/gravity/abci.go index 59b7e8f44..bf0b8e181 100644 --- a/module/x/gravity/abci.go +++ b/module/x/gravity/abci.go @@ -10,16 +10,20 @@ import ( // EndBlocker is called at the end of every block func EndBlocker(ctx sdk.Context, k keeper.Keeper) { + params := k.GetParams(ctx) // Question: what here can be epoched? slashing(ctx, k) attestationTally(ctx, k) cleanupTimedOutBatches(ctx, k) cleanupTimedOutLogicCalls(ctx, k) createValsets(ctx, k) + pruneValsets(ctx, k, params) + // TODO: prune claims, attestations when they pass in the handler } func createValsets(ctx sdk.Context, k keeper.Keeper) { // Auto ValsetRequest Creation. + // WARNING: do not use k.GetLastObservedValset in this function, it *will* result in losing control of the bridge // 1. If there are no valset requests, create a new one. // 2. If there is at least one validator who started unbonding in current block. (we persist last unbonded block height in hooks.go) // This will make sure the unbonding validator has to provide an attestation to a new Valset @@ -33,18 +37,36 @@ func createValsets(ctx sdk.Context, k keeper.Keeper) { } } +func pruneValsets(ctx sdk.Context, k keeper.Keeper, params types.Params) { + // Validator set pruning + // prune all validator sets with a nonce less than the + // last observed nonce, they can't be submitted any longer + // + // Only prune valsets after the signed valsets window has passed + // so that slashing can occur the block before we remove them + lastObserved := k.GetLastObservedValset(ctx) + currentBlock := uint64(ctx.BlockHeight()) + tooEarly := currentBlock < params.SignedValsetsWindow + if lastObserved != nil && !tooEarly { + earliestToPrune := currentBlock - params.SignedValsetsWindow + sets := k.GetValsets(ctx) + for _, set := range sets { + if set.Nonce < lastObserved.Nonce && set.Height < earliestToPrune { + k.DeleteValset(ctx, set.Nonce) + } + } + } +} + func slashing(ctx sdk.Context, k keeper.Keeper) { params := k.GetParams(ctx) - // Slash validator for not confirming valset requests, batch requests and not attesting claims rightfully + // Slash validator for not confirming valset requests, batch requests ValsetSlashing(ctx, k, params) BatchSlashing(ctx, k, params) - // TODO slashing for arbitrary logic is missing + // TODO slashing for arbitrary logic signatures is missing - // TODO: prune validator sets, older than 6 months, this time is chosen out of an abundance of caution - // TODO: prune outgoing tx batches while looping over them above, older than 15h and confirmed - // TODO: prune claims, attestations } // Iterate over all attestations currently being voted on in order of nonce and diff --git a/module/x/gravity/handler.go b/module/x/gravity/handler.go index 20591e28e..0635146e8 100644 --- a/module/x/gravity/handler.go +++ b/module/x/gravity/handler.go @@ -47,6 +47,9 @@ func NewHandler(k keeper.Keeper) sdk.Handler { case *types.MsgLogicCallExecutedClaim: res, err := msgServer.LogicCallExecutedClaim(sdk.WrapSDKContext(ctx), msg) return sdk.WrapServiceResult(ctx, res, err) + case *types.MsgValsetUpdatedClaim: + res, err := msgServer.ValsetUpdateClaim(sdk.WrapSDKContext(ctx), msg) + return sdk.WrapServiceResult(ctx, res, err) default: return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, fmt.Sprintf("Unrecognized Gravity Msg type: %v", msg.Type())) diff --git a/module/x/gravity/keeper/attestation.go b/module/x/gravity/keeper/attestation.go index 1f9e78430..695fd274f 100644 --- a/module/x/gravity/keeper/attestation.go +++ b/module/x/gravity/keeper/attestation.go @@ -237,6 +237,28 @@ func (k Keeper) SetLastObservedEthereumBlockHeight(ctx sdk.Context, ethereumHeig store.Set(types.LastObservedEthereumBlockHeightKey, k.cdc.MustMarshalBinaryBare(&height)) } +// GetLastObservedValset retrieves the last observed validator set from the store +// WARNING: This value is not an up to date validator set on Ethereum, it is a validator set +// that AT ONE POINT was the one in the Gravity bridge on Ethereum. If you assume that it's up +// to date you may break the bridge +func (k Keeper) GetLastObservedValset(ctx sdk.Context) *types.Valset { + store := ctx.KVStore(k.storeKey) + bytes := store.Get(types.LastObservedValsetKey) + + if len(bytes) == 0 { + return nil + } + valset := types.Valset{} + k.cdc.MustUnmarshalBinaryBare(bytes, &valset) + return &valset +} + +// SetLastObservedValset updates the last observed validator set in the store +func (k Keeper) SetLastObservedValset(ctx sdk.Context, valset types.Valset) { + store := ctx.KVStore(k.storeKey) + store.Set(types.LastObservedValsetKey, k.cdc.MustMarshalBinaryBare(&valset)) +} + // setLastObservedEventNonce sets the latest observed event nonce func (k Keeper) setLastObservedEventNonce(ctx sdk.Context, nonce uint64) { store := ctx.KVStore(k.storeKey) diff --git a/module/x/gravity/keeper/attestation_handler.go b/module/x/gravity/keeper/attestation_handler.go index 5ce06c0a3..8b274e17f 100644 --- a/module/x/gravity/keeper/attestation_handler.go +++ b/module/x/gravity/keeper/attestation_handler.go @@ -111,6 +111,14 @@ func (a AttestationHandler) Handle(ctx sdk.Context, att types.Attestation, claim // Add to denom-erc20 mapping a.keeper.setCosmosOriginatedDenomToERC20(ctx, claim.CosmosDenom, claim.TokenContract) + case *types.MsgValsetUpdatedClaim: + // TODO here we should check the contents of the validator set against + // the store, if they differ we should take some action to indicate to the + // user that bridge highjacking has occurred + a.keeper.SetLastObservedValset(ctx, types.Valset{ + Nonce: claim.ValsetNonce, + Members: claim.Members, + }) default: return sdkerrors.Wrapf(types.ErrInvalid, "event type: %s", claim.GetType()) diff --git a/module/x/gravity/keeper/msg_server.go b/module/x/gravity/keeper/msg_server.go index f50611195..b59cb063f 100644 --- a/module/x/gravity/keeper/msg_server.go +++ b/module/x/gravity/keeper/msg_server.go @@ -439,6 +439,46 @@ func (k msgServer) LogicCallExecutedClaim(c context.Context, msg *types.MsgLogic return &types.MsgLogicCallExecutedClaimResponse{}, nil } +// ValsetUpdatedClaim handles claims for executing a validator set update on Ethereum +func (k msgServer) ValsetUpdateClaim(c context.Context, msg *types.MsgValsetUpdatedClaim) (*types.MsgValsetUpdatedClaimResponse, error) { + ctx := sdk.UnwrapSDKContext(c) + + orchaddr, _ := sdk.AccAddressFromBech32(msg.Orchestrator) + validator := k.GetOrchestratorValidator(ctx, orchaddr) + if validator == nil { + return nil, sdkerrors.Wrap(types.ErrUnknown, "validator") + } + + // return an error if the validator isn't in the active set + val := k.StakingKeeper.Validator(ctx, validator) + if val == nil || !val.IsBonded() { + return nil, sdkerrors.Wrap(sdkerrors.ErrorInvalidSigner, "validator not in acitve set") + } + + any, err := codectypes.NewAnyWithValue(msg) + if err != nil { + return nil, err + } + + // Add the claim to the store + _, err = k.Attest(ctx, msg, any) + if err != nil { + return nil, sdkerrors.Wrap(err, "create attestation") + } + + // Emit the handle message event + ctx.EventManager().EmitEvent( + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, msg.Type()), + // TODO: maybe return something better here? is this the right string representation? + sdk.NewAttribute(types.AttributeKeyAttestationID, string(types.GetAttestationKey(msg.EventNonce, msg.ClaimHash()))), + ), + ) + + return &types.MsgValsetUpdatedClaimResponse{}, nil +} + func (k msgServer) CancelSendToEth(c context.Context, msg *types.MsgCancelSendToEth) (*types.MsgCancelSendToEthResponse, error) { ctx := sdk.UnwrapSDKContext(c) sender, err := sdk.AccAddressFromBech32(msg.Sender) diff --git a/module/x/gravity/types/attestation.pb.go b/module/x/gravity/types/attestation.pb.go index 64cdb29f3..d3b3edf10 100644 --- a/module/x/gravity/types/attestation.pb.go +++ b/module/x/gravity/types/attestation.pb.go @@ -35,6 +35,7 @@ const ( CLAIM_TYPE_WITHDRAW ClaimType = 2 CLAIM_TYPE_ERC20_DEPLOYED ClaimType = 3 CLAIM_TYPE_LOGIC_CALL_EXECUTED ClaimType = 4 + CLAIM_TYPE_VALSET_UPDATED ClaimType = 5 ) var ClaimType_name = map[int32]string{ @@ -43,6 +44,7 @@ var ClaimType_name = map[int32]string{ 2: "CLAIM_TYPE_WITHDRAW", 3: "CLAIM_TYPE_ERC20_DEPLOYED", 4: "CLAIM_TYPE_LOGIC_CALL_EXECUTED", + 5: "CLAIM_TYPE_VALSET_UPDATED", } var ClaimType_value = map[string]int32{ @@ -51,6 +53,7 @@ var ClaimType_value = map[string]int32{ "CLAIM_TYPE_WITHDRAW": 2, "CLAIM_TYPE_ERC20_DEPLOYED": 3, "CLAIM_TYPE_LOGIC_CALL_EXECUTED": 4, + "CLAIM_TYPE_VALSET_UPDATED": 5, } func (x ClaimType) String() string { @@ -203,35 +206,36 @@ func init() { func init() { proto.RegisterFile("gravity/v1/attestation.proto", fileDescriptor_e3205613bbab7525) } var fileDescriptor_e3205613bbab7525 = []byte{ - // 448 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x92, 0x31, 0x6f, 0xd3, 0x40, - 0x14, 0xc7, 0x7d, 0x49, 0x1a, 0x35, 0xd7, 0xc5, 0x3a, 0xa2, 0xe0, 0x5a, 0xe0, 0x46, 0x19, 0x50, - 0x54, 0xa9, 0x3e, 0x5a, 0x06, 0x66, 0xd7, 0xbe, 0x82, 0x25, 0x43, 0x82, 0xeb, 0xaa, 0x94, 0x25, - 0xb2, 0x9d, 0xe3, 0x62, 0x35, 0xf6, 0x45, 0xf6, 0xc5, 0xc2, 0x33, 0x0b, 0x23, 0x5f, 0x01, 0xf1, - 0x65, 0x3a, 0x76, 0x44, 0x0c, 0x15, 0x4a, 0xbe, 0x08, 0x8a, 0xed, 0x86, 0x48, 0x4c, 0xf6, 0xff, - 0xff, 0x7b, 0xef, 0xee, 0xff, 0x9e, 0x0e, 0x3e, 0x63, 0xa9, 0x9f, 0x47, 0xa2, 0xc0, 0xf9, 0x29, - 0xf6, 0x85, 0xa0, 0x99, 0xf0, 0x45, 0xc4, 0x13, 0x7d, 0x91, 0x72, 0xc1, 0x11, 0xac, 0xa9, 0x9e, - 0x9f, 0xaa, 0x5d, 0xc6, 0x19, 0x2f, 0x6d, 0xbc, 0xf9, 0xab, 0x2a, 0xd4, 0x43, 0xc6, 0x39, 0x9b, - 0x53, 0x5c, 0xaa, 0x60, 0xf9, 0x19, 0xfb, 0x49, 0x51, 0xa1, 0xc1, 0x57, 0x00, 0x0f, 0x8c, 0x7f, - 0x47, 0x22, 0x15, 0xee, 0xf3, 0x20, 0xa3, 0x69, 0x4e, 0xa7, 0x0a, 0xe8, 0x83, 0xe1, 0xbe, 0xbb, - 0xd5, 0xa8, 0x0b, 0xf7, 0x72, 0x2e, 0x68, 0xa6, 0x34, 0xfa, 0xcd, 0x61, 0xc7, 0xad, 0x04, 0xea, - 0xc1, 0xf6, 0x8c, 0x46, 0x6c, 0x26, 0x94, 0x66, 0x1f, 0x0c, 0x5b, 0x6e, 0xad, 0xd0, 0x31, 0xdc, - 0x0b, 0xe7, 0x7e, 0x14, 0x2b, 0xad, 0x3e, 0x18, 0x1e, 0x9c, 0x75, 0xf5, 0x2a, 0x84, 0xfe, 0x18, - 0x42, 0x37, 0x92, 0xc2, 0xad, 0x4a, 0x06, 0x0b, 0x08, 0x89, 0x6b, 0x9e, 0xbd, 0xf4, 0xf8, 0x2d, - 0x2d, 0x33, 0x84, 0x3c, 0x11, 0xa9, 0x1f, 0x8a, 0x32, 0x43, 0xc7, 0xdd, 0x6a, 0x74, 0x01, 0xdb, - 0x7e, 0xcc, 0x97, 0x89, 0x50, 0x1a, 0x1b, 0x72, 0xae, 0xdf, 0x3d, 0x1c, 0x49, 0xbf, 0x1f, 0x8e, - 0x5e, 0xb0, 0x48, 0xcc, 0x96, 0x81, 0x1e, 0xf2, 0x18, 0x87, 0x3c, 0x8b, 0x79, 0x56, 0x7f, 0x4e, - 0xb2, 0xe9, 0x2d, 0x16, 0xc5, 0x82, 0x66, 0xba, 0x9d, 0x08, 0xb7, 0xee, 0x3e, 0xfe, 0x01, 0x60, - 0xc7, 0xdc, 0xdc, 0xed, 0x15, 0x0b, 0x8a, 0x54, 0xd8, 0x33, 0x1d, 0xc3, 0x7e, 0x37, 0xf1, 0x6e, - 0xc6, 0x64, 0x72, 0xf5, 0xfe, 0x72, 0x4c, 0x4c, 0xfb, 0xc2, 0x26, 0x96, 0x2c, 0xa1, 0x1e, 0x44, - 0x3b, 0xcc, 0x22, 0xe3, 0xd1, 0xa5, 0xed, 0xc9, 0x00, 0x3d, 0x85, 0x4f, 0x76, 0xfc, 0x6b, 0xdb, - 0x7b, 0x6b, 0xb9, 0xc6, 0xb5, 0xdc, 0x40, 0xcf, 0xe1, 0xe1, 0x0e, 0x28, 0xe7, 0xda, 0xb4, 0x39, - 0xa3, 0x1b, 0x62, 0xc9, 0x4d, 0x34, 0x80, 0xda, 0x0e, 0x76, 0x46, 0x6f, 0x6c, 0x73, 0x62, 0x1a, - 0x8e, 0x33, 0x21, 0x1f, 0x89, 0x79, 0xe5, 0x11, 0x4b, 0x6e, 0xa9, 0xad, 0x6f, 0x3f, 0x35, 0xe9, - 0xfc, 0xc3, 0xdd, 0x4a, 0x03, 0xf7, 0x2b, 0x0d, 0xfc, 0x59, 0x69, 0xe0, 0xfb, 0x5a, 0x93, 0xee, - 0xd7, 0x9a, 0xf4, 0x6b, 0xad, 0x49, 0x9f, 0x5e, 0xff, 0x3f, 0x6d, 0xfd, 0x08, 0x4e, 0x82, 0x34, - 0x9a, 0x32, 0x8a, 0x63, 0x3e, 0x5d, 0xce, 0x29, 0xfe, 0xf2, 0xe8, 0x57, 0x2b, 0x08, 0xda, 0xe5, - 0xf6, 0x5f, 0xfd, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xc0, 0x5a, 0x62, 0x07, 0x52, 0x02, 0x00, 0x00, + // 462 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x92, 0xbf, 0x6f, 0xd3, 0x40, + 0x14, 0xc7, 0x7d, 0xf9, 0xa5, 0xe6, 0xba, 0x58, 0x47, 0x14, 0x5c, 0x0b, 0xdc, 0x28, 0x03, 0x8a, + 0x2a, 0xd5, 0xa6, 0x65, 0x60, 0x76, 0xed, 0x2b, 0x58, 0x32, 0x24, 0x38, 0x0e, 0xa5, 0x2c, 0x96, + 0xed, 0x1c, 0x8e, 0xd5, 0xd8, 0x17, 0xd9, 0x17, 0x0b, 0xcf, 0x2c, 0x8c, 0xfc, 0x0f, 0xfc, 0x23, + 0x8c, 0x1d, 0x3b, 0x22, 0x86, 0x0a, 0x25, 0xff, 0x08, 0xf2, 0x8f, 0x86, 0x08, 0xa6, 0xbb, 0xef, + 0xfb, 0xbc, 0xf7, 0xee, 0x7b, 0xef, 0x0e, 0x3e, 0x09, 0x12, 0x37, 0x0b, 0x59, 0xae, 0x64, 0x67, + 0x8a, 0xcb, 0x18, 0x49, 0x99, 0xcb, 0x42, 0x1a, 0xcb, 0xab, 0x84, 0x32, 0x8a, 0x60, 0x4d, 0xe5, + 0xec, 0x4c, 0xec, 0x05, 0x34, 0xa0, 0x65, 0x58, 0x29, 0x76, 0x55, 0x86, 0x78, 0x14, 0x50, 0x1a, + 0x2c, 0x89, 0x52, 0x2a, 0x6f, 0xfd, 0x49, 0x71, 0xe3, 0xbc, 0x42, 0xc3, 0x2f, 0x00, 0x1e, 0xaa, + 0x7f, 0x5b, 0x22, 0x11, 0x1e, 0x50, 0x2f, 0x25, 0x49, 0x46, 0xe6, 0x02, 0x18, 0x80, 0xd1, 0x81, + 0xb5, 0xd3, 0xa8, 0x07, 0xdb, 0x19, 0x65, 0x24, 0x15, 0x1a, 0x83, 0xe6, 0xa8, 0x6b, 0x55, 0x02, + 0xf5, 0x61, 0x67, 0x41, 0xc2, 0x60, 0xc1, 0x84, 0xe6, 0x00, 0x8c, 0x5a, 0x56, 0xad, 0xd0, 0x09, + 0x6c, 0xfb, 0x4b, 0x37, 0x8c, 0x84, 0xd6, 0x00, 0x8c, 0x0e, 0xcf, 0x7b, 0x72, 0x65, 0x42, 0x7e, + 0x30, 0x21, 0xab, 0x71, 0x6e, 0x55, 0x29, 0xc3, 0x15, 0x84, 0xd8, 0xd2, 0xce, 0x9f, 0xdb, 0xf4, + 0x86, 0x94, 0x1e, 0x7c, 0x1a, 0xb3, 0xc4, 0xf5, 0x59, 0xe9, 0xa1, 0x6b, 0xed, 0x34, 0xba, 0x84, + 0x1d, 0x37, 0xa2, 0xeb, 0x98, 0x09, 0x8d, 0x82, 0x5c, 0xc8, 0xb7, 0xf7, 0xc7, 0xdc, 0xaf, 0xfb, + 0xe3, 0x67, 0x41, 0xc8, 0x16, 0x6b, 0x4f, 0xf6, 0x69, 0xa4, 0xf8, 0x34, 0x8d, 0x68, 0x5a, 0x2f, + 0xa7, 0xe9, 0xfc, 0x46, 0x61, 0xf9, 0x8a, 0xa4, 0xb2, 0x11, 0x33, 0xab, 0xae, 0x3e, 0xf9, 0x01, + 0x60, 0x57, 0x2b, 0xce, 0xb6, 0xf3, 0x15, 0x41, 0x22, 0xec, 0x6b, 0xa6, 0x6a, 0xbc, 0x71, 0xec, + 0xeb, 0x09, 0x76, 0x66, 0x6f, 0xa7, 0x13, 0xac, 0x19, 0x97, 0x06, 0xd6, 0x79, 0x0e, 0xf5, 0x21, + 0xda, 0x63, 0x3a, 0x9e, 0x8c, 0xa7, 0x86, 0xcd, 0x03, 0xf4, 0x18, 0x3e, 0xda, 0x8b, 0x5f, 0x19, + 0xf6, 0x6b, 0xdd, 0x52, 0xaf, 0xf8, 0x06, 0x7a, 0x0a, 0x8f, 0xf6, 0x40, 0x79, 0xaf, 0xa2, 0xcc, + 0x1c, 0x5f, 0x63, 0x9d, 0x6f, 0xa2, 0x21, 0x94, 0xf6, 0xb0, 0x39, 0x7e, 0x65, 0x68, 0x8e, 0xa6, + 0x9a, 0xa6, 0x83, 0x3f, 0x60, 0x6d, 0x66, 0x63, 0x9d, 0x6f, 0xfd, 0xd3, 0xe2, 0xbd, 0x6a, 0x4e, + 0xb1, 0xed, 0xcc, 0x26, 0xba, 0x5a, 0xe0, 0xb6, 0xd8, 0xfa, 0xfa, 0x5d, 0xe2, 0x2e, 0xde, 0xdd, + 0x6e, 0x24, 0x70, 0xb7, 0x91, 0xc0, 0xef, 0x8d, 0x04, 0xbe, 0x6d, 0x25, 0xee, 0x6e, 0x2b, 0x71, + 0x3f, 0xb7, 0x12, 0xf7, 0xf1, 0xe5, 0xff, 0xc3, 0xa8, 0xff, 0xc8, 0xa9, 0x97, 0x84, 0xf3, 0x80, + 0x28, 0x11, 0x9d, 0xaf, 0x97, 0x44, 0xf9, 0xfc, 0x10, 0xaf, 0x26, 0xe4, 0x75, 0xca, 0xc7, 0x79, + 0xf1, 0x27, 0x00, 0x00, 0xff, 0xff, 0xa3, 0x60, 0x0d, 0x45, 0x71, 0x02, 0x00, 0x00, } func (m *Attestation) Marshal() (dAtA []byte, err error) { diff --git a/module/x/gravity/types/codec.go b/module/x/gravity/types/codec.go index 375f2c159..d82d08d44 100644 --- a/module/x/gravity/types/codec.go +++ b/module/x/gravity/types/codec.go @@ -27,6 +27,7 @@ func RegisterInterfaces(registry types.InterfaceRegistry) { &MsgERC20DeployedClaim{}, &MsgSetOrchestratorAddress{}, &MsgLogicCallExecutedClaim{}, + &MsgValsetUpdatedClaim{}, &MsgCancelSendToEth{}, ) @@ -37,6 +38,7 @@ func RegisterInterfaces(registry types.InterfaceRegistry) { &MsgWithdrawClaim{}, &MsgERC20DeployedClaim{}, &MsgLogicCallExecutedClaim{}, + &MsgValsetUpdatedClaim{}, ) msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) @@ -56,6 +58,7 @@ func RegisterCodec(cdc *codec.LegacyAmino) { cdc.RegisterConcrete(&MsgWithdrawClaim{}, "gravity/MsgWithdrawClaim", nil) cdc.RegisterConcrete(&MsgERC20DeployedClaim{}, "gravity/MsgERC20DeployedClaim", nil) cdc.RegisterConcrete(&MsgLogicCallExecutedClaim{}, "gravity/MsgLogicCallExecutedClaim", nil) + cdc.RegisterConcrete(&MsgValsetUpdatedClaim{}, "gravity/MsgValsetUpdatedClaim", nil) cdc.RegisterConcrete(&OutgoingTxBatch{}, "gravity/OutgoingTxBatch", nil) cdc.RegisterConcrete(&MsgCancelSendToEth{}, "gravity/MsgCancelSendToEth", nil) cdc.RegisterConcrete(&OutgoingTransferTx{}, "gravity/OutgoingTransferTx", nil) diff --git a/module/x/gravity/types/key.go b/module/x/gravity/types/key.go index 6b947a9b0..c884db91c 100644 --- a/module/x/gravity/types/key.go +++ b/module/x/gravity/types/key.go @@ -91,9 +91,6 @@ var ( // KeyOutgoingLogicConfirm indexes the outgoing logic confirms KeyOutgoingLogicConfirm = []byte{0xae} - // LastObservedEthereumBlockHeightKey indexes the latest Ethereum block height - LastObservedEthereumBlockHeightKey = []byte{0xf9} - // DenomToERC20Key prefixes the index of Cosmos originated asset denoms to ERC20s DenomToERC20Key = []byte{0xf3} @@ -111,6 +108,15 @@ var ( // LastUnBondingBlockHeight indexes the last validator unbonding block height LastUnBondingBlockHeight = []byte{0xf8} + + // LastObservedEthereumBlockHeightKey indexes the latest Ethereum block height + LastObservedEthereumBlockHeightKey = []byte{0xf9} + + // LastObservedValsetNonceKey indexes the latest observed valset nonce + // HERE THERE BE DRAGONS, do not use this value as an up to date validator set + // on Ethereum it will always lag significantly and may be totally wrong at some + // times. + LastObservedValsetKey = []byte{0xfa} ) // GetOrchestratorAddressKey returns the following key format diff --git a/module/x/gravity/types/msgs.go b/module/x/gravity/types/msgs.go index 7bd4f4424..c5d2bd317 100644 --- a/module/x/gravity/types/msgs.go +++ b/module/x/gravity/types/msgs.go @@ -558,7 +558,63 @@ func (b *MsgLogicCallExecutedClaim) ClaimHash() []byte { return tmhash.Sum([]byte(path)) } -// NewMsgSetOrchestratorAddress returns a new msgSetOrchestratorAddress +// EthereumClaim implementation for MsgValsetUpdatedClaim +// ====================================================== + +// GetType returns the type of the claim +func (e *MsgValsetUpdatedClaim) GetType() ClaimType { + return CLAIM_TYPE_VALSET_UPDATED +} + +// ValidateBasic performs stateless checks +func (e *MsgValsetUpdatedClaim) ValidateBasic() error { + if _, err := sdk.AccAddressFromBech32(e.Orchestrator); err != nil { + return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, e.Orchestrator) + } + if e.EventNonce == 0 { + return fmt.Errorf("nonce == 0") + } + return nil +} + +// GetSignBytes encodes the message for signing +func (msg MsgValsetUpdatedClaim) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) +} + +func (msg MsgValsetUpdatedClaim) GetClaimer() sdk.AccAddress { + err := msg.ValidateBasic() + if err != nil { + panic("MsgERC20DeployedClaim failed ValidateBasic! Should have been handled earlier") + } + + val, _ := sdk.AccAddressFromBech32(msg.Orchestrator) + return val +} + +// GetSigners defines whose signature is required +func (msg MsgValsetUpdatedClaim) GetSigners() []sdk.AccAddress { + acc, err := sdk.AccAddressFromBech32(msg.Orchestrator) + if err != nil { + panic(err) + } + + return []sdk.AccAddress{acc} +} + +// Type should return the action +func (msg MsgValsetUpdatedClaim) Type() string { return "Valset_Updated_Claim" } + +// Route should return the name of the module +func (msg MsgValsetUpdatedClaim) Route() string { return RouterKey } + +// Hash implements BridgeDeposit.Hash +func (b *MsgValsetUpdatedClaim) ClaimHash() []byte { + path := fmt.Sprintf("%d/%d/%d/%s/", b.ValsetNonce, b.EventNonce, b.BlockHeight, b.Members) + return tmhash.Sum([]byte(path)) +} + +// NewMsgCancelSendToEth returns a new msgSetOrchestratorAddress func NewMsgCancelSendToEth(val sdk.ValAddress, id uint64) *MsgCancelSendToEth { return &MsgCancelSendToEth{ TransactionId: id, diff --git a/module/x/gravity/types/msgs.pb.go b/module/x/gravity/types/msgs.pb.go index beb5ecf42..dcc7b0986 100644 --- a/module/x/gravity/types/msgs.pb.go +++ b/module/x/gravity/types/msgs.pb.go @@ -1205,6 +1205,120 @@ func (m *MsgLogicCallExecutedClaimResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgLogicCallExecutedClaimResponse proto.InternalMessageInfo +// This informs the Cosmos module that a validator +// set has been updated. +type MsgValsetUpdatedClaim struct { + EventNonce uint64 `protobuf:"varint,1,opt,name=event_nonce,json=eventNonce,proto3" json:"event_nonce,omitempty"` + ValsetNonce uint64 `protobuf:"varint,2,opt,name=valset_nonce,json=valsetNonce,proto3" json:"valset_nonce,omitempty"` + BlockHeight uint64 `protobuf:"varint,3,opt,name=block_height,json=blockHeight,proto3" json:"block_height,omitempty"` + Members []*BridgeValidator `protobuf:"bytes,4,rep,name=members,proto3" json:"members,omitempty"` + Orchestrator string `protobuf:"bytes,6,opt,name=orchestrator,proto3" json:"orchestrator,omitempty"` +} + +func (m *MsgValsetUpdatedClaim) Reset() { *m = MsgValsetUpdatedClaim{} } +func (m *MsgValsetUpdatedClaim) String() string { return proto.CompactTextString(m) } +func (*MsgValsetUpdatedClaim) ProtoMessage() {} +func (*MsgValsetUpdatedClaim) Descriptor() ([]byte, []int) { + return fileDescriptor_2f8523f2f6feb451, []int{20} +} +func (m *MsgValsetUpdatedClaim) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgValsetUpdatedClaim) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgValsetUpdatedClaim.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgValsetUpdatedClaim) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgValsetUpdatedClaim.Merge(m, src) +} +func (m *MsgValsetUpdatedClaim) XXX_Size() int { + return m.Size() +} +func (m *MsgValsetUpdatedClaim) XXX_DiscardUnknown() { + xxx_messageInfo_MsgValsetUpdatedClaim.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgValsetUpdatedClaim proto.InternalMessageInfo + +func (m *MsgValsetUpdatedClaim) GetEventNonce() uint64 { + if m != nil { + return m.EventNonce + } + return 0 +} + +func (m *MsgValsetUpdatedClaim) GetValsetNonce() uint64 { + if m != nil { + return m.ValsetNonce + } + return 0 +} + +func (m *MsgValsetUpdatedClaim) GetBlockHeight() uint64 { + if m != nil { + return m.BlockHeight + } + return 0 +} + +func (m *MsgValsetUpdatedClaim) GetMembers() []*BridgeValidator { + if m != nil { + return m.Members + } + return nil +} + +func (m *MsgValsetUpdatedClaim) GetOrchestrator() string { + if m != nil { + return m.Orchestrator + } + return "" +} + +type MsgValsetUpdatedClaimResponse struct { +} + +func (m *MsgValsetUpdatedClaimResponse) Reset() { *m = MsgValsetUpdatedClaimResponse{} } +func (m *MsgValsetUpdatedClaimResponse) String() string { return proto.CompactTextString(m) } +func (*MsgValsetUpdatedClaimResponse) ProtoMessage() {} +func (*MsgValsetUpdatedClaimResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_2f8523f2f6feb451, []int{21} +} +func (m *MsgValsetUpdatedClaimResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgValsetUpdatedClaimResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgValsetUpdatedClaimResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgValsetUpdatedClaimResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgValsetUpdatedClaimResponse.Merge(m, src) +} +func (m *MsgValsetUpdatedClaimResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgValsetUpdatedClaimResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgValsetUpdatedClaimResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgValsetUpdatedClaimResponse proto.InternalMessageInfo + // This call allows the sender (and only the sender) // to cancel a given MsgSendToEth and recieve a refund // of the tokens @@ -1217,7 +1331,7 @@ func (m *MsgCancelSendToEth) Reset() { *m = MsgCancelSendToEth{} } func (m *MsgCancelSendToEth) String() string { return proto.CompactTextString(m) } func (*MsgCancelSendToEth) ProtoMessage() {} func (*MsgCancelSendToEth) Descriptor() ([]byte, []int) { - return fileDescriptor_2f8523f2f6feb451, []int{20} + return fileDescriptor_2f8523f2f6feb451, []int{22} } func (m *MsgCancelSendToEth) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1267,7 +1381,7 @@ func (m *MsgCancelSendToEthResponse) Reset() { *m = MsgCancelSendToEthRe func (m *MsgCancelSendToEthResponse) String() string { return proto.CompactTextString(m) } func (*MsgCancelSendToEthResponse) ProtoMessage() {} func (*MsgCancelSendToEthResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_2f8523f2f6feb451, []int{21} + return fileDescriptor_2f8523f2f6feb451, []int{23} } func (m *MsgCancelSendToEthResponse) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1317,6 +1431,8 @@ func init() { proto.RegisterType((*MsgERC20DeployedClaimResponse)(nil), "gravity.v1.MsgERC20DeployedClaimResponse") proto.RegisterType((*MsgLogicCallExecutedClaim)(nil), "gravity.v1.MsgLogicCallExecutedClaim") proto.RegisterType((*MsgLogicCallExecutedClaimResponse)(nil), "gravity.v1.MsgLogicCallExecutedClaimResponse") + proto.RegisterType((*MsgValsetUpdatedClaim)(nil), "gravity.v1.MsgValsetUpdatedClaim") + proto.RegisterType((*MsgValsetUpdatedClaimResponse)(nil), "gravity.v1.MsgValsetUpdatedClaimResponse") proto.RegisterType((*MsgCancelSendToEth)(nil), "gravity.v1.MsgCancelSendToEth") proto.RegisterType((*MsgCancelSendToEthResponse)(nil), "gravity.v1.MsgCancelSendToEthResponse") } @@ -1324,87 +1440,93 @@ func init() { func init() { proto.RegisterFile("gravity/v1/msgs.proto", fileDescriptor_2f8523f2f6feb451) } var fileDescriptor_2f8523f2f6feb451 = []byte{ - // 1280 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x57, 0x4d, 0x6f, 0xdb, 0x46, - 0x13, 0x36, 0x6d, 0xd9, 0x89, 0xc7, 0x72, 0x9c, 0x97, 0xaf, 0xe3, 0x48, 0x8c, 0x23, 0xd9, 0x4c, - 0x6c, 0xa7, 0x40, 0x2d, 0xc6, 0xee, 0x21, 0xb7, 0x16, 0xb5, 0x9c, 0xa0, 0x01, 0xea, 0x14, 0x95, - 0x8b, 0x16, 0xe8, 0x85, 0xa0, 0xc8, 0x09, 0x49, 0x84, 0xe4, 0xaa, 0xdc, 0x95, 0x1c, 0x5f, 0x02, - 0xb4, 0xd7, 0xf6, 0xd0, 0x8f, 0x6b, 0xfb, 0x1b, 0x7a, 0xef, 0xb1, 0xa7, 0x9c, 0x8a, 0x00, 0xbd, - 0x14, 0x2d, 0x10, 0x14, 0x49, 0x7f, 0x48, 0xc1, 0xdd, 0x15, 0xc5, 0x2f, 0xc5, 0x3e, 0xb8, 0x27, - 0x89, 0xb3, 0xc3, 0x99, 0xe7, 0x79, 0x66, 0x76, 0x76, 0x09, 0xd7, 0xdc, 0xd8, 0x1a, 0xf9, 0xec, - 0xd4, 0x18, 0xed, 0x19, 0x21, 0x75, 0x69, 0x67, 0x10, 0x13, 0x46, 0x54, 0x90, 0xe6, 0xce, 0x68, - 0x4f, 0x6b, 0xd9, 0x84, 0x86, 0x84, 0x1a, 0x7d, 0x8b, 0xa2, 0x31, 0xda, 0xeb, 0x23, 0xb3, 0xf6, - 0x0c, 0x9b, 0xf8, 0x91, 0xf0, 0xd5, 0x56, 0x5d, 0xe2, 0x12, 0xfe, 0xd7, 0x48, 0xfe, 0x49, 0xeb, - 0xba, 0x4b, 0x88, 0x1b, 0xa0, 0x61, 0x0d, 0x7c, 0xc3, 0x8a, 0x22, 0xc2, 0x2c, 0xe6, 0x93, 0x48, - 0xc6, 0xd7, 0x9f, 0x41, 0xf3, 0x88, 0xba, 0xc7, 0xc8, 0x3e, 0x8a, 0x6d, 0x0f, 0x29, 0x8b, 0x2d, - 0x46, 0xe2, 0xf7, 0x1d, 0x27, 0x46, 0x4a, 0xd5, 0x75, 0x58, 0x1c, 0x59, 0x81, 0xef, 0x24, 0xb6, - 0x86, 0xb2, 0xa1, 0xdc, 0x59, 0xec, 0x4d, 0x0c, 0xaa, 0x0e, 0x75, 0x92, 0x79, 0xa9, 0x31, 0xcb, - 0x1d, 0x72, 0x36, 0xb5, 0x0d, 0x4b, 0xc8, 0x3c, 0xd3, 0x12, 0x01, 0x1b, 0x73, 0xdc, 0x05, 0x90, - 0x79, 0x32, 0x85, 0x7e, 0x0b, 0x36, 0xa7, 0xe6, 0xef, 0x21, 0x1d, 0x90, 0x88, 0xa2, 0xfe, 0xb5, - 0x02, 0x57, 0x8f, 0xa8, 0xfb, 0xa9, 0x15, 0x50, 0x64, 0x5d, 0x12, 0x3d, 0xf6, 0xe3, 0x50, 0x5d, - 0x85, 0xf9, 0x88, 0x44, 0x36, 0x72, 0x60, 0xb5, 0x9e, 0x78, 0xb8, 0x10, 0x50, 0x09, 0x6f, 0xea, - 0xbb, 0x91, 0xc5, 0x86, 0x31, 0x36, 0x6a, 0x82, 0x77, 0x6a, 0xd0, 0x35, 0x68, 0x14, 0xc1, 0xa4, - 0x48, 0x7f, 0x51, 0xa0, 0xce, 0xf9, 0x44, 0xce, 0x27, 0xe4, 0x3e, 0xf3, 0xd4, 0x35, 0x58, 0xa0, - 0x18, 0x39, 0x38, 0xd6, 0x4f, 0x3e, 0xa9, 0x4d, 0xb8, 0x9c, 0x60, 0x70, 0x90, 0x32, 0x89, 0xf1, - 0x12, 0x32, 0xef, 0x10, 0x29, 0x53, 0xef, 0xc1, 0x82, 0x15, 0x92, 0x61, 0xc4, 0x38, 0xb2, 0xa5, - 0xfd, 0x66, 0x47, 0xd4, 0xbd, 0x93, 0xd4, 0xbd, 0x23, 0xeb, 0xde, 0xe9, 0x12, 0x3f, 0x3a, 0xa8, - 0x3d, 0x7f, 0xd9, 0x9e, 0xe9, 0x49, 0x77, 0xf5, 0x5d, 0x80, 0x7e, 0xec, 0x3b, 0x2e, 0x9a, 0x8f, - 0x51, 0xe0, 0x3e, 0xc7, 0xcb, 0x8b, 0xe2, 0x95, 0x07, 0x88, 0xfa, 0x1a, 0xac, 0x66, 0xb1, 0xa7, - 0xa4, 0xde, 0x83, 0x95, 0x23, 0xea, 0xf6, 0xf0, 0x8b, 0x21, 0x52, 0x76, 0x60, 0x31, 0x7b, 0x3a, - 0xad, 0x55, 0x98, 0x77, 0x30, 0x22, 0xa1, 0xe4, 0x24, 0x1e, 0xf4, 0x26, 0x5c, 0x2f, 0x04, 0x48, - 0x63, 0xff, 0xac, 0xf0, 0xe0, 0x52, 0x47, 0x11, 0xbc, 0xba, 0xb2, 0x5b, 0x70, 0x85, 0x91, 0x27, - 0x18, 0x99, 0x36, 0x89, 0x58, 0x6c, 0xd9, 0x63, 0xdd, 0x96, 0xb9, 0xb5, 0x2b, 0x8d, 0xea, 0x4d, - 0x48, 0x2a, 0x69, 0x26, 0xe5, 0xc2, 0x58, 0xd6, 0x76, 0x11, 0x99, 0x77, 0xcc, 0x0d, 0xa5, 0xfe, - 0xa8, 0x55, 0xf4, 0x47, 0xae, 0xfc, 0xf3, 0xc5, 0xf2, 0x0b, 0x32, 0x59, 0xc0, 0x29, 0x99, 0xdf, - 0x14, 0xf8, 0xff, 0x64, 0xed, 0x43, 0xe2, 0xfa, 0x76, 0xd7, 0x0a, 0x02, 0x75, 0x07, 0x56, 0xfc, - 0x48, 0x6e, 0x1c, 0x9f, 0x44, 0xa6, 0xef, 0x48, 0xd9, 0xae, 0x64, 0xcd, 0x0f, 0x1d, 0x75, 0x17, - 0xd4, 0x9c, 0xa3, 0x90, 0x61, 0x96, 0xcb, 0xf0, 0xbf, 0xec, 0xca, 0x23, 0x2e, 0xc9, 0x7f, 0xce, - 0xf5, 0x26, 0xdc, 0xa8, 0xe0, 0x33, 0xe9, 0xf6, 0x59, 0x5e, 0xbc, 0x43, 0x1c, 0x10, 0xea, 0xb3, - 0x6e, 0x60, 0xf9, 0x21, 0xdf, 0x5c, 0x23, 0x8c, 0x98, 0x99, 0x2d, 0x21, 0x70, 0x93, 0x00, 0xbd, - 0x09, 0xf5, 0x7e, 0x40, 0xec, 0x27, 0xa6, 0x87, 0xbe, 0xeb, 0x31, 0xc9, 0x6e, 0x89, 0xdb, 0x3e, - 0xe0, 0xa6, 0x8a, 0x52, 0xcf, 0x55, 0x95, 0xfa, 0x41, 0xba, 0x51, 0x38, 0xb3, 0x83, 0x4e, 0xd2, - 0xd0, 0x7f, 0xbe, 0x6c, 0x6f, 0xbb, 0x3e, 0xf3, 0x86, 0xfd, 0x8e, 0x4d, 0x42, 0x43, 0x8e, 0x4c, - 0xf1, 0xb3, 0x4b, 0x9d, 0x27, 0x06, 0x3b, 0x1d, 0x20, 0xed, 0x3c, 0x8c, 0x58, 0xba, 0x6f, 0x76, - 0x60, 0x05, 0x99, 0x87, 0x31, 0x0e, 0x43, 0x53, 0x76, 0xb5, 0x50, 0xe2, 0xca, 0xd8, 0x7c, 0x2c, - 0xba, 0x7b, 0x07, 0x56, 0x44, 0x20, 0x33, 0x46, 0x1b, 0xfd, 0x11, 0xc6, 0x8d, 0x05, 0xe1, 0x28, - 0xcc, 0x3d, 0x69, 0x2d, 0x29, 0x7f, 0xa9, 0xac, 0xbc, 0xec, 0xa3, 0xac, 0x76, 0xa9, 0xae, 0xbf, - 0x8a, 0x79, 0xf7, 0x99, 0xcf, 0x3c, 0x27, 0xb6, 0x4e, 0x2e, 0x4e, 0xd8, 0x36, 0x2c, 0xf5, 0x93, - 0x8e, 0x95, 0x31, 0xe6, 0x44, 0x0c, 0x6e, 0x7a, 0x34, 0x65, 0x93, 0xd5, 0xaa, 0x94, 0x2f, 0xf2, - 0x9b, 0xaf, 0xe0, 0x27, 0xc6, 0x64, 0x8e, 0x43, 0x4a, 0xf0, 0xbb, 0x59, 0xb8, 0x76, 0x44, 0xdd, - 0xfb, 0xbd, 0xee, 0xfe, 0xdd, 0x43, 0x1c, 0x04, 0xe4, 0x14, 0x9d, 0x8b, 0x63, 0xb9, 0x09, 0x75, - 0x59, 0x26, 0x31, 0x8b, 0x44, 0xf3, 0x2c, 0x09, 0xdb, 0x61, 0x62, 0x3a, 0x2f, 0x4f, 0x15, 0x6a, - 0x91, 0x15, 0x8e, 0x37, 0x06, 0xff, 0xcf, 0x47, 0xdf, 0x69, 0xd8, 0x27, 0x81, 0xac, 0xbd, 0x7c, - 0x52, 0x35, 0xb8, 0xec, 0xa0, 0xed, 0x87, 0x56, 0x40, 0x79, 0xbd, 0x6b, 0xbd, 0xf4, 0xb9, 0xa4, - 0xd7, 0xe5, 0x0a, 0xbd, 0xda, 0x70, 0xb3, 0x52, 0x92, 0x54, 0xb4, 0xbf, 0x14, 0x7e, 0x56, 0xa7, - 0xdb, 0xf0, 0xfe, 0x53, 0xb4, 0x87, 0xec, 0x22, 0x85, 0xab, 0x98, 0x53, 0x89, 0x76, 0xf5, 0x73, - 0xce, 0xa9, 0xda, 0xb4, 0x39, 0x75, 0x9e, 0x76, 0x11, 0x17, 0x81, 0x6a, 0x72, 0xa9, 0x04, 0xc7, - 0xa0, 0x26, 0xf3, 0xc8, 0x8a, 0x6c, 0x0c, 0x26, 0x67, 0x6c, 0x52, 0xcc, 0xd8, 0x8a, 0xa8, 0x65, - 0x67, 0xa7, 0x6b, 0xad, 0xb7, 0x9c, 0xb1, 0x3e, 0x74, 0x32, 0x67, 0xd6, 0x6c, 0xf6, 0xcc, 0xd2, - 0xd7, 0x41, 0x2b, 0x07, 0x1d, 0xa7, 0xdc, 0xff, 0x7e, 0x09, 0xe6, 0x8e, 0xa8, 0xab, 0x9e, 0xc0, - 0x72, 0xfe, 0xfe, 0xb1, 0xde, 0x99, 0x5c, 0xcd, 0x3a, 0xc5, 0x0b, 0x81, 0x76, 0xfb, 0x4d, 0xab, - 0x29, 0x1f, 0xfd, 0xab, 0xdf, 0xff, 0xf9, 0x61, 0x76, 0x5d, 0xd7, 0x8c, 0xcc, 0xed, 0x6f, 0xc4, - 0x5d, 0x93, 0xce, 0xe4, 0x79, 0x3c, 0x58, 0x9c, 0x50, 0x6d, 0x14, 0xc2, 0xa6, 0x2b, 0xda, 0xc6, - 0xb4, 0x95, 0x34, 0x59, 0x9b, 0x27, 0x6b, 0xea, 0xd7, 0xb3, 0xc9, 0x12, 0x0d, 0x4c, 0x46, 0x4c, - 0x64, 0x9e, 0x4a, 0xa1, 0x9e, 0x3b, 0xe4, 0x6f, 0x14, 0x42, 0x66, 0x17, 0xb5, 0x5b, 0x6f, 0x58, - 0x4c, 0x53, 0x6e, 0xf2, 0x94, 0x37, 0xf4, 0x66, 0x36, 0x65, 0x2c, 0x3c, 0x4d, 0x3e, 0x76, 0x92, - 0xa4, 0xb9, 0xc3, 0xbf, 0x98, 0x34, 0xbb, 0x58, 0x4a, 0x5a, 0x79, 0x0a, 0x57, 0x26, 0x95, 0x6a, - 0xca, 0xa4, 0xcf, 0xe0, 0x6a, 0xe9, 0x90, 0x6e, 0x57, 0xc7, 0x4e, 0x1d, 0xb4, 0x9d, 0x33, 0x1c, - 0x52, 0x00, 0x1b, 0x1c, 0x80, 0xa6, 0x37, 0x4a, 0x00, 0x42, 0x33, 0x48, 0xbc, 0x13, 0xd2, 0xb9, - 0x43, 0xb3, 0x48, 0x3a, 0xbb, 0x58, 0x22, 0x5d, 0x79, 0x64, 0x54, 0x92, 0x76, 0x84, 0xa7, 0x69, - 0xf3, 0x24, 0x27, 0xb0, 0x9c, 0x3f, 0x51, 0x8a, 0x1d, 0x9c, 0x5b, 0x2d, 0x75, 0x70, 0xf5, 0x24, - 0xaf, 0xec, 0xe0, 0x13, 0xe9, 0x2a, 0x13, 0x7f, 0xa3, 0x80, 0x5a, 0x31, 0xea, 0x37, 0x0b, 0x09, - 0xca, 0x2e, 0xda, 0x5b, 0x67, 0xba, 0xa4, 0x40, 0xee, 0x70, 0x20, 0xba, 0xbe, 0x91, 0x05, 0x82, - 0xb1, 0xbd, 0x7f, 0xd7, 0x74, 0xe4, 0x0b, 0x12, 0xce, 0x4f, 0x0a, 0xac, 0x4d, 0x19, 0xa2, 0x5b, - 0x85, 0x7c, 0xd5, 0x6e, 0xda, 0xee, 0xb9, 0xdc, 0x52, 0x68, 0xbb, 0x1c, 0xda, 0x8e, 0xbe, 0x95, - 0x85, 0xc6, 0x1b, 0xc1, 0xb4, 0xad, 0x20, 0x30, 0x51, 0xbe, 0x25, 0xf1, 0xfd, 0xa8, 0xc0, 0xda, - 0x94, 0x0f, 0xb2, 0xad, 0xd2, 0x26, 0xaf, 0x72, 0x2b, 0xe1, 0x3b, 0xe3, 0xf3, 0xea, 0x6d, 0x8e, - 0x6f, 0x5b, 0xbf, 0x9d, 0x1f, 0x0c, 0xcc, 0xcc, 0x0e, 0xe8, 0xf1, 0xe7, 0x92, 0xfa, 0xa5, 0x02, - 0x2b, 0xc5, 0x09, 0xdc, 0x2a, 0x6e, 0x8d, 0xfc, 0xba, 0xb6, 0xfd, 0xe6, 0xf5, 0x14, 0xc9, 0x36, - 0x47, 0xb2, 0xa1, 0xb7, 0x72, 0x3b, 0x87, 0x3b, 0x9b, 0x99, 0x49, 0x75, 0xf0, 0xf1, 0xf3, 0x57, - 0x2d, 0xe5, 0xc5, 0xab, 0x96, 0xf2, 0xf7, 0xab, 0x96, 0xf2, 0xed, 0xeb, 0xd6, 0xcc, 0x8b, 0xd7, - 0xad, 0x99, 0x3f, 0x5e, 0xb7, 0x66, 0x3e, 0xbf, 0x57, 0xbe, 0xfb, 0xc9, 0x50, 0xbb, 0xe2, 0x43, - 0xc7, 0x08, 0x89, 0x33, 0x0c, 0xd0, 0x78, 0x9a, 0xa6, 0xe0, 0x17, 0xc2, 0xfe, 0x02, 0xff, 0x1e, - 0x7e, 0xe7, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa9, 0x09, 0x75, 0xd0, 0x88, 0x0f, 0x00, 0x00, + // 1374 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x58, 0x4f, 0x6f, 0xdc, 0x54, + 0x10, 0x8f, 0x93, 0x4d, 0xda, 0xcc, 0x6e, 0x9a, 0xd6, 0xa4, 0xe9, 0xc6, 0x49, 0x77, 0x13, 0xb7, + 0x49, 0x8a, 0x44, 0x76, 0x9b, 0x20, 0xd4, 0x1b, 0x88, 0x24, 0xad, 0xa8, 0x44, 0x8a, 0xd8, 0x40, + 0x91, 0xb8, 0x58, 0x5e, 0x7b, 0x6a, 0x5b, 0xb5, 0xfd, 0x16, 0xbf, 0xb7, 0x9b, 0xe6, 0x52, 0x09, + 0xae, 0xe5, 0x00, 0xe2, 0x0a, 0x12, 0xdf, 0x80, 0x3b, 0x47, 0x4e, 0x3d, 0xa1, 0x4a, 0x5c, 0xf8, + 0x23, 0x55, 0xa8, 0xe5, 0x83, 0x20, 0xbf, 0xf7, 0xd6, 0xeb, 0x7f, 0x9b, 0x04, 0x29, 0x9c, 0xb2, + 0x9e, 0x37, 0x9e, 0xf9, 0xfd, 0x66, 0xe6, 0xcd, 0x8c, 0x03, 0x57, 0x9d, 0xc8, 0x1c, 0x78, 0xec, + 0xb8, 0x3d, 0xd8, 0x6e, 0x07, 0xd4, 0xa1, 0xad, 0x5e, 0x44, 0x18, 0x51, 0x41, 0x8a, 0x5b, 0x83, + 0x6d, 0xad, 0x61, 0x11, 0x1a, 0x10, 0xda, 0xee, 0x9a, 0x14, 0xdb, 0x83, 0xed, 0x2e, 0x32, 0x73, + 0xbb, 0x6d, 0x11, 0x2f, 0x14, 0xba, 0xda, 0x82, 0x43, 0x1c, 0xc2, 0x7f, 0xb6, 0xe3, 0x5f, 0x52, + 0xba, 0xe2, 0x10, 0xe2, 0xf8, 0xd8, 0x36, 0x7b, 0x5e, 0xdb, 0x0c, 0x43, 0xc2, 0x4c, 0xe6, 0x91, + 0x50, 0xda, 0xd7, 0x16, 0x53, 0x6e, 0xd9, 0x71, 0x0f, 0xa5, 0x5c, 0x7f, 0x0a, 0x4b, 0x07, 0xd4, + 0x39, 0x44, 0xf6, 0x51, 0x64, 0xb9, 0x48, 0x59, 0x64, 0x32, 0x12, 0xbd, 0x6f, 0xdb, 0x11, 0x52, + 0xaa, 0xae, 0xc0, 0xec, 0xc0, 0xf4, 0x3d, 0x3b, 0x96, 0xd5, 0x95, 0x55, 0xe5, 0xd6, 0x6c, 0x67, + 0x24, 0x50, 0x75, 0xa8, 0x91, 0xd4, 0x4b, 0xf5, 0x49, 0xae, 0x90, 0x91, 0xa9, 0x4d, 0xa8, 0x22, + 0x73, 0x0d, 0x53, 0x18, 0xac, 0x4f, 0x71, 0x15, 0x40, 0xe6, 0x4a, 0x17, 0xfa, 0x0d, 0x58, 0x1b, + 0xeb, 0xbf, 0x83, 0xb4, 0x47, 0x42, 0x8a, 0xfa, 0x33, 0x05, 0x2e, 0x1f, 0x50, 0xe7, 0xa1, 0xe9, + 0x53, 0x64, 0x7b, 0x24, 0x7c, 0xe4, 0x45, 0x81, 0xba, 0x00, 0xd3, 0x21, 0x09, 0x2d, 0xe4, 0xc0, + 0x2a, 0x1d, 0xf1, 0x70, 0x2e, 0xa0, 0x62, 0xde, 0xd4, 0x73, 0x42, 0x93, 0xf5, 0x23, 0xac, 0x57, + 0x04, 0xef, 0x44, 0xa0, 0x6b, 0x50, 0xcf, 0x83, 0x49, 0x90, 0xfe, 0xac, 0x40, 0x8d, 0xf3, 0x09, + 0xed, 0x4f, 0xc8, 0x5d, 0xe6, 0xaa, 0x8b, 0x30, 0x43, 0x31, 0xb4, 0x71, 0x18, 0x3f, 0xf9, 0xa4, + 0x2e, 0xc1, 0xc5, 0x18, 0x83, 0x8d, 0x94, 0x49, 0x8c, 0x17, 0x90, 0xb9, 0xfb, 0x48, 0x99, 0x7a, + 0x07, 0x66, 0xcc, 0x80, 0xf4, 0x43, 0xc6, 0x91, 0x55, 0x77, 0x96, 0x5a, 0xa2, 0x1e, 0x5a, 0x71, + 0x3d, 0xb4, 0x64, 0x3d, 0xb4, 0xf6, 0x88, 0x17, 0xee, 0x56, 0x9e, 0xbf, 0x6c, 0x4e, 0x74, 0xa4, + 0xba, 0xfa, 0x2e, 0x40, 0x37, 0xf2, 0x6c, 0x07, 0x8d, 0x47, 0x28, 0x70, 0x9f, 0xe1, 0xe5, 0x59, + 0xf1, 0xca, 0x3d, 0x44, 0x7d, 0x11, 0x16, 0xd2, 0xd8, 0x13, 0x52, 0xef, 0xc1, 0xfc, 0x01, 0x75, + 0x3a, 0xf8, 0x45, 0x1f, 0x29, 0xdb, 0x35, 0x99, 0x35, 0x9e, 0xd6, 0x02, 0x4c, 0xdb, 0x18, 0x92, + 0x40, 0x72, 0x12, 0x0f, 0xfa, 0x12, 0x5c, 0xcb, 0x19, 0x48, 0x6c, 0xff, 0xa4, 0x70, 0xe3, 0x32, + 0x8e, 0xc2, 0x78, 0x79, 0x66, 0xd7, 0xe1, 0x12, 0x23, 0x8f, 0x31, 0x34, 0x2c, 0x12, 0xb2, 0xc8, + 0xb4, 0x86, 0x71, 0x9b, 0xe3, 0xd2, 0x3d, 0x29, 0x54, 0xaf, 0x43, 0x9c, 0x49, 0x23, 0x4e, 0x17, + 0x46, 0x32, 0xb7, 0xb3, 0xc8, 0xdc, 0x43, 0x2e, 0x28, 0xd4, 0x47, 0xa5, 0xa4, 0x3e, 0x32, 0xe9, + 0x9f, 0xce, 0xa7, 0x5f, 0x90, 0x49, 0x03, 0x4e, 0xc8, 0xfc, 0xaa, 0xc0, 0x1b, 0xa3, 0xb3, 0x0f, + 0x89, 0xe3, 0x59, 0x7b, 0xa6, 0xef, 0xab, 0x9b, 0x30, 0xef, 0x85, 0xf2, 0xe2, 0x78, 0x24, 0x34, + 0x3c, 0x5b, 0x86, 0xed, 0x52, 0x5a, 0x7c, 0xdf, 0x56, 0xb7, 0x40, 0xcd, 0x28, 0x8a, 0x30, 0x4c, + 0xf2, 0x30, 0x5c, 0x49, 0x9f, 0x3c, 0xe0, 0x21, 0xf9, 0xdf, 0xb9, 0x5e, 0x87, 0xe5, 0x12, 0x3e, + 0xa3, 0x6a, 0x9f, 0xe4, 0xc9, 0xdb, 0xc7, 0x1e, 0xa1, 0x1e, 0xdb, 0xf3, 0x4d, 0x2f, 0xe0, 0x97, + 0x6b, 0x80, 0x21, 0x33, 0xd2, 0x29, 0x04, 0x2e, 0x12, 0xa0, 0xd7, 0xa0, 0xd6, 0xf5, 0x89, 0xf5, + 0xd8, 0x70, 0xd1, 0x73, 0x5c, 0x26, 0xd9, 0x55, 0xb9, 0xec, 0x03, 0x2e, 0x2a, 0x49, 0xf5, 0x54, + 0x59, 0xaa, 0xef, 0x25, 0x17, 0x85, 0x33, 0xdb, 0x6d, 0xc5, 0x05, 0xfd, 0xe7, 0xcb, 0xe6, 0x86, + 0xe3, 0x31, 0xb7, 0xdf, 0x6d, 0x59, 0x24, 0x68, 0xcb, 0x56, 0x2a, 0xfe, 0x6c, 0x51, 0xfb, 0xb1, + 0xec, 0x7e, 0xf7, 0x43, 0x96, 0xdc, 0x9b, 0x4d, 0x98, 0x47, 0xe6, 0x62, 0x84, 0xfd, 0xc0, 0x90, + 0x55, 0x2d, 0x22, 0x71, 0x69, 0x28, 0x3e, 0x14, 0xd5, 0xbd, 0x09, 0xf3, 0xc2, 0x90, 0x11, 0xa1, + 0x85, 0xde, 0x00, 0xa3, 0xfa, 0x8c, 0x50, 0x14, 0xe2, 0x8e, 0x94, 0x16, 0x22, 0x7f, 0xa1, 0x18, + 0x79, 0x59, 0x47, 0xe9, 0xd8, 0x25, 0x71, 0xfd, 0x45, 0xf4, 0xbb, 0xcf, 0x3c, 0xe6, 0xda, 0x91, + 0x79, 0x74, 0x7e, 0x81, 0x6d, 0x42, 0xb5, 0x1b, 0x57, 0xac, 0xb4, 0x31, 0x25, 0x6c, 0x70, 0xd1, + 0x83, 0x31, 0x97, 0xac, 0x52, 0x16, 0xf9, 0x3c, 0xbf, 0xe9, 0x12, 0x7e, 0xa2, 0x4d, 0x66, 0x38, + 0x24, 0x04, 0xbf, 0x9d, 0x84, 0xab, 0x07, 0xd4, 0xb9, 0xdb, 0xd9, 0xdb, 0xb9, 0xbd, 0x8f, 0x3d, + 0x9f, 0x1c, 0xa3, 0x7d, 0x7e, 0x2c, 0xd7, 0xa0, 0x26, 0xd3, 0x24, 0x7a, 0x91, 0x28, 0x9e, 0xaa, + 0x90, 0xed, 0xc7, 0xa2, 0xb3, 0xf2, 0x54, 0xa1, 0x12, 0x9a, 0xc1, 0xf0, 0x62, 0xf0, 0xdf, 0xbc, + 0xf5, 0x1d, 0x07, 0x5d, 0xe2, 0xcb, 0xdc, 0xcb, 0x27, 0x55, 0x83, 0x8b, 0x36, 0x5a, 0x5e, 0x60, + 0xfa, 0x94, 0xe7, 0xbb, 0xd2, 0x49, 0x9e, 0x0b, 0xf1, 0xba, 0x58, 0x12, 0xaf, 0x26, 0x5c, 0x2f, + 0x0d, 0x49, 0x12, 0xb4, 0xbf, 0x14, 0x3e, 0xab, 0x93, 0x6b, 0x78, 0xf7, 0x09, 0x5a, 0x7d, 0x76, + 0x9e, 0x81, 0x2b, 0xe9, 0x53, 0x71, 0xec, 0x6a, 0x67, 0xec, 0x53, 0x95, 0x71, 0x7d, 0xea, 0x2c, + 0xe5, 0x22, 0x16, 0x81, 0x72, 0x72, 0x49, 0x08, 0xfe, 0x50, 0x78, 0xdd, 0x88, 0xd9, 0xfb, 0x69, + 0xcf, 0x36, 0xff, 0x13, 0xfd, 0x01, 0x7f, 0x2d, 0xd3, 0x54, 0xab, 0x42, 0x56, 0x1e, 0xa1, 0xa9, + 0x62, 0x84, 0xde, 0x81, 0x0b, 0x01, 0x06, 0x5d, 0x8c, 0x68, 0xbd, 0xb2, 0x3a, 0x75, 0xab, 0xba, + 0xb3, 0xdc, 0x1a, 0x2d, 0x6e, 0xad, 0x5d, 0x3e, 0x4a, 0x1f, 0x0e, 0x37, 0xa4, 0xce, 0x50, 0xb7, + 0x10, 0x80, 0x99, 0xb1, 0xf9, 0x2f, 0x52, 0x4b, 0xc8, 0x1f, 0x82, 0x1a, 0x37, 0x63, 0x33, 0xb4, + 0xd0, 0x1f, 0x2d, 0x18, 0x71, 0x25, 0x47, 0x66, 0x48, 0x4d, 0x2b, 0x3d, 0x5a, 0x2a, 0x9d, 0xb9, + 0x94, 0xf4, 0xbe, 0x9d, 0x1a, 0xd8, 0x93, 0xe9, 0x81, 0xad, 0xaf, 0x80, 0x56, 0x34, 0x3a, 0x74, + 0xb9, 0xf3, 0x63, 0x0d, 0xa6, 0x0e, 0xa8, 0xa3, 0x1e, 0xc1, 0x5c, 0x76, 0xf9, 0x5a, 0x49, 0xd3, + 0xce, 0x6f, 0x43, 0xda, 0xcd, 0x93, 0x4e, 0x13, 0x3e, 0xfa, 0x57, 0xbf, 0xfd, 0xf3, 0xdd, 0xe4, + 0x8a, 0xae, 0xb5, 0x53, 0xbb, 0xa9, 0xcc, 0x91, 0x25, 0xfd, 0xb8, 0x30, 0x3b, 0xa2, 0x5a, 0xcf, + 0x99, 0x4d, 0x4e, 0xb4, 0xd5, 0x71, 0x27, 0x89, 0xb3, 0x26, 0x77, 0xb6, 0xa4, 0x5f, 0x4b, 0x3b, + 0x8b, 0x63, 0x60, 0x30, 0x62, 0x20, 0x73, 0x55, 0x0a, 0xb5, 0xcc, 0x86, 0xb3, 0x9c, 0x33, 0x99, + 0x3e, 0xd4, 0x6e, 0x9c, 0x70, 0x98, 0xb8, 0x5c, 0xe3, 0x2e, 0x97, 0xf5, 0xa5, 0xb4, 0xcb, 0x48, + 0x68, 0x1a, 0xbc, 0xe7, 0xc6, 0x4e, 0x33, 0x9b, 0x4f, 0xde, 0x69, 0xfa, 0xb0, 0xe0, 0xb4, 0x74, + 0x05, 0x29, 0x75, 0x2a, 0xa3, 0x29, 0x9d, 0x3e, 0x85, 0xcb, 0x85, 0x0d, 0xa5, 0x59, 0x6e, 0x3b, + 0x51, 0xd0, 0x36, 0x4f, 0x51, 0x48, 0x00, 0xac, 0x72, 0x00, 0x9a, 0x5e, 0x2f, 0x00, 0x08, 0x0c, + 0x3f, 0xd6, 0x8e, 0x49, 0x67, 0x36, 0x86, 0x3c, 0xe9, 0xf4, 0x61, 0x81, 0x74, 0xe9, 0xbc, 0x2c, + 0x25, 0x6d, 0x0b, 0x4d, 0xc3, 0xe2, 0x4e, 0x8e, 0x60, 0x2e, 0x3b, 0x4e, 0xf3, 0x15, 0x9c, 0x39, + 0x2d, 0x54, 0x70, 0xf9, 0x18, 0x2b, 0xad, 0xe0, 0x23, 0xa9, 0x2a, 0x1d, 0x3f, 0x53, 0xe0, 0x4a, + 0xfa, 0x52, 0x0b, 0xef, 0x6b, 0xa5, 0x37, 0x24, 0x7d, 0xed, 0xb5, 0x37, 0x4f, 0x55, 0x49, 0x70, + 0xdc, 0xe2, 0x38, 0x74, 0x7d, 0xb5, 0xe4, 0x26, 0xf5, 0xc5, 0x0b, 0x12, 0xcd, 0xd7, 0x0a, 0xa8, + 0x25, 0x53, 0x37, 0x0f, 0xa7, 0xa8, 0x52, 0x80, 0x73, 0xc2, 0xa0, 0x2a, 0x85, 0x83, 0x91, 0xb5, + 0x73, 0xdb, 0xb0, 0xe5, 0x0b, 0x12, 0xce, 0x0f, 0x0a, 0x2c, 0x8e, 0x99, 0x67, 0xeb, 0x39, 0x7f, + 0xe5, 0x6a, 0xda, 0xd6, 0x99, 0xd4, 0x12, 0x68, 0x5b, 0x1c, 0xda, 0xa6, 0xbe, 0x9e, 0x86, 0xc6, + 0xcb, 0xd2, 0xb0, 0x4c, 0xdf, 0x37, 0x50, 0xbe, 0x25, 0xf1, 0x7d, 0xaf, 0xc0, 0xe2, 0x98, 0x6f, + 0xe3, 0xf5, 0x42, 0xcb, 0x29, 0x53, 0x2b, 0xe0, 0x3b, 0xe5, 0x4b, 0xf7, 0x2d, 0x8e, 0x6f, 0x43, + 0xbf, 0x99, 0x6d, 0x53, 0xcc, 0x48, 0x8f, 0x8a, 0xe1, 0x97, 0xab, 0xfa, 0xa5, 0x02, 0xf3, 0xf9, + 0x79, 0xd0, 0xc8, 0x5f, 0xd4, 0xec, 0xb9, 0xb6, 0x71, 0xf2, 0x79, 0x82, 0x64, 0x83, 0x23, 0x59, + 0xd5, 0x1b, 0x99, 0x7b, 0xcc, 0x95, 0x8d, 0x54, 0xdf, 0xdc, 0xfd, 0xf8, 0xf9, 0xab, 0x86, 0xf2, + 0xe2, 0x55, 0x43, 0xf9, 0xfb, 0x55, 0x43, 0xf9, 0xe6, 0x75, 0x63, 0xe2, 0xc5, 0xeb, 0xc6, 0xc4, + 0xef, 0xaf, 0x1b, 0x13, 0x9f, 0xdf, 0x29, 0xae, 0xe1, 0xd2, 0xd4, 0x96, 0xf8, 0xe6, 0x6c, 0x07, + 0xc4, 0xee, 0xfb, 0xd8, 0x7e, 0x92, 0xb8, 0xe0, 0xbb, 0x79, 0x77, 0x86, 0xff, 0x6b, 0xe2, 0xed, + 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0xec, 0xf5, 0x0a, 0x6c, 0x2b, 0x11, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1426,6 +1548,7 @@ type MsgClient interface { ConfirmLogicCall(ctx context.Context, in *MsgConfirmLogicCall, opts ...grpc.CallOption) (*MsgConfirmLogicCallResponse, error) DepositClaim(ctx context.Context, in *MsgDepositClaim, opts ...grpc.CallOption) (*MsgDepositClaimResponse, error) WithdrawClaim(ctx context.Context, in *MsgWithdrawClaim, opts ...grpc.CallOption) (*MsgWithdrawClaimResponse, error) + ValsetUpdateClaim(ctx context.Context, in *MsgValsetUpdatedClaim, opts ...grpc.CallOption) (*MsgValsetUpdatedClaimResponse, error) ERC20DeployedClaim(ctx context.Context, in *MsgERC20DeployedClaim, opts ...grpc.CallOption) (*MsgERC20DeployedClaimResponse, error) LogicCallExecutedClaim(ctx context.Context, in *MsgLogicCallExecutedClaim, opts ...grpc.CallOption) (*MsgLogicCallExecutedClaimResponse, error) SetOrchestratorAddress(ctx context.Context, in *MsgSetOrchestratorAddress, opts ...grpc.CallOption) (*MsgSetOrchestratorAddressResponse, error) @@ -1503,6 +1626,15 @@ func (c *msgClient) WithdrawClaim(ctx context.Context, in *MsgWithdrawClaim, opt return out, nil } +func (c *msgClient) ValsetUpdateClaim(ctx context.Context, in *MsgValsetUpdatedClaim, opts ...grpc.CallOption) (*MsgValsetUpdatedClaimResponse, error) { + out := new(MsgValsetUpdatedClaimResponse) + err := c.cc.Invoke(ctx, "/gravity.v1.Msg/ValsetUpdateClaim", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *msgClient) ERC20DeployedClaim(ctx context.Context, in *MsgERC20DeployedClaim, opts ...grpc.CallOption) (*MsgERC20DeployedClaimResponse, error) { out := new(MsgERC20DeployedClaimResponse) err := c.cc.Invoke(ctx, "/gravity.v1.Msg/ERC20DeployedClaim", in, out, opts...) @@ -1548,6 +1680,7 @@ type MsgServer interface { ConfirmLogicCall(context.Context, *MsgConfirmLogicCall) (*MsgConfirmLogicCallResponse, error) DepositClaim(context.Context, *MsgDepositClaim) (*MsgDepositClaimResponse, error) WithdrawClaim(context.Context, *MsgWithdrawClaim) (*MsgWithdrawClaimResponse, error) + ValsetUpdateClaim(context.Context, *MsgValsetUpdatedClaim) (*MsgValsetUpdatedClaimResponse, error) ERC20DeployedClaim(context.Context, *MsgERC20DeployedClaim) (*MsgERC20DeployedClaimResponse, error) LogicCallExecutedClaim(context.Context, *MsgLogicCallExecutedClaim) (*MsgLogicCallExecutedClaimResponse, error) SetOrchestratorAddress(context.Context, *MsgSetOrchestratorAddress) (*MsgSetOrchestratorAddressResponse, error) @@ -1579,6 +1712,9 @@ func (*UnimplementedMsgServer) DepositClaim(ctx context.Context, req *MsgDeposit func (*UnimplementedMsgServer) WithdrawClaim(ctx context.Context, req *MsgWithdrawClaim) (*MsgWithdrawClaimResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method WithdrawClaim not implemented") } +func (*UnimplementedMsgServer) ValsetUpdateClaim(ctx context.Context, req *MsgValsetUpdatedClaim) (*MsgValsetUpdatedClaimResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ValsetUpdateClaim not implemented") +} func (*UnimplementedMsgServer) ERC20DeployedClaim(ctx context.Context, req *MsgERC20DeployedClaim) (*MsgERC20DeployedClaimResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method ERC20DeployedClaim not implemented") } @@ -1722,6 +1858,24 @@ func _Msg_WithdrawClaim_Handler(srv interface{}, ctx context.Context, dec func(i return interceptor(ctx, in, info, handler) } +func _Msg_ValsetUpdateClaim_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgValsetUpdatedClaim) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).ValsetUpdateClaim(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/gravity.v1.Msg/ValsetUpdateClaim", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).ValsetUpdateClaim(ctx, req.(*MsgValsetUpdatedClaim)) + } + return interceptor(ctx, in, info, handler) +} + func _Msg_ERC20DeployedClaim_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(MsgERC20DeployedClaim) if err := dec(in); err != nil { @@ -1826,6 +1980,10 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ MethodName: "WithdrawClaim", Handler: _Msg_WithdrawClaim_Handler, }, + { + MethodName: "ValsetUpdateClaim", + Handler: _Msg_ValsetUpdateClaim_Handler, + }, { MethodName: "ERC20DeployedClaim", Handler: _Msg_ERC20DeployedClaim_Handler, @@ -2624,6 +2782,88 @@ func (m *MsgLogicCallExecutedClaimResponse) MarshalToSizedBuffer(dAtA []byte) (i return len(dAtA) - i, nil } +func (m *MsgValsetUpdatedClaim) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgValsetUpdatedClaim) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgValsetUpdatedClaim) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Orchestrator) > 0 { + i -= len(m.Orchestrator) + copy(dAtA[i:], m.Orchestrator) + i = encodeVarintMsgs(dAtA, i, uint64(len(m.Orchestrator))) + i-- + dAtA[i] = 0x32 + } + if len(m.Members) > 0 { + for iNdEx := len(m.Members) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Members[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMsgs(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if m.BlockHeight != 0 { + i = encodeVarintMsgs(dAtA, i, uint64(m.BlockHeight)) + i-- + dAtA[i] = 0x18 + } + if m.ValsetNonce != 0 { + i = encodeVarintMsgs(dAtA, i, uint64(m.ValsetNonce)) + i-- + dAtA[i] = 0x10 + } + if m.EventNonce != 0 { + i = encodeVarintMsgs(dAtA, i, uint64(m.EventNonce)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *MsgValsetUpdatedClaimResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgValsetUpdatedClaimResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgValsetUpdatedClaimResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + func (m *MsgCancelSendToEth) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -3045,6 +3285,43 @@ func (m *MsgLogicCallExecutedClaimResponse) Size() (n int) { return n } +func (m *MsgValsetUpdatedClaim) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.EventNonce != 0 { + n += 1 + sovMsgs(uint64(m.EventNonce)) + } + if m.ValsetNonce != 0 { + n += 1 + sovMsgs(uint64(m.ValsetNonce)) + } + if m.BlockHeight != 0 { + n += 1 + sovMsgs(uint64(m.BlockHeight)) + } + if len(m.Members) > 0 { + for _, e := range m.Members { + l = e.Size() + n += 1 + l + sovMsgs(uint64(l)) + } + } + l = len(m.Orchestrator) + if l > 0 { + n += 1 + l + sovMsgs(uint64(l)) + } + return n +} + +func (m *MsgValsetUpdatedClaimResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + func (m *MsgCancelSendToEth) Size() (n int) { if m == nil { return 0 @@ -5496,6 +5773,235 @@ func (m *MsgLogicCallExecutedClaimResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *MsgValsetUpdatedClaim) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgValsetUpdatedClaim: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgValsetUpdatedClaim: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field EventNonce", wireType) + } + m.EventNonce = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.EventNonce |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ValsetNonce", wireType) + } + m.ValsetNonce = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ValsetNonce |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field BlockHeight", wireType) + } + m.BlockHeight = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.BlockHeight |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Members", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMsgs + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Members = append(m.Members, &BridgeValidator{}) + if err := m.Members[len(m.Members)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Orchestrator", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthMsgs + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthMsgs + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Orchestrator = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMsgs(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthMsgs + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthMsgs + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgValsetUpdatedClaimResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMsgs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgValsetUpdatedClaimResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgValsetUpdatedClaimResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipMsgs(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthMsgs + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthMsgs + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *MsgCancelSendToEth) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/orchestrator/cosmos_gravity/src/send.rs b/orchestrator/cosmos_gravity/src/send.rs index bdffaa341..a902c3f57 100644 --- a/orchestrator/cosmos_gravity/src/send.rs +++ b/orchestrator/cosmos_gravity/src/send.rs @@ -21,6 +21,7 @@ use gravity_proto::gravity::MsgRequestBatch; use gravity_proto::gravity::MsgSendToEth; use gravity_proto::gravity::MsgSetOrchestratorAddress; use gravity_proto::gravity::MsgValsetConfirm; +use gravity_proto::gravity::MsgValsetUpdatedClaim; use gravity_proto::gravity::MsgWithdrawClaim; use gravity_utils::message_signatures::{ encode_logic_call_confirm, encode_tx_batch_confirm, encode_valset_confirm, @@ -267,6 +268,7 @@ pub async fn send_logic_call_confirm( .await } +#[allow(clippy::clippy::too_many_arguments)] pub async fn send_ethereum_claims( contact: &Contact, private_key: PrivateKey, @@ -274,6 +276,7 @@ pub async fn send_ethereum_claims( withdraws: Vec, erc20_deploys: Vec, logic_calls: Vec, + valsets: Vec, fee: Coin, ) -> Result { let our_address = private_key.to_address(&contact.get_prefix()).unwrap(); @@ -289,7 +292,7 @@ pub async fn send_ethereum_claims( let mut unordered_msgs = HashMap::new(); for deposit in deposits { let claim = MsgDepositClaim { - event_nonce: downcast_uint256(deposit.event_nonce.clone()).unwrap(), + event_nonce: deposit.event_nonce, block_height: downcast_uint256(deposit.block_height).unwrap(), token_contract: deposit.erc20.to_string(), amount: deposit.amount.to_string(), @@ -298,22 +301,22 @@ pub async fn send_ethereum_claims( orchestrator: our_address.to_string(), }; let msg = Msg::new("/gravity.v1.MsgDepositClaim", claim); - unordered_msgs.insert(deposit.event_nonce.clone(), msg); + unordered_msgs.insert(deposit.event_nonce, msg); } for withdraw in withdraws { let claim = MsgWithdrawClaim { - event_nonce: downcast_uint256(withdraw.event_nonce.clone()).unwrap(), + event_nonce: withdraw.event_nonce, block_height: downcast_uint256(withdraw.block_height).unwrap(), token_contract: withdraw.erc20.to_string(), - batch_nonce: downcast_uint256(withdraw.batch_nonce).unwrap(), + batch_nonce: withdraw.batch_nonce, orchestrator: our_address.to_string(), }; let msg = Msg::new("/gravity.v1.MsgWithdrawClaim", claim); - unordered_msgs.insert(withdraw.event_nonce.clone(), msg); + unordered_msgs.insert(withdraw.event_nonce, msg); } for deploy in erc20_deploys { let claim = MsgErc20DeployedClaim { - event_nonce: downcast_uint256(deploy.event_nonce.clone()).unwrap(), + event_nonce: deploy.event_nonce, block_height: downcast_uint256(deploy.block_height).unwrap(), cosmos_denom: deploy.cosmos_denom, token_contract: deploy.erc20_address.to_string(), @@ -323,24 +326,35 @@ pub async fn send_ethereum_claims( orchestrator: our_address.to_string(), }; let msg = Msg::new("/gravity.v1.MsgERC20DeployedClaim", claim); - unordered_msgs.insert(deploy.event_nonce.clone(), msg); + unordered_msgs.insert(deploy.event_nonce, msg); } for call in logic_calls { let claim = MsgLogicCallExecutedClaim { - event_nonce: downcast_uint256(call.event_nonce.clone()).unwrap(), + event_nonce: call.event_nonce, block_height: downcast_uint256(call.block_height).unwrap(), invalidation_id: call.invalidation_id, - invalidation_nonce: downcast_uint256(call.invalidation_nonce).unwrap(), + invalidation_nonce: call.invalidation_nonce, orchestrator: our_address.to_string(), }; let msg = Msg::new("/gravity.v1.MsgLogicCallExecutedClaim", claim); - unordered_msgs.insert(call.event_nonce.clone(), msg); + unordered_msgs.insert(call.event_nonce, msg); + } + for valset in valsets { + let claim = MsgValsetUpdatedClaim { + event_nonce: valset.event_nonce, + valset_nonce: valset.valset_nonce, + block_height: downcast_uint256(valset.block_height).unwrap(), + members: valset.members.iter().map(|v| v.into()).collect(), + orchestrator: our_address.to_string(), + }; + let msg = Msg::new("/gravity.v1.MsgValsetUpdatedClaim", claim); + unordered_msgs.insert(valset.event_nonce, msg); } let mut keys = Vec::new(); for (key, _) in unordered_msgs.iter() { - keys.push(key.clone()); + keys.push(*key); } - keys.sort(); + keys.sort_unstable(); let mut msgs = Vec::new(); for i in keys { diff --git a/orchestrator/gravity_proto/src/prost/gravity.v1.rs b/orchestrator/gravity_proto/src/prost/gravity.v1.rs index 7a7a4c2fb..e8bd2df3d 100644 --- a/orchestrator/gravity_proto/src/prost/gravity.v1.rs +++ b/orchestrator/gravity_proto/src/prost/gravity.v1.rs @@ -46,6 +46,7 @@ pub enum ClaimType { Withdraw = 2, Erc20Deployed = 3, LogicCallExecuted = 4, + ValsetUpdated = 5, } /// IDSet represents a set of IDs #[derive(Clone, PartialEq, ::prost::Message)] @@ -388,6 +389,24 @@ pub struct MsgLogicCallExecutedClaim { #[derive(Clone, PartialEq, ::prost::Message)] pub struct MsgLogicCallExecutedClaimResponse { } +/// This informs the Cosmos module that a validator +/// set has been updated. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MsgValsetUpdatedClaim { + #[prost(uint64, tag="1")] + pub event_nonce: u64, + #[prost(uint64, tag="2")] + pub valset_nonce: u64, + #[prost(uint64, tag="3")] + pub block_height: u64, + #[prost(message, repeated, tag="4")] + pub members: ::prost::alloc::vec::Vec, + #[prost(string, tag="6")] + pub orchestrator: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MsgValsetUpdatedClaimResponse { +} /// This call allows the sender (and only the sender) /// to cancel a given MsgSendToEth and recieve a refund /// of the tokens @@ -401,7 +420,7 @@ pub struct MsgCancelSendToEth { #[derive(Clone, PartialEq, ::prost::Message)] pub struct MsgCancelSendToEthResponse { } -# [doc = r" Generated client implementations."] pub mod msg_client { # ! [allow (unused_variables , dead_code , missing_docs)] use tonic :: codegen :: * ; # [doc = " Msg defines the state transitions possible within gravity"] pub struct MsgClient < T > { inner : tonic :: client :: Grpc < T > , } impl MsgClient < tonic :: transport :: Channel > { # [doc = r" Attempt to create a new client by connecting to a given endpoint."] pub async fn connect < D > (dst : D) -> Result < Self , tonic :: transport :: Error > where D : std :: convert :: TryInto < tonic :: transport :: Endpoint > , D :: Error : Into < StdError > , { let conn = tonic :: transport :: Endpoint :: new (dst) ? . connect () . await ? ; Ok (Self :: new (conn)) } } impl < T > MsgClient < T > where T : tonic :: client :: GrpcService < tonic :: body :: BoxBody > , T :: ResponseBody : Body + HttpBody + Send + 'static , T :: Error : Into < StdError > , < T :: ResponseBody as HttpBody > :: Error : Into < StdError > + Send , { pub fn new (inner : T) -> Self { let inner = tonic :: client :: Grpc :: new (inner) ; Self { inner } } pub fn with_interceptor (inner : T , interceptor : impl Into < tonic :: Interceptor >) -> Self { let inner = tonic :: client :: Grpc :: with_interceptor (inner , interceptor) ; Self { inner } } pub async fn valset_confirm (& mut self , request : impl tonic :: IntoRequest < super :: MsgValsetConfirm > ,) -> Result < tonic :: Response < super :: MsgValsetConfirmResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/gravity.v1.Msg/ValsetConfirm") ; self . inner . unary (request . into_request () , path , codec) . await } pub async fn send_to_eth (& mut self , request : impl tonic :: IntoRequest < super :: MsgSendToEth > ,) -> Result < tonic :: Response < super :: MsgSendToEthResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/gravity.v1.Msg/SendToEth") ; self . inner . unary (request . into_request () , path , codec) . await } pub async fn request_batch (& mut self , request : impl tonic :: IntoRequest < super :: MsgRequestBatch > ,) -> Result < tonic :: Response < super :: MsgRequestBatchResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/gravity.v1.Msg/RequestBatch") ; self . inner . unary (request . into_request () , path , codec) . await } pub async fn confirm_batch (& mut self , request : impl tonic :: IntoRequest < super :: MsgConfirmBatch > ,) -> Result < tonic :: Response < super :: MsgConfirmBatchResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/gravity.v1.Msg/ConfirmBatch") ; self . inner . unary (request . into_request () , path , codec) . await } pub async fn confirm_logic_call (& mut self , request : impl tonic :: IntoRequest < super :: MsgConfirmLogicCall > ,) -> Result < tonic :: Response < super :: MsgConfirmLogicCallResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/gravity.v1.Msg/ConfirmLogicCall") ; self . inner . unary (request . into_request () , path , codec) . await } pub async fn deposit_claim (& mut self , request : impl tonic :: IntoRequest < super :: MsgDepositClaim > ,) -> Result < tonic :: Response < super :: MsgDepositClaimResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/gravity.v1.Msg/DepositClaim") ; self . inner . unary (request . into_request () , path , codec) . await } pub async fn withdraw_claim (& mut self , request : impl tonic :: IntoRequest < super :: MsgWithdrawClaim > ,) -> Result < tonic :: Response < super :: MsgWithdrawClaimResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/gravity.v1.Msg/WithdrawClaim") ; self . inner . unary (request . into_request () , path , codec) . await } pub async fn erc20_deployed_claim (& mut self , request : impl tonic :: IntoRequest < super :: MsgErc20DeployedClaim > ,) -> Result < tonic :: Response < super :: MsgErc20DeployedClaimResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/gravity.v1.Msg/ERC20DeployedClaim") ; self . inner . unary (request . into_request () , path , codec) . await } pub async fn logic_call_executed_claim (& mut self , request : impl tonic :: IntoRequest < super :: MsgLogicCallExecutedClaim > ,) -> Result < tonic :: Response < super :: MsgLogicCallExecutedClaimResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/gravity.v1.Msg/LogicCallExecutedClaim") ; self . inner . unary (request . into_request () , path , codec) . await } pub async fn set_orchestrator_address (& mut self , request : impl tonic :: IntoRequest < super :: MsgSetOrchestratorAddress > ,) -> Result < tonic :: Response < super :: MsgSetOrchestratorAddressResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/gravity.v1.Msg/SetOrchestratorAddress") ; self . inner . unary (request . into_request () , path , codec) . await } pub async fn cancel_send_to_eth (& mut self , request : impl tonic :: IntoRequest < super :: MsgCancelSendToEth > ,) -> Result < tonic :: Response < super :: MsgCancelSendToEthResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/gravity.v1.Msg/CancelSendToEth") ; self . inner . unary (request . into_request () , path , codec) . await } } impl < T : Clone > Clone for MsgClient < T > { fn clone (& self) -> Self { Self { inner : self . inner . clone () , } } } impl < T > std :: fmt :: Debug for MsgClient < T > { fn fmt (& self , f : & mut std :: fmt :: Formatter < '_ >) -> std :: fmt :: Result { write ! (f , "MsgClient {{ ... }}") } } }/// Params represent the Gravity genesis and store parameters +# [doc = r" Generated client implementations."] pub mod msg_client { # ! [allow (unused_variables , dead_code , missing_docs)] use tonic :: codegen :: * ; # [doc = " Msg defines the state transitions possible within gravity"] pub struct MsgClient < T > { inner : tonic :: client :: Grpc < T > , } impl MsgClient < tonic :: transport :: Channel > { # [doc = r" Attempt to create a new client by connecting to a given endpoint."] pub async fn connect < D > (dst : D) -> Result < Self , tonic :: transport :: Error > where D : std :: convert :: TryInto < tonic :: transport :: Endpoint > , D :: Error : Into < StdError > , { let conn = tonic :: transport :: Endpoint :: new (dst) ? . connect () . await ? ; Ok (Self :: new (conn)) } } impl < T > MsgClient < T > where T : tonic :: client :: GrpcService < tonic :: body :: BoxBody > , T :: ResponseBody : Body + HttpBody + Send + 'static , T :: Error : Into < StdError > , < T :: ResponseBody as HttpBody > :: Error : Into < StdError > + Send , { pub fn new (inner : T) -> Self { let inner = tonic :: client :: Grpc :: new (inner) ; Self { inner } } pub fn with_interceptor (inner : T , interceptor : impl Into < tonic :: Interceptor >) -> Self { let inner = tonic :: client :: Grpc :: with_interceptor (inner , interceptor) ; Self { inner } } pub async fn valset_confirm (& mut self , request : impl tonic :: IntoRequest < super :: MsgValsetConfirm > ,) -> Result < tonic :: Response < super :: MsgValsetConfirmResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/gravity.v1.Msg/ValsetConfirm") ; self . inner . unary (request . into_request () , path , codec) . await } pub async fn send_to_eth (& mut self , request : impl tonic :: IntoRequest < super :: MsgSendToEth > ,) -> Result < tonic :: Response < super :: MsgSendToEthResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/gravity.v1.Msg/SendToEth") ; self . inner . unary (request . into_request () , path , codec) . await } pub async fn request_batch (& mut self , request : impl tonic :: IntoRequest < super :: MsgRequestBatch > ,) -> Result < tonic :: Response < super :: MsgRequestBatchResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/gravity.v1.Msg/RequestBatch") ; self . inner . unary (request . into_request () , path , codec) . await } pub async fn confirm_batch (& mut self , request : impl tonic :: IntoRequest < super :: MsgConfirmBatch > ,) -> Result < tonic :: Response < super :: MsgConfirmBatchResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/gravity.v1.Msg/ConfirmBatch") ; self . inner . unary (request . into_request () , path , codec) . await } pub async fn confirm_logic_call (& mut self , request : impl tonic :: IntoRequest < super :: MsgConfirmLogicCall > ,) -> Result < tonic :: Response < super :: MsgConfirmLogicCallResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/gravity.v1.Msg/ConfirmLogicCall") ; self . inner . unary (request . into_request () , path , codec) . await } pub async fn deposit_claim (& mut self , request : impl tonic :: IntoRequest < super :: MsgDepositClaim > ,) -> Result < tonic :: Response < super :: MsgDepositClaimResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/gravity.v1.Msg/DepositClaim") ; self . inner . unary (request . into_request () , path , codec) . await } pub async fn withdraw_claim (& mut self , request : impl tonic :: IntoRequest < super :: MsgWithdrawClaim > ,) -> Result < tonic :: Response < super :: MsgWithdrawClaimResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/gravity.v1.Msg/WithdrawClaim") ; self . inner . unary (request . into_request () , path , codec) . await } pub async fn valset_update_claim (& mut self , request : impl tonic :: IntoRequest < super :: MsgValsetUpdatedClaim > ,) -> Result < tonic :: Response < super :: MsgValsetUpdatedClaimResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/gravity.v1.Msg/ValsetUpdateClaim") ; self . inner . unary (request . into_request () , path , codec) . await } pub async fn erc20_deployed_claim (& mut self , request : impl tonic :: IntoRequest < super :: MsgErc20DeployedClaim > ,) -> Result < tonic :: Response < super :: MsgErc20DeployedClaimResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/gravity.v1.Msg/ERC20DeployedClaim") ; self . inner . unary (request . into_request () , path , codec) . await } pub async fn logic_call_executed_claim (& mut self , request : impl tonic :: IntoRequest < super :: MsgLogicCallExecutedClaim > ,) -> Result < tonic :: Response < super :: MsgLogicCallExecutedClaimResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/gravity.v1.Msg/LogicCallExecutedClaim") ; self . inner . unary (request . into_request () , path , codec) . await } pub async fn set_orchestrator_address (& mut self , request : impl tonic :: IntoRequest < super :: MsgSetOrchestratorAddress > ,) -> Result < tonic :: Response < super :: MsgSetOrchestratorAddressResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/gravity.v1.Msg/SetOrchestratorAddress") ; self . inner . unary (request . into_request () , path , codec) . await } pub async fn cancel_send_to_eth (& mut self , request : impl tonic :: IntoRequest < super :: MsgCancelSendToEth > ,) -> Result < tonic :: Response < super :: MsgCancelSendToEthResponse > , tonic :: Status > { self . inner . ready () . await . map_err (| e | { tonic :: Status :: new (tonic :: Code :: Unknown , format ! ("Service was not ready: {}" , e . into ())) }) ? ; let codec = tonic :: codec :: ProstCodec :: default () ; let path = http :: uri :: PathAndQuery :: from_static ("/gravity.v1.Msg/CancelSendToEth") ; self . inner . unary (request . into_request () , path , codec) . await } } impl < T : Clone > Clone for MsgClient < T > { fn clone (& self) -> Self { Self { inner : self . inner . clone () , } } } impl < T > std :: fmt :: Debug for MsgClient < T > { fn fmt (& self , f : & mut std :: fmt :: Formatter < '_ >) -> std :: fmt :: Result { write ! (f , "MsgClient {{ ... }}") } } }/// Params represent the Gravity genesis and store parameters /// gravity_id: /// a random 32 byte value to prevent signature reuse, for example if the /// cosmos validators decided to use the same Ethereum keys for another chain diff --git a/orchestrator/gravity_utils/src/types/ethereum_events.rs b/orchestrator/gravity_utils/src/types/ethereum_events.rs index ab5e0511e..85ccc3644 100644 --- a/orchestrator/gravity_utils/src/types/ethereum_events.rs +++ b/orchestrator/gravity_utils/src/types/ethereum_events.rs @@ -1,17 +1,25 @@ -use std::unimplemented; +//! This file parses the Gravity contract ethereum events. Note that there is no Ethereum ABI unpacking implementation. Instead each event +//! is parsed directly from it's binary representation. This is technical debt within this implementation. It's quite easy to parse any +//! individual event manually but a generic decoder can be quite challenging to implement. A proper implementation would probably closely +//! mirror Serde and perhaps even become a serde crate for Ethereum ABI decoding +//! For now reference the ABI encoding document here https://docs.soliditylang.org/en/v0.8.3/abi-spec.html use super::ValsetMember; use crate::error::GravityError; use clarity::Address as EthAddress; +use deep_space::utils::bytes_to_hex_str; use deep_space::Address as CosmosAddress; use num256::Uint256; +use std::unimplemented; use web30::types::Log; /// A parsed struct representing the Ethereum event fired by the Gravity contract /// when the validator set is updated. #[derive(Serialize, Deserialize, Debug, Default, Clone, Eq, PartialEq, Hash)] pub struct ValsetUpdatedEvent { - pub nonce: u64, + pub valset_nonce: u64, + pub event_nonce: u64, + pub block_height: Uint256, pub members: Vec, } @@ -19,24 +27,37 @@ impl ValsetUpdatedEvent { /// This function is not an abi compatible bytes parser, but it's actually /// not hard at all to extract data like this by hand. pub fn from_log(input: &Log) -> Result { - // we have one indexed event so we should fine two indexes, one the event itself + // we have one indexed event so we should fined two indexes, one the event itself // and one the indexed nonce if input.topics.get(1).is_none() { return Err(GravityError::InvalidEventLogError( "Too few topics".to_string(), )); } - let nonce_data = &input.topics[1]; - let nonce = Uint256::from_bytes_be(nonce_data); - if nonce > u64::MAX.into() { + let valset_nonce_data = &input.topics[1]; + let valset_nonce = Uint256::from_bytes_be(valset_nonce_data); + if valset_nonce > u64::MAX.into() { return Err(GravityError::InvalidEventLogError( "Nonce overflow, probably incorrect parsing".to_string(), )); } - let nonce: u64 = nonce.to_string().parse().unwrap(); - // first two indexes contain event info we don't care about, third index is - // the length of the eth addresses array - let index_start = 2 * 32; + let valset_nonce: u64 = valset_nonce.to_string().parse().unwrap(); + + // first index is the event nonce, following two have event data we don't + // care about, fourth index contains the length of the eth address array + let index_start = 0; + let index_end = index_start + 32; + let nonce_data = &input.data[index_start..index_end]; + let event_nonce = Uint256::from_bytes_be(nonce_data); + if event_nonce > u64::MAX.into() { + return Err(GravityError::InvalidEventLogError( + "Nonce overflow, probably incorrect parsing".to_string(), + )); + } + let event_nonce: u64 = event_nonce.to_string().parse().unwrap(); + // first index is the event nonce, following two have event data we don't + // care about, fourth index contains the length of the eth address array + let index_start = 3 * 32; let index_end = index_start + 32; let eth_addresses_offset = index_start + 32; let len_eth_addresses = Uint256::from_bytes_be(&input.data[index_start..index_end]); @@ -46,7 +67,7 @@ impl ValsetUpdatedEvent { )); } let len_eth_addresses: usize = len_eth_addresses.to_string().parse().unwrap(); - let index_start = (3 + len_eth_addresses) * 32; + let index_start = (4 + len_eth_addresses) * 32; let index_end = index_start + 32; let powers_offset = index_start + 32; let len_powers = Uint256::from_bytes_be(&input.data[index_start..index_end]); @@ -96,8 +117,19 @@ impl ValsetUpdatedEvent { ); } + let block_height = if let Some(bn) = input.block_number.clone() { + bn + } else { + return Err(GravityError::InvalidEventLogError( + "Log does not have block number, we only search logs already in blocks?" + .to_string(), + )); + }; + Ok(ValsetUpdatedEvent { - nonce, + valset_nonce, + event_nonce, + block_height, members: validators, }) } @@ -108,6 +140,17 @@ impl ValsetUpdatedEvent { } Ok(res) } + /// returns all values in the array with event nonces greater + /// than the provided value + pub fn filter_by_event_nonce(event_nonce: u64, input: &[Self]) -> Vec { + let mut ret = Vec::new(); + for item in input { + if item.event_nonce > event_nonce { + ret.push(item.clone()) + } + } + ret + } } /// A parsed struct representing the Ethereum event fired by the Gravity contract when @@ -116,7 +159,7 @@ impl ValsetUpdatedEvent { pub struct TransactionBatchExecutedEvent { /// the nonce attached to the transaction batch that follows /// it throughout it's lifecycle - pub batch_nonce: Uint256, + pub batch_nonce: u64, /// The block height this event occurred at pub block_height: Uint256, /// The ERC20 token contract address for the batch executed, since batches are uniform @@ -125,7 +168,7 @@ pub struct TransactionBatchExecutedEvent { /// the event nonce representing a unique ordering of events coming out /// of the Gravity solidity contract. Ensuring that these events can only be played /// back in order - pub event_nonce: Uint256, + pub event_nonce: u64, } impl TransactionBatchExecutedEvent { @@ -152,6 +195,8 @@ impl TransactionBatchExecutedEvent { "Event nonce overflow, probably incorrect parsing".to_string(), )) } else { + let batch_nonce: u64 = batch_nonce.to_string().parse().unwrap(); + let event_nonce: u64 = event_nonce.to_string().parse().unwrap(); Ok(TransactionBatchExecutedEvent { batch_nonce, block_height, @@ -177,7 +222,7 @@ impl TransactionBatchExecutedEvent { pub fn filter_by_event_nonce(event_nonce: u64, input: &[Self]) -> Vec { let mut ret = Vec::new(); for item in input { - if item.event_nonce > event_nonce.into() { + if item.event_nonce > event_nonce { ret.push(item.clone()) } } @@ -198,7 +243,7 @@ pub struct SendToCosmosEvent { /// The amount of the erc20 token that is being sent pub amount: Uint256, /// The transaction's nonce, used to make sure there can be no accidental duplication - pub event_nonce: Uint256, + pub event_nonce: u64, /// The block height this event occurred at pub block_height: Uint256, } @@ -234,6 +279,7 @@ impl SendToCosmosEvent { "Event nonce overflow, probably incorrect parsing".to_string(), )) } else { + let event_nonce: u64 = event_nonce.to_string().parse().unwrap(); Ok(SendToCosmosEvent { erc20, sender, @@ -261,7 +307,7 @@ impl SendToCosmosEvent { pub fn filter_by_event_nonce(event_nonce: u64, input: &[Self]) -> Vec { let mut ret = Vec::new(); for item in input { - if item.event_nonce > event_nonce.into() { + if item.event_nonce > event_nonce { ret.push(item.clone()) } } @@ -285,7 +331,7 @@ pub struct Erc20DeployedEvent { pub symbol: String, /// The number of decimals required to represent the smallest unit of this token pub decimals: u8, - pub event_nonce: Uint256, + pub event_nonce: u64, pub block_height: Uint256, } @@ -312,6 +358,7 @@ impl Erc20DeployedEvent { "Nonce overflow, probably incorrect parsing".to_string(), )); } + let event_nonce: u64 = nonce.to_string().parse().unwrap(); let index_start = 5 * 32; let index_end = index_start + 32; @@ -398,7 +445,7 @@ impl Erc20DeployedEvent { cosmos_denom: denom, name: erc20_name, decimals, - event_nonce: nonce, + event_nonce, erc20_address: erc20, symbol, block_height, @@ -421,7 +468,7 @@ impl Erc20DeployedEvent { pub fn filter_by_event_nonce(event_nonce: u64, input: &[Self]) -> Vec { let mut ret = Vec::new(); for item in input { - if item.event_nonce > event_nonce.into() { + if item.event_nonce > event_nonce { ret.push(item.clone()) } } @@ -433,9 +480,9 @@ impl Erc20DeployedEvent { #[derive(Serialize, Deserialize, Debug, Default, Clone, Eq, PartialEq, Hash)] pub struct LogicCallExecutedEvent { pub invalidation_id: Vec, - pub invalidation_nonce: Uint256, + pub invalidation_nonce: u64, pub return_data: Vec, - pub event_nonce: Uint256, + pub event_nonce: u64, pub block_height: Uint256, } @@ -455,10 +502,21 @@ impl LogicCallExecutedEvent { pub fn filter_by_event_nonce(event_nonce: u64, input: &[Self]) -> Vec { let mut ret = Vec::new(); for item in input { - if item.event_nonce > event_nonce.into() { + if item.event_nonce > event_nonce { ret.push(item.clone()) } } ret } } + +/// Function used for debug printing hex dumps +/// of ethereum events +fn _debug_print_data(input: &[u8]) { + let count = input.len() / 32; + println!("data hex dump"); + for i in 0..count { + println!("0x{}", bytes_to_hex_str(&input[(i * 32)..((i * 32) + 32)])) + } + println!("end dump"); +} diff --git a/orchestrator/gravity_utils/src/types/event_signatures.rs b/orchestrator/gravity_utils/src/types/event_signatures.rs new file mode 100644 index 000000000..b188086c5 --- /dev/null +++ b/orchestrator/gravity_utils/src/types/event_signatures.rs @@ -0,0 +1,13 @@ +pub const TRANSACTION_BATCH_EXECUTED_EVENT_SIG: &str = + "TransactionBatchExecutedEvent(uint256,address,uint256)"; + +pub const SENT_TO_COSMOS_EVENT_SIG: &str = + "SendToCosmosEvent(address,address,bytes32,uint256,uint256)"; + +pub const ERC20_DEPLOYED_EVENT_SIG: &str = + "ERC20DeployedEvent(string,address,string,string,uint8,uint256)"; + +pub const LOGIC_CALL_EVENT_SIG: &str = "LogicCallEvent(bytes32,uint256,bytes,uint256)"; + +pub const VALSET_UPDATED_EVENT_SIG: &str = + "ValsetUpdatedEvent(uint256,uint256,address[],uint256[])"; diff --git a/orchestrator/gravity_utils/src/types/mod.rs b/orchestrator/gravity_utils/src/types/mod.rs index 83021d3b9..ca62b9174 100644 --- a/orchestrator/gravity_utils/src/types/mod.rs +++ b/orchestrator/gravity_utils/src/types/mod.rs @@ -2,6 +2,7 @@ use clarity::Address as EthAddress; use num256::Uint256; mod batches; mod ethereum_events; +pub mod event_signatures; mod logic_call; mod signatures; mod valsets; diff --git a/orchestrator/gravity_utils/src/types/valsets.rs b/orchestrator/gravity_utils/src/types/valsets.rs index 401ca8076..ce3228328 100644 --- a/orchestrator/gravity_utils/src/types/valsets.rs +++ b/orchestrator/gravity_utils/src/types/valsets.rs @@ -419,3 +419,16 @@ impl From<&gravity_proto::gravity::BridgeValidator> for ValsetMember { } } } + +impl From<&ValsetMember> for gravity_proto::gravity::BridgeValidator { + fn from(input: &ValsetMember) -> gravity_proto::gravity::BridgeValidator { + let ethereum_address = match input.eth_address { + Some(e) => e.to_string(), + None => String::new(), + }; + gravity_proto::gravity::BridgeValidator { + power: input.power, + ethereum_address, + } + } +} diff --git a/orchestrator/orchestrator/src/ethereum_event_watcher.rs b/orchestrator/orchestrator/src/ethereum_event_watcher.rs index 709ce58e8..fa8a8a0ea 100644 --- a/orchestrator/orchestrator/src/ethereum_event_watcher.rs +++ b/orchestrator/orchestrator/src/ethereum_event_watcher.rs @@ -6,6 +6,7 @@ use cosmos_gravity::{query::get_last_event_nonce, send::send_ethereum_claims}; use deep_space::Contact; use deep_space::{coin::Coin, private_key::PrivateKey as CosmosPrivateKey}; use gravity_proto::gravity::query_client::QueryClient as GravityQueryClient; +use gravity_utils::types::event_signatures::*; use gravity_utils::{ error::GravityError, types::{ @@ -38,7 +39,7 @@ pub async fn check_for_events( starting_block.clone(), Some(latest_block.clone()), vec![gravity_contract_address], - vec!["SendToCosmosEvent(address,address,bytes32,uint256,uint256)"], + vec![SENT_TO_COSMOS_EVENT_SIG], ) .await; trace!("Deposits {:?}", deposits); @@ -48,7 +49,7 @@ pub async fn check_for_events( starting_block.clone(), Some(latest_block.clone()), vec![gravity_contract_address], - vec!["TransactionBatchExecutedEvent(uint256,address,uint256)"], + vec![TRANSACTION_BATCH_EXECUTED_EVENT_SIG], ) .await; trace!("Batches {:?}", batches); @@ -58,7 +59,7 @@ pub async fn check_for_events( starting_block.clone(), Some(latest_block.clone()), vec![gravity_contract_address], - vec!["ValsetUpdatedEvent(uint256,address[],uint256[])"], + vec![VALSET_UPDATED_EVENT_SIG], ) .await; trace!("Valsets {:?}", valsets); @@ -68,7 +69,7 @@ pub async fn check_for_events( starting_block.clone(), Some(latest_block.clone()), vec![gravity_contract_address], - vec!["ERC20DeployedEvent(string,address,string,string,uint8,uint256)"], + vec![ERC20_DEPLOYED_EVENT_SIG], ) .await; trace!("ERC20 Deployments {:?}", erc20_deployed); @@ -78,7 +79,7 @@ pub async fn check_for_events( starting_block.clone(), Some(latest_block.clone()), vec![gravity_contract_address], - vec!["LogicCallEvent(bytes32,uint256,bytes,uint256)"], + vec![LOGIC_CALL_EVENT_SIG], ) .await; trace!("Logic call executions {:?}", logic_call_executed); @@ -107,6 +108,7 @@ pub async fn check_for_events( // multi event block again. In theory we only send all events for every block and that will pass of fail // atomicly but lets not take that risk. let last_event_nonce = get_last_event_nonce(grpc_client, our_cosmos_address).await?; + let valsets = ValsetUpdatedEvent::filter_by_event_nonce(last_event_nonce, &valsets); let deposits = SendToCosmosEvent::filter_by_event_nonce(last_event_nonce, &deposits); let withdraws = TransactionBatchExecutedEvent::filter_by_event_nonce(last_event_nonce, &withdraws); @@ -115,6 +117,12 @@ pub async fn check_for_events( let logic_calls = LogicCallExecutedEvent::filter_by_event_nonce(last_event_nonce, &logic_calls); + if !valsets.is_empty() { + info!( + "Oracle observed Valset update with nonce {} and event nonce {}", + valsets[0].valset_nonce, valsets[0].event_nonce + ) + } if !deposits.is_empty() { info!( "Oracle observed deposit with sender {}, destination {}, amount {}, and event nonce {}", @@ -154,6 +162,7 @@ pub async fn check_for_events( withdraws, erc20_deploys, logic_calls, + valsets, fee, ) .await?; diff --git a/orchestrator/orchestrator/src/oracle_resync.rs b/orchestrator/orchestrator/src/oracle_resync.rs index eda23354f..3b27b124d 100644 --- a/orchestrator/orchestrator/src/oracle_resync.rs +++ b/orchestrator/orchestrator/src/oracle_resync.rs @@ -12,6 +12,7 @@ use web30::client::Web3; use crate::get_with_retry::get_block_number_with_retry; use crate::get_with_retry::get_last_event_nonce_with_retry; use crate::get_with_retry::RETRY_TIME; +use gravity_utils::types::event_signatures::*; /// This function retrieves the last event nonce this oracle has relayed to Cosmos /// it then uses the Ethereum indexes to determine what block the last entry @@ -54,7 +55,7 @@ pub async fn get_last_checked_block( end_search.clone(), Some(current_block.clone()), vec![gravity_contract_address], - vec!["TransactionBatchExecutedEvent(uint256,address,uint256)"], + vec![TRANSACTION_BATCH_EXECUTED_EVENT_SIG], ) .await; let send_to_cosmos_events = web3 @@ -62,7 +63,7 @@ pub async fn get_last_checked_block( end_search.clone(), Some(current_block.clone()), vec![gravity_contract_address], - vec!["SendToCosmosEvent(address,address,bytes32,uint256,uint256)"], + vec![SENT_TO_COSMOS_EVENT_SIG], ) .await; let erc20_deployed_events = web3 @@ -70,7 +71,7 @@ pub async fn get_last_checked_block( end_search.clone(), Some(current_block.clone()), vec![gravity_contract_address], - vec!["ERC20DeployedEvent(string,address,string,string,uint8,uint256)"], + vec![ERC20_DEPLOYED_EVENT_SIG], ) .await; let logic_call_executed_events = web3 @@ -78,7 +79,7 @@ pub async fn get_last_checked_block( end_search.clone(), Some(current_block.clone()), vec![gravity_contract_address], - vec!["LogicCallEvent(bytes32,uint256,bytes,uint256)"], + vec![LOGIC_CALL_EVENT_SIG], ) .await; @@ -93,7 +94,7 @@ pub async fn get_last_checked_block( end_search.clone(), Some(current_block.clone()), vec![gravity_contract_address], - vec!["ValsetUpdatedEvent(uint256,address[],uint256[])"], + vec![VALSET_UPDATED_EVENT_SIG], ) .await; if batch_events.is_err() @@ -119,7 +120,8 @@ pub async fn get_last_checked_block( for event in batch_events { match TransactionBatchExecutedEvent::from_log(&event) { Ok(batch) => { - if batch.event_nonce == last_event_nonce && event.block_number.is_some() { + if upcast(batch.event_nonce) == last_event_nonce && event.block_number.is_some() + { return event.block_number.unwrap(); } } @@ -129,7 +131,8 @@ pub async fn get_last_checked_block( for event in send_to_cosmos_events { match SendToCosmosEvent::from_log(&event) { Ok(send) => { - if send.event_nonce == last_event_nonce && event.block_number.is_some() { + if upcast(send.event_nonce) == last_event_nonce && event.block_number.is_some() + { return event.block_number.unwrap(); } } @@ -139,7 +142,9 @@ pub async fn get_last_checked_block( for event in erc20_deployed_events { match Erc20DeployedEvent::from_log(&event) { Ok(deploy) => { - if deploy.event_nonce == last_event_nonce && event.block_number.is_some() { + if upcast(deploy.event_nonce) == last_event_nonce + && event.block_number.is_some() + { return event.block_number.unwrap(); } } @@ -149,7 +154,8 @@ pub async fn get_last_checked_block( for event in logic_call_executed_events { match LogicCallExecutedEvent::from_log(&event) { Ok(call) => { - if call.event_nonce == last_event_nonce && event.block_number.is_some() { + if upcast(call.event_nonce) == last_event_nonce && event.block_number.is_some() + { return event.block_number.unwrap(); } } @@ -162,12 +168,12 @@ pub async fn get_last_checked_block( // if we've found this event it is the first possible event from the contract // no other events can come before it, therefore either there's been a parsing error // or no events have been submitted on this chain yet. - if valset.nonce == 0 && last_event_nonce == 1u8.into() { - return latest_block; + if valset.valset_nonce == 0 && last_event_nonce == 1u8.into() { + return event.block_number.unwrap(); } // if we're looking for a later event nonce and we find the deployment of the contract // we must have failed to parse the event we're looking for. The oracle can not start - if valset.nonce == 0 && last_event_nonce > 1u8.into() { + else if valset.valset_nonce == 0 && last_event_nonce > 1u8.into() { panic!("Could not find the last event relayed by {}, Last Event nonce is {} but no event matching that could be found!", our_cosmos_address, last_event_nonce) } } @@ -177,5 +183,11 @@ pub async fn get_last_checked_block( current_block = end_search; } + // we should exit above when we find the zero valset, if we have the wrong contract address through we could be at it a while as we go over + // the entire history to 'prove' it. panic!("You have reached the end of block history without finding the Gravity contract deploy event! You must have the wrong contract address!"); } + +fn upcast(input: u64) -> Uint256 { + input.into() +} diff --git a/orchestrator/relayer/src/find_latest_valset.rs b/orchestrator/relayer/src/find_latest_valset.rs index b834ac82b..b83373a4c 100644 --- a/orchestrator/relayer/src/find_latest_valset.rs +++ b/orchestrator/relayer/src/find_latest_valset.rs @@ -2,6 +2,7 @@ use clarity::Address as EthAddress; use clarity::{Address, Uint256}; use ethereum_gravity::utils::get_valset_nonce; use gravity_proto::gravity::query_client::QueryClient as GravityQueryClient; +use gravity_utils::types::event_signatures::*; use gravity_utils::types::ValsetUpdatedEvent; use gravity_utils::{error::GravityError, types::Valset}; use tonic::transport::Channel; @@ -41,7 +42,7 @@ pub async fn find_latest_valset( end_search.clone(), Some(current_block.clone()), vec![gravity_contract_address], - vec!["ValsetUpdatedEvent(uint256,address[],uint256[])"], + vec![VALSET_UPDATED_EVENT_SIG], ) .await?; // by default the lowest found valset goes first, we want the highest. @@ -55,7 +56,7 @@ pub async fn find_latest_valset( match ValsetUpdatedEvent::from_log(event) { Ok(event) => { let valset = Valset { - nonce: event.nonce, + nonce: event.valset_nonce, members: event.members, }; check_if_valsets_differ(cosmos_chain_valset, &valset); diff --git a/orchestrator/test_runner/src/happy_path.rs b/orchestrator/test_runner/src/happy_path.rs index c72c8fed7..ed7bbbbc9 100644 --- a/orchestrator/test_runner/src/happy_path.rs +++ b/orchestrator/test_runner/src/happy_path.rs @@ -85,7 +85,7 @@ pub async fn happy_path_test( // this test may have false positives if the timeout is not // long enough. TODO check for an error on the cosmos send response submit_duplicate_erc20_send( - 1u64.into(), + 1u64, &contact, erc20_address, 1u64.into(), @@ -425,7 +425,7 @@ async fn test_batch( // already been submitted to test the nonce functionality. #[allow(clippy::too_many_arguments)] async fn submit_duplicate_erc20_send( - nonce: Uint256, + nonce: u64, contact: &Contact, erc20_address: EthAddress, amount: Uint256, @@ -459,6 +459,7 @@ async fn submit_duplicate_erc20_send( vec![], vec![], vec![], + vec![], get_fee(), ) .await diff --git a/solidity/contracts/Gravity.sol b/solidity/contracts/Gravity.sol index dcc7ce54b..f000be684 100644 --- a/solidity/contracts/Gravity.sol +++ b/solidity/contracts/Gravity.sol @@ -35,7 +35,9 @@ contract Gravity is ReentrancyGuard { mapping(address => uint256) public state_lastBatchNonces; mapping(bytes32 => uint256) public state_invalidationMapping; uint256 public state_lastValsetNonce = 0; - uint256 public state_lastEventNonce = 0; + // event nonce zero is reserved by the Cosmos module as a special + // value indicating that no events have yet been submitted + uint256 public state_lastEventNonce = 1; // These are set once at initialization bytes32 public state_gravityId; @@ -70,6 +72,7 @@ contract Gravity is ReentrancyGuard { ); event ValsetUpdatedEvent( uint256 indexed _newValsetNonce, + uint256 _eventNonce, address[] _validators, uint256[] _powers ); @@ -116,6 +119,7 @@ contract Gravity is ReentrancyGuard { function lastBatchNonce(address _erc20Address) public view returns (uint256) { return state_lastBatchNonces[_erc20Address]; } + function lastLogicCallNonce(bytes32 _invalidation_id) public view returns (uint256) { return state_invalidationMapping[_invalidation_id]; } @@ -273,7 +277,8 @@ contract Gravity is ReentrancyGuard { // LOGS - emit ValsetUpdatedEvent(_newValsetNonce, _newValidators, _newPowers); + state_lastEventNonce = state_lastEventNonce.add(1); + emit ValsetUpdatedEvent(_newValsetNonce, state_lastEventNonce, _newValidators, _newPowers); } // submitBatch processes a batch of Cosmos -> Ethereum transactions by sending the tokens in the transactions @@ -298,7 +303,7 @@ contract Gravity is ReentrancyGuard { // a block height beyond which this batch is not valid // used to provide a fee-free timeout uint256 _batchTimeout - ) nonReentrant public { + ) public nonReentrant { // CHECKS scoped to reduce stack depth { // Check that the batch nonce is higher than the last nonce for this token @@ -407,7 +412,7 @@ contract Gravity is ReentrancyGuard { bytes32[] memory _r, bytes32[] memory _s, LogicCallArgs memory _args - ) public nonReentrant{ + ) public nonReentrant { // CHECKS scoped to reduce stack depth { // Check that the call has not timed out @@ -592,6 +597,6 @@ contract Gravity is ReentrancyGuard { // LOGS - emit ValsetUpdatedEvent(0, _validators, _powers); + emit ValsetUpdatedEvent(state_lastValsetNonce, state_lastEventNonce, _validators, _powers); } } diff --git a/solidity/test/deployERC20.ts b/solidity/test/deployERC20.ts index 75320833e..d28b0de6d 100644 --- a/solidity/test/deployERC20.ts +++ b/solidity/test/deployERC20.ts @@ -63,7 +63,7 @@ async function runTest(opts: {}) { _name: 'Atom', _symbol: 'ATOM', _decimals: 6, - _eventNonce: BigNumber.from(1) + _eventNonce: BigNumber.from(2) }) diff --git a/solidity/test/sendToCosmos.ts b/solidity/test/sendToCosmos.ts index 3ff46c880..8be707fe2 100644 --- a/solidity/test/sendToCosmos.ts +++ b/solidity/test/sendToCosmos.ts @@ -45,11 +45,11 @@ async function runTest(opts: {}) { await signers[0].getAddress(), ethers.utils.formatBytes32String("myCosmosAddress"), 1000, - 1 + 2 ); expect((await testERC20.functions.balanceOf(gravity.address))[0]).to.equal(1000); - expect((await gravity.functions.state_lastEventNonce())[0]).to.equal(1); + expect((await gravity.functions.state_lastEventNonce())[0]).to.equal(2); @@ -65,11 +65,11 @@ async function runTest(opts: {}) { await signers[0].getAddress(), ethers.utils.formatBytes32String("myCosmosAddress"), 1000, - 2 + 3 ); expect((await testERC20.functions.balanceOf(gravity.address))[0]).to.equal(2000); - expect((await gravity.functions.state_lastEventNonce())[0]).to.equal(2); + expect((await gravity.functions.state_lastEventNonce())[0]).to.equal(3); } describe("sendToCosmos tests", function () {