From b1233ff1f7e62278381e346072553268b92be683 Mon Sep 17 00:00:00 2001 From: Vitalis Salis Date: Thu, 7 Jul 2022 17:05:42 +0300 Subject: [PATCH 01/16] Add TipUpdated event and hook --- app/app.go | 4 +- proto/babylon/btclightclient/event.proto | 10 + x/btclightclient/keeper/hooks.go | 16 ++ x/btclightclient/keeper/keeper.go | 20 +- x/btclightclient/keeper/state.go | 13 +- x/btclightclient/types/event.pb.go | 301 +++++++++++++++++++++ x/btclightclient/types/expected_keepers.go | 4 + x/btclightclient/types/hooks.go | 17 ++ 8 files changed, 378 insertions(+), 7 deletions(-) create mode 100644 proto/babylon/btclightclient/event.proto create mode 100644 x/btclightclient/keeper/hooks.go create mode 100644 x/btclightclient/types/event.pb.go create mode 100644 x/btclightclient/types/hooks.go diff --git a/app/app.go b/app/app.go index 8ed6825fd..191ad30fd 100644 --- a/app/app.go +++ b/app/app.go @@ -330,7 +330,9 @@ func NewBabylonApp( epochingKeeper.SetMsgServiceRouter(app.BaseApp.MsgServiceRouter()) app.EpochingKeeper = epochingKeeper - app.BTCLightClientKeeper = *btclightclientkeeper.NewKeeper(appCodec, keys[btclightclienttypes.StoreKey], keys[btclightclienttypes.MemStoreKey], app.GetSubspace(btclightclienttypes.ModuleName)) + btclightclientKeeper := *btclightclientkeeper.NewKeeper(appCodec, keys[btclightclienttypes.StoreKey], keys[btclightclienttypes.MemStoreKey], app.GetSubspace(btclightclienttypes.ModuleName)) + btclightclientKeeper.SetHooks(btclightclienttypes.NewMultiBTCLightClientHooks()) + app.BTCLightClientKeeper = btclightclientKeeper // TODO for now use mocks, as soon as Checkpoining and lightClient will have correct interfaces // change to correct implementations diff --git a/proto/babylon/btclightclient/event.proto b/proto/babylon/btclightclient/event.proto new file mode 100644 index 000000000..ca7db2260 --- /dev/null +++ b/proto/babylon/btclightclient/event.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; +package babylon.btclightclient.v1; + +option go_package = "github.com/babylonchain/babylon/x/btclightclient/types"; + +// EventTipUpdated is emitted on Msg/InsertHeader +message EventTipUpdated { + // The height of the new tip + uint64 height = 1; +} diff --git a/x/btclightclient/keeper/hooks.go b/x/btclightclient/keeper/hooks.go new file mode 100644 index 000000000..1b89ad1f4 --- /dev/null +++ b/x/btclightclient/keeper/hooks.go @@ -0,0 +1,16 @@ +package keeper + +import ( + "github.com/babylonchain/babylon/x/btclightclient/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// Implements BTCLightClientHooks interface +var _ types.BTCLightClientHooks = Keeper{} + +// AfterTipUpdated - call hook if registered +func (k Keeper) AfterTipUpdated(ctx sdk.Context, height uint64) { + if k.hooks != nil { + k.hooks.AfterTipUpdated(ctx, height) + } +} diff --git a/x/btclightclient/keeper/keeper.go b/x/btclightclient/keeper/keeper.go index 1d11d18bf..b80ab99d1 100644 --- a/x/btclightclient/keeper/keeper.go +++ b/x/btclightclient/keeper/keeper.go @@ -17,6 +17,7 @@ type ( cdc codec.BinaryCodec storeKey sdk.StoreKey memKey sdk.StoreKey + hooks types.BTCLightClientHooks paramstore paramtypes.Subspace } ) @@ -37,6 +38,7 @@ func NewKeeper( cdc: cdc, storeKey: storeKey, memKey: memKey, + hooks: nil, paramstore: ps, } } @@ -45,6 +47,16 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) } +// SetHooks sets the btclightclient hooks +func (k *Keeper) SetHooks(bh types.BTCLightClientHooks) *Keeper { + if k.hooks != nil { + panic("cannot set btclightclient hooks twice") + } + k.hooks = bh + + return k +} + // InsertHeader inserts a btcd header into the header state func (k Keeper) InsertHeader(ctx sdk.Context, header *wire.BlockHeader) error { headerHash := header.BlockHash() @@ -74,6 +86,12 @@ func (k Keeper) InsertHeader(ctx sdk.Context, header *wire.BlockHeader) error { cumulativeWork := types.CumulativeWork(headerWork, parentWork) // Create the header - k.HeadersState(ctx).CreateHeader(header, height+1, cumulativeWork) + tipUpdated := k.HeadersState(ctx).CreateHeader(header, height+1, cumulativeWork) + if tipUpdated { + // Trigger TipUpdated hook + k.AfterTipUpdated(ctx, height+1) + // Emit TipUpdated event + ctx.EventManager().EmitTypedEvent(&types.EventChainExtended{Height: height + 1}) + } return nil } diff --git a/x/btclightclient/keeper/state.go b/x/btclightclient/keeper/state.go index 53277bac2..2b670800c 100644 --- a/x/btclightclient/keeper/state.go +++ b/x/btclightclient/keeper/state.go @@ -35,7 +35,8 @@ func (k Keeper) HeadersState(ctx sdk.Context) HeadersState { // - hash->height // - hash->work // - (height, hash)->header storage -func (s HeadersState) CreateHeader(header *wire.BlockHeader, height uint64, cumulativeWork *big.Int) { +// Returns a boolean value indicating whether there is a new tip +func (s HeadersState) CreateHeader(header *wire.BlockHeader, height uint64, cumulativeWork *big.Int) bool { headerHash := header.BlockHash() // Get necessary keys according headersKey := types.HeadersObjectKey(height, &headerHash) @@ -52,7 +53,7 @@ func (s HeadersState) CreateHeader(header *wire.BlockHeader, height uint64, cumu // map header to work s.hashToWork.Set(workKey, cumulativeWork.Bytes()) - s.updateLongestChain(header, cumulativeWork) + return s.updateLongestChain(header, cumulativeWork) } // CreateTip sets the provided header as the tip @@ -235,12 +236,12 @@ func (s HeadersState) TipExists() bool { return s.tip.Has(tipKey) } -// updateLongestChain checks whether the tip should be updated and acts accordingly -func (s HeadersState) updateLongestChain(header *wire.BlockHeader, cumulativeWork *big.Int) { +// updateLongestChain checks whether the tip should be updated and returns true if it does +func (s HeadersState) updateLongestChain(header *wire.BlockHeader, cumulativeWork *big.Int) bool { // If there is no existing tip, then the header is set as the tip if !s.TipExists() { s.CreateTip(header) - return + return true } // Get the current tip header hash @@ -257,5 +258,7 @@ func (s HeadersState) updateLongestChain(header *wire.BlockHeader, cumulativeWor // the provided header is set as the tip. if tipWork.Cmp(cumulativeWork) < 0 { s.CreateTip(header) + return true } + return false } diff --git a/x/btclightclient/types/event.pb.go b/x/btclightclient/types/event.pb.go new file mode 100644 index 000000000..283a47ff1 --- /dev/null +++ b/x/btclightclient/types/event.pb.go @@ -0,0 +1,301 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: babylon/btclightclient/event.proto + +package types + +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type EventChainExtended struct { + Height uint64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` +} + +func (m *EventChainExtended) Reset() { *m = EventChainExtended{} } +func (m *EventChainExtended) String() string { return proto.CompactTextString(m) } +func (*EventChainExtended) ProtoMessage() {} +func (*EventChainExtended) Descriptor() ([]byte, []int) { + return fileDescriptor_dbeb7d7d6407e7ec, []int{0} +} +func (m *EventChainExtended) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventChainExtended) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventChainExtended.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 *EventChainExtended) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventChainExtended.Merge(m, src) +} +func (m *EventChainExtended) XXX_Size() int { + return m.Size() +} +func (m *EventChainExtended) XXX_DiscardUnknown() { + xxx_messageInfo_EventChainExtended.DiscardUnknown(m) +} + +var xxx_messageInfo_EventChainExtended proto.InternalMessageInfo + +func (m *EventChainExtended) GetHeight() uint64 { + if m != nil { + return m.Height + } + return 0 +} + +func init() { + proto.RegisterType((*EventChainExtended)(nil), "babylon.btclightclient.v1.EventChainExtended") +} + +func init() { + proto.RegisterFile("babylon/btclightclient/event.proto", fileDescriptor_dbeb7d7d6407e7ec) +} + +var fileDescriptor_dbeb7d7d6407e7ec = []byte{ + // 167 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x4a, 0x4a, 0x4c, 0xaa, + 0xcc, 0xc9, 0xcf, 0xd3, 0x4f, 0x2a, 0x49, 0xce, 0xc9, 0x4c, 0xcf, 0x00, 0x91, 0xa9, 0x79, 0x25, + 0xfa, 0xa9, 0x65, 0xa9, 0x79, 0x25, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0x92, 0x50, 0x35, + 0x7a, 0xa8, 0x6a, 0xf4, 0xca, 0x0c, 0x95, 0x74, 0xb8, 0x84, 0x5c, 0x41, 0x2a, 0x9d, 0x33, 0x12, + 0x33, 0xf3, 0x5c, 0x2b, 0x4a, 0x52, 0xf3, 0x52, 0x52, 0x53, 0x84, 0xc4, 0xb8, 0xd8, 0x32, 0x52, + 0x41, 0x0a, 0x25, 0x18, 0x15, 0x18, 0x35, 0x58, 0x82, 0xa0, 0x3c, 0xa7, 0x80, 0x13, 0x8f, 0xe4, + 0x18, 0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, 0x0b, 0x8f, + 0xe5, 0x18, 0x6e, 0x3c, 0x96, 0x63, 0x88, 0x32, 0x4b, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, + 0xce, 0xcf, 0xd5, 0x87, 0xda, 0x96, 0x0c, 0x32, 0x12, 0xc6, 0xd1, 0xaf, 0x40, 0x77, 0x60, 0x49, + 0x65, 0x41, 0x6a, 0x71, 0x12, 0x1b, 0xd8, 0x85, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x46, + 0xb6, 0x04, 0x4e, 0xc7, 0x00, 0x00, 0x00, +} + +func (m *EventChainExtended) 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 *EventChainExtended) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventChainExtended) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Height != 0 { + i = encodeVarintEvent(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintEvent(dAtA []byte, offset int, v uint64) int { + offset -= sovEvent(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *EventChainExtended) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Height != 0 { + n += 1 + sovEvent(uint64(m.Height)) + } + return n +} + +func sovEvent(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozEvent(x uint64) (n int) { + return sovEvent(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *EventChainExtended) 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 ErrIntOverflowEvent + } + 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: EventChainExtended: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventChainExtended: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvent + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipEvent(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvent + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipEvent(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowEvent + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowEvent + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowEvent + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthEvent + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupEvent + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthEvent + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthEvent = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowEvent = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupEvent = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/btclightclient/types/expected_keepers.go b/x/btclightclient/types/expected_keepers.go index 6aa6e9778..f0ef0e58b 100644 --- a/x/btclightclient/types/expected_keepers.go +++ b/x/btclightclient/types/expected_keepers.go @@ -16,3 +16,7 @@ type BankKeeper interface { SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins // Methods imported from bank should be defined here } + +type BTCLightClientHooks interface { + AfterTipUpdated(ctx sdk.Context, height uint64) // Must be called after the tip is updated +} diff --git a/x/btclightclient/types/hooks.go b/x/btclightclient/types/hooks.go new file mode 100644 index 000000000..38b4566c2 --- /dev/null +++ b/x/btclightclient/types/hooks.go @@ -0,0 +1,17 @@ +package types + +import sdk "github.com/cosmos/cosmos-sdk/types" + +var _ BTCLightClientHooks = &MultiBTCLightClientHooks{} + +type MultiBTCLightClientHooks []BTCLightClientHooks + +func NewMultiBTCLightClientHooks(hooks ...BTCLightClientHooks) MultiBTCLightClientHooks { + return hooks +} + +func (h MultiBTCLightClientHooks) AfterTipUpdated(ctx sdk.Context, height uint64) { + for i := range h { + h[i].AfterTipUpdated(ctx, height) + } +} From 6c583375e7c9333bab623442536d0e714213b062 Mon Sep 17 00:00:00 2001 From: Vitalis Salis Date: Thu, 7 Jul 2022 17:34:30 +0300 Subject: [PATCH 02/16] Add BlockHeight keeper --- x/btclightclient/keeper/keeper.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/x/btclightclient/keeper/keeper.go b/x/btclightclient/keeper/keeper.go index b80ab99d1..a00e76577 100644 --- a/x/btclightclient/keeper/keeper.go +++ b/x/btclightclient/keeper/keeper.go @@ -95,3 +95,9 @@ func (k Keeper) InsertHeader(ctx sdk.Context, header *wire.BlockHeader) error { } return nil } + +// BlockHeight returns the height of the provided header +func (k Keeper) BlockHeight(ctx sdk.Context, header *wire.BlockHeader) (uint64, error) { + headerHash := header.BlockHash() + return k.HeadersState(ctx).GetHeaderHeight(&headerHash) +} From d90a4bea5563bcc1f8dfc55ac90579ec81ad4bc2 Mon Sep 17 00:00:00 2001 From: Vitalis Salis Date: Fri, 8 Jul 2022 10:41:24 +0300 Subject: [PATCH 03/16] Add hash to AfterTipUpdated event --- proto/babylon/btclightclient/event.proto | 5 + x/btclightclient/keeper/keeper.go | 6 +- x/btclightclient/types/event.pb.go | 126 +++++++++++++++++------ 3 files changed, 103 insertions(+), 34 deletions(-) diff --git a/proto/babylon/btclightclient/event.proto b/proto/babylon/btclightclient/event.proto index ca7db2260..85504ee06 100644 --- a/proto/babylon/btclightclient/event.proto +++ b/proto/babylon/btclightclient/event.proto @@ -1,10 +1,15 @@ syntax = "proto3"; package babylon.btclightclient.v1; +import "gogoproto/gogo.proto"; + option go_package = "github.com/babylonchain/babylon/x/btclightclient/types"; // EventTipUpdated is emitted on Msg/InsertHeader message EventTipUpdated { // The height of the new tip uint64 height = 1; + bytes hash = 2 [ + (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BTCHeaderHashBytes" + ]; } diff --git a/x/btclightclient/keeper/keeper.go b/x/btclightclient/keeper/keeper.go index a00e76577..77c83fbff 100644 --- a/x/btclightclient/keeper/keeper.go +++ b/x/btclightclient/keeper/keeper.go @@ -2,6 +2,7 @@ package keeper import ( "fmt" + bbl "github.com/babylonchain/babylon/types" "github.com/btcsuite/btcd/wire" "github.com/tendermint/tendermint/libs/log" @@ -88,10 +89,13 @@ func (k Keeper) InsertHeader(ctx sdk.Context, header *wire.BlockHeader) error { // Create the header tipUpdated := k.HeadersState(ctx).CreateHeader(header, height+1, cumulativeWork) if tipUpdated { + btcHeaderHashBytes := bbl.NewBTCHeaderHashBytesFromChainhash(&headerHash) // Trigger TipUpdated hook k.AfterTipUpdated(ctx, height+1) // Emit TipUpdated event - ctx.EventManager().EmitTypedEvent(&types.EventChainExtended{Height: height + 1}) + ctx.EventManager().EmitTypedEvent(&types.EventTipUpdated{ + Height: height + 1, + Hash: &btcHeaderHashBytes}) } return nil } diff --git a/x/btclightclient/types/event.pb.go b/x/btclightclient/types/event.pb.go index 283a47ff1..2cba296e5 100644 --- a/x/btclightclient/types/event.pb.go +++ b/x/btclightclient/types/event.pb.go @@ -5,6 +5,8 @@ package types import ( fmt "fmt" + github_com_babylonchain_babylon_types "github.com/babylonchain/babylon/types" + _ "github.com/gogo/protobuf/gogoproto" proto "github.com/gogo/protobuf/proto" io "io" math "math" @@ -22,22 +24,25 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package -type EventChainExtended struct { - Height uint64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` +// EventTipUpdated is emitted on Msg/InsertHeader +type EventTipUpdated struct { + // The height of the new tip + Height uint64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` + Hash *github_com_babylonchain_babylon_types.BTCHeaderHashBytes `protobuf:"bytes,2,opt,name=hash,proto3,customtype=github.com/babylonchain/babylon/types.BTCHeaderHashBytes" json:"hash,omitempty"` } -func (m *EventChainExtended) Reset() { *m = EventChainExtended{} } -func (m *EventChainExtended) String() string { return proto.CompactTextString(m) } -func (*EventChainExtended) ProtoMessage() {} -func (*EventChainExtended) Descriptor() ([]byte, []int) { +func (m *EventTipUpdated) Reset() { *m = EventTipUpdated{} } +func (m *EventTipUpdated) String() string { return proto.CompactTextString(m) } +func (*EventTipUpdated) ProtoMessage() {} +func (*EventTipUpdated) Descriptor() ([]byte, []int) { return fileDescriptor_dbeb7d7d6407e7ec, []int{0} } -func (m *EventChainExtended) XXX_Unmarshal(b []byte) error { +func (m *EventTipUpdated) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *EventChainExtended) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *EventTipUpdated) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_EventChainExtended.Marshal(b, m, deterministic) + return xxx_messageInfo_EventTipUpdated.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -47,19 +52,19 @@ func (m *EventChainExtended) XXX_Marshal(b []byte, deterministic bool) ([]byte, return b[:n], nil } } -func (m *EventChainExtended) XXX_Merge(src proto.Message) { - xxx_messageInfo_EventChainExtended.Merge(m, src) +func (m *EventTipUpdated) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventTipUpdated.Merge(m, src) } -func (m *EventChainExtended) XXX_Size() int { +func (m *EventTipUpdated) XXX_Size() int { return m.Size() } -func (m *EventChainExtended) XXX_DiscardUnknown() { - xxx_messageInfo_EventChainExtended.DiscardUnknown(m) +func (m *EventTipUpdated) XXX_DiscardUnknown() { + xxx_messageInfo_EventTipUpdated.DiscardUnknown(m) } -var xxx_messageInfo_EventChainExtended proto.InternalMessageInfo +var xxx_messageInfo_EventTipUpdated proto.InternalMessageInfo -func (m *EventChainExtended) GetHeight() uint64 { +func (m *EventTipUpdated) GetHeight() uint64 { if m != nil { return m.Height } @@ -67,7 +72,7 @@ func (m *EventChainExtended) GetHeight() uint64 { } func init() { - proto.RegisterType((*EventChainExtended)(nil), "babylon.btclightclient.v1.EventChainExtended") + proto.RegisterType((*EventTipUpdated)(nil), "babylon.btclightclient.v1.EventTipUpdated") } func init() { @@ -75,21 +80,25 @@ func init() { } var fileDescriptor_dbeb7d7d6407e7ec = []byte{ - // 167 bytes of a gzipped FileDescriptorProto + // 225 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x4a, 0x4a, 0x4c, 0xaa, 0xcc, 0xc9, 0xcf, 0xd3, 0x4f, 0x2a, 0x49, 0xce, 0xc9, 0x4c, 0xcf, 0x00, 0x91, 0xa9, 0x79, 0x25, 0xfa, 0xa9, 0x65, 0xa9, 0x79, 0x25, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0x92, 0x50, 0x35, - 0x7a, 0xa8, 0x6a, 0xf4, 0xca, 0x0c, 0x95, 0x74, 0xb8, 0x84, 0x5c, 0x41, 0x2a, 0x9d, 0x33, 0x12, - 0x33, 0xf3, 0x5c, 0x2b, 0x4a, 0x52, 0xf3, 0x52, 0x52, 0x53, 0x84, 0xc4, 0xb8, 0xd8, 0x32, 0x52, - 0x41, 0x0a, 0x25, 0x18, 0x15, 0x18, 0x35, 0x58, 0x82, 0xa0, 0x3c, 0xa7, 0x80, 0x13, 0x8f, 0xe4, - 0x18, 0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, 0x0b, 0x8f, - 0xe5, 0x18, 0x6e, 0x3c, 0x96, 0x63, 0x88, 0x32, 0x4b, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, - 0xce, 0xcf, 0xd5, 0x87, 0xda, 0x96, 0x0c, 0x32, 0x12, 0xc6, 0xd1, 0xaf, 0x40, 0x77, 0x60, 0x49, - 0x65, 0x41, 0x6a, 0x71, 0x12, 0x1b, 0xd8, 0x85, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x46, - 0xb6, 0x04, 0x4e, 0xc7, 0x00, 0x00, 0x00, + 0x7a, 0xa8, 0x6a, 0xf4, 0xca, 0x0c, 0xa5, 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0xaa, 0xf4, 0x41, + 0x2c, 0x88, 0x06, 0xa5, 0x6a, 0x2e, 0x7e, 0x57, 0x90, 0xfe, 0x90, 0xcc, 0x82, 0xd0, 0x82, 0x94, + 0xc4, 0x92, 0xd4, 0x14, 0x21, 0x31, 0x2e, 0xb6, 0x8c, 0x54, 0x90, 0x5e, 0x09, 0x46, 0x05, 0x46, + 0x0d, 0x96, 0x20, 0x28, 0x4f, 0x28, 0x80, 0x8b, 0x25, 0x23, 0xb1, 0x38, 0x43, 0x82, 0x49, 0x81, + 0x51, 0x83, 0xc7, 0xc9, 0xe6, 0xd6, 0x3d, 0x79, 0x8b, 0xf4, 0xcc, 0x92, 0x8c, 0xd2, 0x24, 0xbd, + 0xe4, 0xfc, 0x5c, 0x7d, 0xa8, 0xc5, 0xc9, 0x19, 0x89, 0x99, 0x79, 0x30, 0x8e, 0x7e, 0x49, 0x65, + 0x41, 0x6a, 0xb1, 0x9e, 0x53, 0x88, 0xb3, 0x47, 0x6a, 0x62, 0x4a, 0x6a, 0x91, 0x47, 0x62, 0x71, + 0x86, 0x53, 0x65, 0x49, 0x6a, 0x71, 0x10, 0xd8, 0x24, 0xa7, 0x80, 0x13, 0x8f, 0xe4, 0x18, 0x2f, + 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, 0x0b, 0x8f, 0xe5, 0x18, + 0x6e, 0x3c, 0x96, 0x63, 0x88, 0x32, 0x23, 0x64, 0x72, 0x05, 0x7a, 0x28, 0x80, 0xad, 0x4a, 0x62, + 0x03, 0xfb, 0xca, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0x9d, 0x31, 0x8a, 0x8c, 0x2c, 0x01, 0x00, + 0x00, } -func (m *EventChainExtended) Marshal() (dAtA []byte, err error) { +func (m *EventTipUpdated) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -99,16 +108,28 @@ func (m *EventChainExtended) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *EventChainExtended) MarshalTo(dAtA []byte) (int, error) { +func (m *EventTipUpdated) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *EventChainExtended) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *EventTipUpdated) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l + if m.Hash != nil { + { + size := m.Hash.Size() + i -= size + if _, err := m.Hash.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintEvent(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } if m.Height != 0 { i = encodeVarintEvent(dAtA, i, uint64(m.Height)) i-- @@ -128,7 +149,7 @@ func encodeVarintEvent(dAtA []byte, offset int, v uint64) int { dAtA[offset] = uint8(v) return base } -func (m *EventChainExtended) Size() (n int) { +func (m *EventTipUpdated) Size() (n int) { if m == nil { return 0 } @@ -137,6 +158,10 @@ func (m *EventChainExtended) Size() (n int) { if m.Height != 0 { n += 1 + sovEvent(uint64(m.Height)) } + if m.Hash != nil { + l = m.Hash.Size() + n += 1 + l + sovEvent(uint64(l)) + } return n } @@ -146,7 +171,7 @@ func sovEvent(x uint64) (n int) { func sozEvent(x uint64) (n int) { return sovEvent(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } -func (m *EventChainExtended) Unmarshal(dAtA []byte) error { +func (m *EventTipUpdated) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -169,10 +194,10 @@ func (m *EventChainExtended) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: EventChainExtended: wiretype end group for non-group") + return fmt.Errorf("proto: EventTipUpdated: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: EventChainExtended: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: EventTipUpdated: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -194,6 +219,41 @@ func (m *EventChainExtended) Unmarshal(dAtA []byte) error { break } } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hash", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvent + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthEvent + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthEvent + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.BTCHeaderHashBytes + m.Hash = &v + if err := m.Hash.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipEvent(dAtA[iNdEx:]) From 973aad0f02dfd2e4f3279ecfd04e6b4510128a72 Mon Sep 17 00:00:00 2001 From: Vitalis Salis Date: Fri, 8 Jul 2022 15:12:56 +0300 Subject: [PATCH 04/16] Add events with more information --- proto/babylon/btclightclient/event.proto | 17 +- x/btclightclient/keeper/hooks.go | 14 +- x/btclightclient/keeper/keeper.go | 22 +- x/btclightclient/keeper/state.go | 86 ++++++ x/btclightclient/keeper/triggers.go | 32 +++ x/btclightclient/types/event.pb.go | 289 ++++++++++++++++++--- x/btclightclient/types/expected_keepers.go | 4 +- x/btclightclient/types/hooks.go | 15 +- 8 files changed, 422 insertions(+), 57 deletions(-) create mode 100644 x/btclightclient/keeper/triggers.go diff --git a/proto/babylon/btclightclient/event.proto b/proto/babylon/btclightclient/event.proto index 85504ee06..9c8004e08 100644 --- a/proto/babylon/btclightclient/event.proto +++ b/proto/babylon/btclightclient/event.proto @@ -5,10 +5,21 @@ import "gogoproto/gogo.proto"; option go_package = "github.com/babylonchain/babylon/x/btclightclient/types"; -// EventTipUpdated is emitted on Msg/InsertHeader -message EventTipUpdated { - // The height of the new tip +// EventBTCRollBack is emitted on Msg/InsertHeader +message EventBTCRollBack { + // The height to which we rolled back uint64 height = 1; + // The hash of the header to which we rolled back + bytes hash = 2 [ + (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BTCHeaderHashBytes" + ]; +} + +// EventBTCRollForward is emitted on Msg/InsertHeader +message EventBTCRollForward { + // The height to which we rolled forward + uint64 height = 1; + // The hash of the header to which we rolled forward bytes hash = 2 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BTCHeaderHashBytes" ]; diff --git a/x/btclightclient/keeper/hooks.go b/x/btclightclient/keeper/hooks.go index 1b89ad1f4..e0c5539dc 100644 --- a/x/btclightclient/keeper/hooks.go +++ b/x/btclightclient/keeper/hooks.go @@ -1,6 +1,7 @@ package keeper import ( + bbl "github.com/babylonchain/babylon/types" "github.com/babylonchain/babylon/x/btclightclient/types" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -8,9 +9,16 @@ import ( // Implements BTCLightClientHooks interface var _ types.BTCLightClientHooks = Keeper{} -// AfterTipUpdated - call hook if registered -func (k Keeper) AfterTipUpdated(ctx sdk.Context, height uint64) { +// AfterBTCRollBack - call hook if registered +func (k Keeper) AfterBTCRollBack(ctx sdk.Context, hash bbl.BTCHeaderHashBytes, height uint64) { if k.hooks != nil { - k.hooks.AfterTipUpdated(ctx, height) + k.hooks.AfterBTCRollBack(ctx, hash, height) + } +} + +// AfterBTCRollForward - call hook if registered +func (k Keeper) AfterBTCRollForward(ctx sdk.Context, hash bbl.BTCHeaderHashBytes, height uint64) { + if k.hooks != nil { + k.hooks.AfterBTCRollBack(ctx, hash, height) } } diff --git a/x/btclightclient/keeper/keeper.go b/x/btclightclient/keeper/keeper.go index 77c83fbff..c62bb66d2 100644 --- a/x/btclightclient/keeper/keeper.go +++ b/x/btclightclient/keeper/keeper.go @@ -2,7 +2,6 @@ package keeper import ( "fmt" - bbl "github.com/babylonchain/babylon/types" "github.com/btcsuite/btcd/wire" "github.com/tendermint/tendermint/libs/log" @@ -86,16 +85,23 @@ func (k Keeper) InsertHeader(ctx sdk.Context, header *wire.BlockHeader) error { headerWork := types.CalcWork(header) cumulativeWork := types.CumulativeWork(headerWork, parentWork) + previousTip := k.HeadersState(ctx).GetTip() // Create the header tipUpdated := k.HeadersState(ctx).CreateHeader(header, height+1, cumulativeWork) + + // The tip has been updated to `header` if tipUpdated { - btcHeaderHashBytes := bbl.NewBTCHeaderHashBytesFromChainhash(&headerHash) - // Trigger TipUpdated hook - k.AfterTipUpdated(ctx, height+1) - // Emit TipUpdated event - ctx.EventManager().EmitTypedEvent(&types.EventTipUpdated{ - Height: height + 1, - Hash: &btcHeaderHashBytes}) + hca, hcaHeight := k.HeadersState(ctx).GetHighestCommonAncestor(previousTip, header) + + k.triggerRollBack(ctx, hca, hcaHeight) + + addedToMainChain := k.HeadersState(ctx).GetInOrderAncestorsUntil(header, hca) + + for idx, added := range addedToMainChain { + // height + 1 -> height of the tip + // height + 1 - len(addedToMainChain) -> height of highest common ancestor + k.triggerRollForward(ctx, added, height+1-uint64(len(addedToMainChain))+uint64(idx)) + } } return nil } diff --git a/x/btclightclient/keeper/state.go b/x/btclightclient/keeper/state.go index 2b670800c..328e14b22 100644 --- a/x/btclightclient/keeper/state.go +++ b/x/btclightclient/keeper/state.go @@ -216,6 +216,92 @@ func (s HeadersState) GetMainChain() []*wire.BlockHeader { return chain } +// GetHighestCommonAncestor traverses the ancestors of both headers +// to identify the common ancestor with the highest height +func (s HeadersState) GetHighestCommonAncestor(header1 *wire.BlockHeader, header2 *wire.BlockHeader) (*wire.BlockHeader, uint64) { + // Get the prefix store for the (height, hash) -> header collection + store := prefix.NewStore(s.headers, types.HeadersObjectPrefix) + // Iterate it in reverse in order to get highest heights first + // TODO: need to verify this assumption + iter := store.ReverseIterator(nil, nil) + defer iter.Close() + + // The algorithm works as follows: + // 1. Initialize a hashmap hash -> bool denoting whether the hash + // of an ancestor of either header1 or header2 has been encountered + // 2. Maintain ancestor1 and ancestor2 as variables that point + // to the current ancestor hash of the header1 and header2 parameters + // 3. Whenever a node is encountered with a hash that is equal to ancestor{1,2}, + // update the ancestor{1,2} variables. + // 4. If ancestor1 or ancestor2 is set to the hash table, + // then that's the hash of the earliest ancestor + // 5. Using the hash of the heighest ancestor wait until we get the header bytes + // in order to avoid an extra access. + ancestor1 := header1.BlockHash() + ancestor2 := header2.BlockHash() + var encountered map[string]bool + encountered[ancestor1.String()] = true + encountered[ancestor2.String()] = true + var found *chainhash.Hash = nil + + for ; iter.Valid(); iter.Next() { + btcdBlock := blockHeaderFromStoredBytes(iter.Value()) + // We have already found what we're looking for, no need to proceed further + if found != nil { + if *found == btcdBlock.BlockHash() { + height, err := s.GetHeaderHeight(found) + if err != nil { + panic("Height for header existing in storage not maintained") + } + return btcdBlock, height + } + } else { + if ancestor1 == btcdBlock.BlockHash() { + ancestor1 = btcdBlock.PrevBlock + if encountered[ancestor1.String()] { + found = &ancestor1 + } + encountered[ancestor1.String()] = true + } + if ancestor2 == btcdBlock.BlockHash() { + ancestor2 = btcdBlock.PrevBlock + if encountered[ancestor2.String()] { + found = &ancestor2 + } + encountered[ancestor2.String()] = true + } + } + } + return nil, 0 +} + +// GetInOrderAncestorsUntil returns the list of nodes starting from the node after `parent` until the `child` +func (s HeadersState) GetInOrderAncestorsUntil(child *wire.BlockHeader, parent *wire.BlockHeader) []*wire.BlockHeader { + // Get the prefix store for the (height, hash) -> header collection + store := prefix.NewStore(s.headers, types.HeadersObjectPrefix) + // Iterate it in reverse in order to get highest heights first + // TODO: need to verify this assumption + iter := store.ReverseIterator(nil, nil) + defer iter.Close() + + currentHeader := child + + var ancestors []*wire.BlockHeader + ancestors = append(ancestors, child) + for ; iter.Valid(); iter.Next() { + btcdHeader := blockHeaderFromStoredBytes(iter.Value()) + if btcdHeader.BlockHash() == parent.BlockHash() { + break + } + if btcdHeader.BlockHash().String() == currentHeader.PrevBlock.String() { + currentHeader = btcdHeader + ancestors = append(ancestors, btcdHeader) + } + } + + return ancestors +} + // HeaderExists Check whether a hash is maintained in storage func (s HeadersState) HeaderExists(hash *chainhash.Hash) bool { // Get the prefix store for the hash->height collection diff --git a/x/btclightclient/keeper/triggers.go b/x/btclightclient/keeper/triggers.go new file mode 100644 index 000000000..519b73e25 --- /dev/null +++ b/x/btclightclient/keeper/triggers.go @@ -0,0 +1,32 @@ +package keeper + +import ( + bbl "github.com/babylonchain/babylon/types" + "github.com/babylonchain/babylon/x/btclightclient/types" + "github.com/btcsuite/btcd/wire" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +func (k Keeper) triggerRollBack(ctx sdk.Context, header *wire.BlockHeader, height uint64) { + headerHash := header.BlockHash() + btcHeaderHashBytes := bbl.NewBTCHeaderHashBytesFromChainhash(&headerHash) + // Trigger AfterBTCRollBack hook + k.AfterBTCRollBack(ctx, btcHeaderHashBytes, height+1) + // Emit BTCRollBack event + ctx.EventManager().EmitTypedEvent(&types.EventBTCRollBack{ + Height: height + 1, + Hash: &btcHeaderHashBytes, + }) +} + +func (k Keeper) triggerRollForward(ctx sdk.Context, header *wire.BlockHeader, height uint64) { + headerHash := header.BlockHash() + btcHeaderHashBytes := bbl.NewBTCHeaderHashBytesFromChainhash(&headerHash) + // Trigger AfterBTCRollForward hook + k.AfterBTCRollForward(ctx, btcHeaderHashBytes, height+1) + // Emit BTCRollForward event + ctx.EventManager().EmitTypedEvent(&types.EventBTCRollForward{ + Height: height + 1, + Hash: &btcHeaderHashBytes, + }) +} diff --git a/x/btclightclient/types/event.pb.go b/x/btclightclient/types/event.pb.go index 2cba296e5..e74ef5109 100644 --- a/x/btclightclient/types/event.pb.go +++ b/x/btclightclient/types/event.pb.go @@ -24,25 +24,26 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package -// EventTipUpdated is emitted on Msg/InsertHeader -type EventTipUpdated struct { - // The height of the new tip - Height uint64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` - Hash *github_com_babylonchain_babylon_types.BTCHeaderHashBytes `protobuf:"bytes,2,opt,name=hash,proto3,customtype=github.com/babylonchain/babylon/types.BTCHeaderHashBytes" json:"hash,omitempty"` +// EventBTCRollBack is emitted on Msg/InsertHeader +type EventBTCRollBack struct { + // The height to which we rolled back + Height uint64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` + // The hash of the header to which we rolled back + Hash *github_com_babylonchain_babylon_types.BTCHeaderHashBytes `protobuf:"bytes,2,opt,name=hash,proto3,customtype=github.com/babylonchain/babylon/types.BTCHeaderHashBytes" json:"hash,omitempty"` } -func (m *EventTipUpdated) Reset() { *m = EventTipUpdated{} } -func (m *EventTipUpdated) String() string { return proto.CompactTextString(m) } -func (*EventTipUpdated) ProtoMessage() {} -func (*EventTipUpdated) Descriptor() ([]byte, []int) { +func (m *EventBTCRollBack) Reset() { *m = EventBTCRollBack{} } +func (m *EventBTCRollBack) String() string { return proto.CompactTextString(m) } +func (*EventBTCRollBack) ProtoMessage() {} +func (*EventBTCRollBack) Descriptor() ([]byte, []int) { return fileDescriptor_dbeb7d7d6407e7ec, []int{0} } -func (m *EventTipUpdated) XXX_Unmarshal(b []byte) error { +func (m *EventBTCRollBack) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *EventTipUpdated) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *EventBTCRollBack) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_EventTipUpdated.Marshal(b, m, deterministic) + return xxx_messageInfo_EventBTCRollBack.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -52,19 +53,67 @@ func (m *EventTipUpdated) XXX_Marshal(b []byte, deterministic bool) ([]byte, err return b[:n], nil } } -func (m *EventTipUpdated) XXX_Merge(src proto.Message) { - xxx_messageInfo_EventTipUpdated.Merge(m, src) +func (m *EventBTCRollBack) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventBTCRollBack.Merge(m, src) } -func (m *EventTipUpdated) XXX_Size() int { +func (m *EventBTCRollBack) XXX_Size() int { return m.Size() } -func (m *EventTipUpdated) XXX_DiscardUnknown() { - xxx_messageInfo_EventTipUpdated.DiscardUnknown(m) +func (m *EventBTCRollBack) XXX_DiscardUnknown() { + xxx_messageInfo_EventBTCRollBack.DiscardUnknown(m) } -var xxx_messageInfo_EventTipUpdated proto.InternalMessageInfo +var xxx_messageInfo_EventBTCRollBack proto.InternalMessageInfo -func (m *EventTipUpdated) GetHeight() uint64 { +func (m *EventBTCRollBack) GetHeight() uint64 { + if m != nil { + return m.Height + } + return 0 +} + +// EventBTCRollForward is emitted on Msg/InsertHeader +type EventBTCRollForward struct { + // The height to which we rolled forward + Height uint64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` + // The hash of the header to which we rolled forward + Hash *github_com_babylonchain_babylon_types.BTCHeaderHashBytes `protobuf:"bytes,2,opt,name=hash,proto3,customtype=github.com/babylonchain/babylon/types.BTCHeaderHashBytes" json:"hash,omitempty"` +} + +func (m *EventBTCRollForward) Reset() { *m = EventBTCRollForward{} } +func (m *EventBTCRollForward) String() string { return proto.CompactTextString(m) } +func (*EventBTCRollForward) ProtoMessage() {} +func (*EventBTCRollForward) Descriptor() ([]byte, []int) { + return fileDescriptor_dbeb7d7d6407e7ec, []int{1} +} +func (m *EventBTCRollForward) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *EventBTCRollForward) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_EventBTCRollForward.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 *EventBTCRollForward) XXX_Merge(src proto.Message) { + xxx_messageInfo_EventBTCRollForward.Merge(m, src) +} +func (m *EventBTCRollForward) XXX_Size() int { + return m.Size() +} +func (m *EventBTCRollForward) XXX_DiscardUnknown() { + xxx_messageInfo_EventBTCRollForward.DiscardUnknown(m) +} + +var xxx_messageInfo_EventBTCRollForward proto.InternalMessageInfo + +func (m *EventBTCRollForward) GetHeight() uint64 { if m != nil { return m.Height } @@ -72,7 +121,8 @@ func (m *EventTipUpdated) GetHeight() uint64 { } func init() { - proto.RegisterType((*EventTipUpdated)(nil), "babylon.btclightclient.v1.EventTipUpdated") + proto.RegisterType((*EventBTCRollBack)(nil), "babylon.btclightclient.v1.EventBTCRollBack") + proto.RegisterType((*EventBTCRollForward)(nil), "babylon.btclightclient.v1.EventBTCRollForward") } func init() { @@ -80,25 +130,66 @@ func init() { } var fileDescriptor_dbeb7d7d6407e7ec = []byte{ - // 225 bytes of a gzipped FileDescriptorProto + // 242 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x4a, 0x4a, 0x4c, 0xaa, 0xcc, 0xc9, 0xcf, 0xd3, 0x4f, 0x2a, 0x49, 0xce, 0xc9, 0x4c, 0xcf, 0x00, 0x91, 0xa9, 0x79, 0x25, 0xfa, 0xa9, 0x65, 0xa9, 0x79, 0x25, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0x92, 0x50, 0x35, 0x7a, 0xa8, 0x6a, 0xf4, 0xca, 0x0c, 0xa5, 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0xaa, 0xf4, 0x41, - 0x2c, 0x88, 0x06, 0xa5, 0x6a, 0x2e, 0x7e, 0x57, 0x90, 0xfe, 0x90, 0xcc, 0x82, 0xd0, 0x82, 0x94, - 0xc4, 0x92, 0xd4, 0x14, 0x21, 0x31, 0x2e, 0xb6, 0x8c, 0x54, 0x90, 0x5e, 0x09, 0x46, 0x05, 0x46, - 0x0d, 0x96, 0x20, 0x28, 0x4f, 0x28, 0x80, 0x8b, 0x25, 0x23, 0xb1, 0x38, 0x43, 0x82, 0x49, 0x81, - 0x51, 0x83, 0xc7, 0xc9, 0xe6, 0xd6, 0x3d, 0x79, 0x8b, 0xf4, 0xcc, 0x92, 0x8c, 0xd2, 0x24, 0xbd, - 0xe4, 0xfc, 0x5c, 0x7d, 0xa8, 0xc5, 0xc9, 0x19, 0x89, 0x99, 0x79, 0x30, 0x8e, 0x7e, 0x49, 0x65, - 0x41, 0x6a, 0xb1, 0x9e, 0x53, 0x88, 0xb3, 0x47, 0x6a, 0x62, 0x4a, 0x6a, 0x91, 0x47, 0x62, 0x71, - 0x86, 0x53, 0x65, 0x49, 0x6a, 0x71, 0x10, 0xd8, 0x24, 0xa7, 0x80, 0x13, 0x8f, 0xe4, 0x18, 0x2f, - 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, 0x0b, 0x8f, 0xe5, 0x18, - 0x6e, 0x3c, 0x96, 0x63, 0x88, 0x32, 0x23, 0x64, 0x72, 0x05, 0x7a, 0x28, 0x80, 0xad, 0x4a, 0x62, - 0x03, 0xfb, 0xca, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0x9d, 0x31, 0x8a, 0x8c, 0x2c, 0x01, 0x00, - 0x00, + 0x2c, 0x88, 0x06, 0xa5, 0x1a, 0x2e, 0x01, 0x57, 0x90, 0x7e, 0xa7, 0x10, 0xe7, 0xa0, 0xfc, 0x9c, + 0x1c, 0xa7, 0xc4, 0xe4, 0x6c, 0x21, 0x31, 0x2e, 0xb6, 0x8c, 0x54, 0x90, 0x66, 0x09, 0x46, 0x05, + 0x46, 0x0d, 0x96, 0x20, 0x28, 0x4f, 0x28, 0x80, 0x8b, 0x25, 0x23, 0xb1, 0x38, 0x43, 0x82, 0x49, + 0x81, 0x51, 0x83, 0xc7, 0xc9, 0xe6, 0xd6, 0x3d, 0x79, 0x8b, 0xf4, 0xcc, 0x92, 0x8c, 0xd2, 0x24, + 0xbd, 0xe4, 0xfc, 0x5c, 0x7d, 0xa8, 0xcd, 0xc9, 0x19, 0x89, 0x99, 0x79, 0x30, 0x8e, 0x7e, 0x49, + 0x65, 0x41, 0x6a, 0xb1, 0x9e, 0x53, 0x88, 0xb3, 0x47, 0x6a, 0x62, 0x4a, 0x6a, 0x91, 0x47, 0x62, + 0x71, 0x86, 0x53, 0x65, 0x49, 0x6a, 0x71, 0x10, 0xd8, 0x24, 0xa5, 0x7a, 0x2e, 0x61, 0x64, 0xdb, + 0xdd, 0xf2, 0x8b, 0xca, 0x13, 0x8b, 0x52, 0xe8, 0xe7, 0x00, 0xa7, 0x80, 0x13, 0x8f, 0xe4, 0x18, + 0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, 0x0b, 0x8f, 0xe5, + 0x18, 0x6e, 0x3c, 0x96, 0x63, 0x88, 0x32, 0x23, 0x64, 0x72, 0x05, 0x7a, 0x3c, 0x80, 0xad, 0x4a, + 0x62, 0x03, 0x87, 0xab, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0x64, 0x1a, 0xeb, 0xce, 0xae, 0x01, + 0x00, 0x00, +} + +func (m *EventBTCRollBack) 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 *EventBTCRollBack) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *EventBTCRollBack) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Hash != nil { + { + size := m.Hash.Size() + i -= size + if _, err := m.Hash.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintEvent(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.Height != 0 { + i = encodeVarintEvent(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil } -func (m *EventTipUpdated) Marshal() (dAtA []byte, err error) { +func (m *EventBTCRollForward) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -108,12 +199,12 @@ func (m *EventTipUpdated) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *EventTipUpdated) MarshalTo(dAtA []byte) (int, error) { +func (m *EventBTCRollForward) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *EventTipUpdated) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *EventBTCRollForward) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int @@ -149,7 +240,23 @@ func encodeVarintEvent(dAtA []byte, offset int, v uint64) int { dAtA[offset] = uint8(v) return base } -func (m *EventTipUpdated) Size() (n int) { +func (m *EventBTCRollBack) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Height != 0 { + n += 1 + sovEvent(uint64(m.Height)) + } + if m.Hash != nil { + l = m.Hash.Size() + n += 1 + l + sovEvent(uint64(l)) + } + return n +} + +func (m *EventBTCRollForward) Size() (n int) { if m == nil { return 0 } @@ -171,7 +278,111 @@ func sovEvent(x uint64) (n int) { func sozEvent(x uint64) (n int) { return sovEvent(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } -func (m *EventTipUpdated) Unmarshal(dAtA []byte) error { +func (m *EventBTCRollBack) 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 ErrIntOverflowEvent + } + 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: EventBTCRollBack: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: EventBTCRollBack: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvent + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Hash", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvent + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthEvent + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthEvent + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var v github_com_babylonchain_babylon_types.BTCHeaderHashBytes + m.Hash = &v + if err := m.Hash.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipEvent(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthEvent + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *EventBTCRollForward) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -194,10 +405,10 @@ func (m *EventTipUpdated) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: EventTipUpdated: wiretype end group for non-group") + return fmt.Errorf("proto: EventBTCRollForward: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: EventTipUpdated: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: EventBTCRollForward: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: diff --git a/x/btclightclient/types/expected_keepers.go b/x/btclightclient/types/expected_keepers.go index f0ef0e58b..76b3b2294 100644 --- a/x/btclightclient/types/expected_keepers.go +++ b/x/btclightclient/types/expected_keepers.go @@ -1,6 +1,7 @@ package types import ( + bbl "github.com/babylonchain/babylon/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth/types" ) @@ -18,5 +19,6 @@ type BankKeeper interface { } type BTCLightClientHooks interface { - AfterTipUpdated(ctx sdk.Context, height uint64) // Must be called after the tip is updated + AfterBTCRollBack(ctx sdk.Context, hash bbl.BTCHeaderHashBytes, height uint64) // Must be called after the chain is rolled back + AfterBTCRollForward(ctx sdk.Context, hash bbl.BTCHeaderHashBytes, height uint64) // Must be called after the chain is rolled forward } diff --git a/x/btclightclient/types/hooks.go b/x/btclightclient/types/hooks.go index 38b4566c2..afe650051 100644 --- a/x/btclightclient/types/hooks.go +++ b/x/btclightclient/types/hooks.go @@ -1,6 +1,9 @@ package types -import sdk "github.com/cosmos/cosmos-sdk/types" +import ( + bbl "github.com/babylonchain/babylon/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) var _ BTCLightClientHooks = &MultiBTCLightClientHooks{} @@ -10,8 +13,14 @@ func NewMultiBTCLightClientHooks(hooks ...BTCLightClientHooks) MultiBTCLightClie return hooks } -func (h MultiBTCLightClientHooks) AfterTipUpdated(ctx sdk.Context, height uint64) { +func (h MultiBTCLightClientHooks) AfterBTCRollBack(ctx sdk.Context, hash bbl.BTCHeaderHashBytes, height uint64) { for i := range h { - h[i].AfterTipUpdated(ctx, height) + h[i].AfterBTCRollBack(ctx, hash, height) + } +} + +func (h MultiBTCLightClientHooks) AfterBTCRollForward(ctx sdk.Context, hash bbl.BTCHeaderHashBytes, height uint64) { + for i := range h { + h[i].AfterBTCRollForward(ctx, hash, height) } } From 4122827219d5882d0991e3c63b01c88966615a03 Mon Sep 17 00:00:00 2001 From: Vitalis Salis Date: Fri, 8 Jul 2022 15:19:20 +0300 Subject: [PATCH 05/16] Implement iterateReverseHeaders method --- x/btclightclient/keeper/state.go | 86 ++++++++++++++++---------------- 1 file changed, 44 insertions(+), 42 deletions(-) diff --git a/x/btclightclient/keeper/state.go b/x/btclightclient/keeper/state.go index 328e14b22..6da394029 100644 --- a/x/btclightclient/keeper/state.go +++ b/x/btclightclient/keeper/state.go @@ -174,17 +174,11 @@ func (s HeadersState) GetHeadersByHeight(height uint64, f func(*wire.BlockHeader // GetDescendingHeaders returns a collection of descending headers according to their height func (s HeadersState) GetDescendingHeaders() []*wire.BlockHeader { - // Get the prefix store for the (height, hash) -> header collection - store := prefix.NewStore(s.headers, types.HeadersObjectPrefix) - // Iterate it in reverse in order to get highest heights first - // TODO: need to verify this assumption - iter := store.ReverseIterator(nil, nil) - defer iter.Close() - var headers []*wire.BlockHeader - for ; iter.Valid(); iter.Next() { - headers = append(headers, blockHeaderFromStoredBytes(iter.Value())) - } + s.iterateReverseHeaders(func(header *wire.BlockHeader) bool { + headers = append(headers, header) + return true + }) return headers } @@ -219,13 +213,6 @@ func (s HeadersState) GetMainChain() []*wire.BlockHeader { // GetHighestCommonAncestor traverses the ancestors of both headers // to identify the common ancestor with the highest height func (s HeadersState) GetHighestCommonAncestor(header1 *wire.BlockHeader, header2 *wire.BlockHeader) (*wire.BlockHeader, uint64) { - // Get the prefix store for the (height, hash) -> header collection - store := prefix.NewStore(s.headers, types.HeadersObjectPrefix) - // Iterate it in reverse in order to get highest heights first - // TODO: need to verify this assumption - iter := store.ReverseIterator(nil, nil) - defer iter.Close() - // The algorithm works as follows: // 1. Initialize a hashmap hash -> bool denoting whether the hash // of an ancestor of either header1 or header2 has been encountered @@ -244,60 +231,58 @@ func (s HeadersState) GetHighestCommonAncestor(header1 *wire.BlockHeader, header encountered[ancestor2.String()] = true var found *chainhash.Hash = nil - for ; iter.Valid(); iter.Next() { - btcdBlock := blockHeaderFromStoredBytes(iter.Value()) + var resHeader *wire.BlockHeader = nil + var resHeight uint64 = 0 + + s.iterateReverseHeaders(func(btcdHeader *wire.BlockHeader) bool { // We have already found what we're looking for, no need to proceed further if found != nil { - if *found == btcdBlock.BlockHash() { + if *found == btcdHeader.BlockHash() { height, err := s.GetHeaderHeight(found) if err != nil { panic("Height for header existing in storage not maintained") } - return btcdBlock, height + resHeader = btcdHeader + resHeight = height + return false } } else { - if ancestor1 == btcdBlock.BlockHash() { - ancestor1 = btcdBlock.PrevBlock + if ancestor1 == btcdHeader.BlockHash() { + ancestor1 = btcdHeader.PrevBlock if encountered[ancestor1.String()] { found = &ancestor1 } encountered[ancestor1.String()] = true } - if ancestor2 == btcdBlock.BlockHash() { - ancestor2 = btcdBlock.PrevBlock + if ancestor2 == btcdHeader.BlockHash() { + ancestor2 = btcdHeader.PrevBlock if encountered[ancestor2.String()] { found = &ancestor2 } encountered[ancestor2.String()] = true } } - } - return nil, 0 + return true + }) + return resHeader, resHeight } // GetInOrderAncestorsUntil returns the list of nodes starting from the node after `parent` until the `child` func (s HeadersState) GetInOrderAncestorsUntil(child *wire.BlockHeader, parent *wire.BlockHeader) []*wire.BlockHeader { - // Get the prefix store for the (height, hash) -> header collection - store := prefix.NewStore(s.headers, types.HeadersObjectPrefix) - // Iterate it in reverse in order to get highest heights first - // TODO: need to verify this assumption - iter := store.ReverseIterator(nil, nil) - defer iter.Close() - currentHeader := child var ancestors []*wire.BlockHeader ancestors = append(ancestors, child) - for ; iter.Valid(); iter.Next() { - btcdHeader := blockHeaderFromStoredBytes(iter.Value()) - if btcdHeader.BlockHash() == parent.BlockHash() { - break + s.iterateReverseHeaders(func(header *wire.BlockHeader) bool { + if header.BlockHash() == parent.BlockHash() { + return false } - if btcdHeader.BlockHash().String() == currentHeader.PrevBlock.String() { - currentHeader = btcdHeader - ancestors = append(ancestors, btcdHeader) + if header.BlockHash().String() == currentHeader.PrevBlock.String() { + currentHeader = header + ancestors = append(ancestors, header) } - } + return true + }) return ancestors } @@ -348,3 +333,20 @@ func (s HeadersState) updateLongestChain(header *wire.BlockHeader, cumulativeWor } return false } + +func (s HeadersState) iterateReverseHeaders(fn func(*wire.BlockHeader) bool) { + // Get the prefix store for the (height, hash) -> header collection + store := prefix.NewStore(s.headers, types.HeadersObjectPrefix) + // Iterate it in reverse in order to get highest heights first + // TODO: need to verify this assumption + iter := store.ReverseIterator(nil, nil) + defer iter.Close() + + for ; iter.Valid(); iter.Next() { + btcdHeader := blockHeaderFromStoredBytes(iter.Value()) + stop := fn(btcdHeader) + if stop { + break + } + } +} From d97a563a2fdc0971d8e480304c602700a9fddda5 Mon Sep 17 00:00:00 2001 From: Vitalis Salis Date: Fri, 8 Jul 2022 16:16:00 +0300 Subject: [PATCH 06/16] fix --- x/btclightclient/keeper/state.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/x/btclightclient/keeper/state.go b/x/btclightclient/keeper/state.go index 6da394029..22cebf89d 100644 --- a/x/btclightclient/keeper/state.go +++ b/x/btclightclient/keeper/state.go @@ -177,7 +177,7 @@ func (s HeadersState) GetDescendingHeaders() []*wire.BlockHeader { var headers []*wire.BlockHeader s.iterateReverseHeaders(func(header *wire.BlockHeader) bool { headers = append(headers, header) - return true + return false }) return headers } @@ -244,7 +244,7 @@ func (s HeadersState) GetHighestCommonAncestor(header1 *wire.BlockHeader, header } resHeader = btcdHeader resHeight = height - return false + return true } } else { if ancestor1 == btcdHeader.BlockHash() { @@ -262,7 +262,7 @@ func (s HeadersState) GetHighestCommonAncestor(header1 *wire.BlockHeader, header encountered[ancestor2.String()] = true } } - return true + return false }) return resHeader, resHeight } @@ -275,13 +275,13 @@ func (s HeadersState) GetInOrderAncestorsUntil(child *wire.BlockHeader, parent * ancestors = append(ancestors, child) s.iterateReverseHeaders(func(header *wire.BlockHeader) bool { if header.BlockHash() == parent.BlockHash() { - return false + return true } if header.BlockHash().String() == currentHeader.PrevBlock.String() { currentHeader = header ancestors = append(ancestors, header) } - return true + return false }) return ancestors From ea4d7000aaef7632d76df759628e2e05c44a1178 Mon Sep 17 00:00:00 2001 From: Vitalis Salis Date: Sat, 9 Jul 2022 13:32:00 +0300 Subject: [PATCH 07/16] Fix chain extension logic --- x/btclightclient/keeper/keeper.go | 49 ++++++++++++++++++++++--------- x/btclightclient/keeper/state.go | 11 +++---- 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/x/btclightclient/keeper/keeper.go b/x/btclightclient/keeper/keeper.go index c62bb66d2..983b76e2a 100644 --- a/x/btclightclient/keeper/keeper.go +++ b/x/btclightclient/keeper/keeper.go @@ -87,22 +87,39 @@ func (k Keeper) InsertHeader(ctx sdk.Context, header *wire.BlockHeader) error { previousTip := k.HeadersState(ctx).GetTip() // Create the header - tipUpdated := k.HeadersState(ctx).CreateHeader(header, height+1, cumulativeWork) - - // The tip has been updated to `header` - if tipUpdated { - hca, hcaHeight := k.HeadersState(ctx).GetHighestCommonAncestor(previousTip, header) - - k.triggerRollBack(ctx, hca, hcaHeight) - - addedToMainChain := k.HeadersState(ctx).GetInOrderAncestorsUntil(header, hca) - - for idx, added := range addedToMainChain { - // height + 1 -> height of the tip - // height + 1 - len(addedToMainChain) -> height of highest common ancestor - k.triggerRollForward(ctx, added, height+1-uint64(len(addedToMainChain))+uint64(idx)) + k.HeadersState(ctx).CreateHeader(header, height+1, cumulativeWork) + + // Get the new tip + currentTip := k.HeadersState(ctx).GetTip() + + // Variable maintaining the headers that have been added to the main chain + var addedToMainChain []*wire.BlockHeader + + // The tip has changed, we need to send events + if currentTip.BlockHash().String() != previousTip.BlockHash().String() { + // The new tip extends the old tip + if isParent(currentTip, previousTip) { + // If the new header extended + addedToMainChain = append(addedToMainChain, currentTip) + } else { + // There has been a chain re-org + // Get the highest common ancestor between + hca, hcaHeight := k.HeadersState(ctx).GetHighestCommonAncestor(previousTip, header) + // Trigger a roll-back event to that ancestor + k.triggerRollBack(ctx, hca, hcaHeight) + + // Find the newly added headers to the main chain + addedToMainChain = k.HeadersState(ctx).GetInOrderAncestorsUntil(header, hca) } } + // Iterate through the headers that were added to the main chain + // and trigger a roll-forward event + for idx, added := range addedToMainChain { + // height + 1 -> height of the tip + // height + 1 - len(addedToMainChain) -> height of highest common ancestor + k.triggerRollForward(ctx, added, height+1-uint64(len(addedToMainChain))+uint64(idx)) + } + return nil } @@ -111,3 +128,7 @@ func (k Keeper) BlockHeight(ctx sdk.Context, header *wire.BlockHeader) (uint64, headerHash := header.BlockHash() return k.HeadersState(ctx).GetHeaderHeight(&headerHash) } + +func isParent(child *wire.BlockHeader, parent *wire.BlockHeader) bool { + return child.PrevBlock.String() == parent.BlockHash().String() +} diff --git a/x/btclightclient/keeper/state.go b/x/btclightclient/keeper/state.go index 22cebf89d..79c55437b 100644 --- a/x/btclightclient/keeper/state.go +++ b/x/btclightclient/keeper/state.go @@ -35,8 +35,7 @@ func (k Keeper) HeadersState(ctx sdk.Context) HeadersState { // - hash->height // - hash->work // - (height, hash)->header storage -// Returns a boolean value indicating whether there is a new tip -func (s HeadersState) CreateHeader(header *wire.BlockHeader, height uint64, cumulativeWork *big.Int) bool { +func (s HeadersState) CreateHeader(header *wire.BlockHeader, height uint64, cumulativeWork *big.Int) { headerHash := header.BlockHash() // Get necessary keys according headersKey := types.HeadersObjectKey(height, &headerHash) @@ -53,7 +52,7 @@ func (s HeadersState) CreateHeader(header *wire.BlockHeader, height uint64, cumu // map header to work s.hashToWork.Set(workKey, cumulativeWork.Bytes()) - return s.updateLongestChain(header, cumulativeWork) + s.updateLongestChain(header, cumulativeWork) } // CreateTip sets the provided header as the tip @@ -308,11 +307,11 @@ func (s HeadersState) TipExists() bool { } // updateLongestChain checks whether the tip should be updated and returns true if it does -func (s HeadersState) updateLongestChain(header *wire.BlockHeader, cumulativeWork *big.Int) bool { +func (s HeadersState) updateLongestChain(header *wire.BlockHeader, cumulativeWork *big.Int) { // If there is no existing tip, then the header is set as the tip if !s.TipExists() { s.CreateTip(header) - return true + return } // Get the current tip header hash @@ -329,9 +328,7 @@ func (s HeadersState) updateLongestChain(header *wire.BlockHeader, cumulativeWor // the provided header is set as the tip. if tipWork.Cmp(cumulativeWork) < 0 { s.CreateTip(header) - return true } - return false } func (s HeadersState) iterateReverseHeaders(fn func(*wire.BlockHeader) bool) { From 44d8c3d50adc4f7649013eb036d80b104184a248 Mon Sep 17 00:00:00 2001 From: Vitalis Salis Date: Sat, 9 Jul 2022 14:14:40 +0300 Subject: [PATCH 08/16] Use BTCHeaderInfo for events --- .../btclightclient/btclightclient.proto | 3 +- proto/babylon/btclightclient/event.proto | 15 +- proto/babylon/btclightclient/query.proto | 2 +- x/btclightclient/keeper/grpc_query.go | 13 +- x/btclightclient/keeper/hooks.go | 9 +- x/btclightclient/keeper/keeper.go | 30 ++-- x/btclightclient/keeper/triggers.go | 21 +-- .../{header_info.go => btc_header_info.go} | 5 +- ...r_info_test.go => btc_header_info_test.go} | 5 +- x/btclightclient/types/btclightclient.pb.go | 95 ++++++---- x/btclightclient/types/event.pb.go | 163 ++++++------------ x/btclightclient/types/expected_keepers.go | 5 +- x/btclightclient/types/hooks.go | 9 +- x/btclightclient/types/query.pb.go | 84 ++++----- 14 files changed, 215 insertions(+), 244 deletions(-) rename x/btclightclient/types/{header_info.go => btc_header_info.go} (73%) rename x/btclightclient/types/{header_info_test.go => btc_header_info_test.go} (90%) diff --git a/proto/babylon/btclightclient/btclightclient.proto b/proto/babylon/btclightclient/btclightclient.proto index b8883a9f7..e9487396a 100644 --- a/proto/babylon/btclightclient/btclightclient.proto +++ b/proto/babylon/btclightclient/btclightclient.proto @@ -14,12 +14,13 @@ message BaseBTCHeader { uint64 height = 2; } -message HeaderInfo { +message BTCHeaderInfo { bytes header = 1 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BTCHeaderBytes" ]; bytes hash = 2 [ (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BTCHeaderHashBytes" ]; + uint64 height = 3; } diff --git a/proto/babylon/btclightclient/event.proto b/proto/babylon/btclightclient/event.proto index 9c8004e08..60d005996 100644 --- a/proto/babylon/btclightclient/event.proto +++ b/proto/babylon/btclightclient/event.proto @@ -2,25 +2,16 @@ syntax = "proto3"; package babylon.btclightclient.v1; import "gogoproto/gogo.proto"; +import "babylon/btclightclient/btclightclient.proto"; option go_package = "github.com/babylonchain/babylon/x/btclightclient/types"; // EventBTCRollBack is emitted on Msg/InsertHeader message EventBTCRollBack { - // The height to which we rolled back - uint64 height = 1; - // The hash of the header to which we rolled back - bytes hash = 2 [ - (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BTCHeaderHashBytes" - ]; + BTCHeaderInfo header = 1; } // EventBTCRollForward is emitted on Msg/InsertHeader message EventBTCRollForward { - // The height to which we rolled forward - uint64 height = 1; - // The hash of the header to which we rolled forward - bytes hash = 2 [ - (gogoproto.customtype) = "github.com/babylonchain/babylon/types.BTCHeaderHashBytes" - ]; + BTCHeaderInfo header = 1; } diff --git a/proto/babylon/btclightclient/query.proto b/proto/babylon/btclightclient/query.proto index 8d1203092..dc28a6307 100644 --- a/proto/babylon/btclightclient/query.proto +++ b/proto/babylon/btclightclient/query.proto @@ -75,7 +75,7 @@ message QueryMainChainRequest { // QueryMainChainResponse is response type for the Query/MainChain RPC method. message QueryMainChainResponse { - repeated HeaderInfo headers = 1; + repeated BTCHeaderInfo headers = 1; cosmos.base.query.v1beta1.PageResponse pagination = 2; } diff --git a/x/btclightclient/keeper/grpc_query.go b/x/btclightclient/keeper/grpc_query.go index ea5bcf303..17706794a 100644 --- a/x/btclightclient/keeper/grpc_query.go +++ b/x/btclightclient/keeper/grpc_query.go @@ -67,6 +67,11 @@ func (k Keeper) MainChain(ctx context.Context, req *types.QueryMainChainRequest) } // If a starting key has not been set, then the first header is the tip prevHeader := k.HeadersState(sdkCtx).GetTip() + prevHeaderHash := prevHeader.BlockHash() + prevHeaderHeight, err := k.HeadersState(sdkCtx).GetHeaderHeight(&prevHeaderHash) + if err != nil { + panic("Maintained header does not have a height") + } // Otherwise, retrieve the header from the key if len(req.Pagination.Key) != 0 { headerHash, err := bbl.NewBTCHeaderHashBytesFromBytes(req.Pagination.Key) @@ -82,8 +87,9 @@ func (k Keeper) MainChain(ctx context.Context, req *types.QueryMainChainRequest) return &types.QueryMainChainResponse{}, nil } - var headers []*types.HeaderInfo - headerInfo := types.NewHeaderInfo(prevHeader) + var headers []*types.BTCHeaderInfo + currentHeight := prevHeaderHeight + headerInfo := types.NewBTCHeaderInfo(prevHeader, prevHeaderHeight) headers = append(headers, headerInfo) store := prefix.NewStore(k.HeadersState(sdkCtx).headers, types.HeadersObjectPrefix) @@ -94,8 +100,9 @@ func (k Keeper) MainChain(ctx context.Context, req *types.QueryMainChainRequest) btcdHeader := blockHeaderFromStoredBytes(value) // If the previous block extends this block, then this block is part of the main chain if prevHeader.PrevBlock.String() == btcdHeader.BlockHash().String() { + currentHeight -= 1 prevHeader = btcdHeader - headers = append(headers, types.NewHeaderInfo(btcdHeader)) + headers = append(headers, types.NewBTCHeaderInfo(btcdHeader, currentHeight)) } } return true, nil diff --git a/x/btclightclient/keeper/hooks.go b/x/btclightclient/keeper/hooks.go index e0c5539dc..502e6244c 100644 --- a/x/btclightclient/keeper/hooks.go +++ b/x/btclightclient/keeper/hooks.go @@ -1,7 +1,6 @@ package keeper import ( - bbl "github.com/babylonchain/babylon/types" "github.com/babylonchain/babylon/x/btclightclient/types" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -10,15 +9,15 @@ import ( var _ types.BTCLightClientHooks = Keeper{} // AfterBTCRollBack - call hook if registered -func (k Keeper) AfterBTCRollBack(ctx sdk.Context, hash bbl.BTCHeaderHashBytes, height uint64) { +func (k Keeper) AfterBTCRollBack(ctx sdk.Context, headerInfo *types.BTCHeaderInfo) { if k.hooks != nil { - k.hooks.AfterBTCRollBack(ctx, hash, height) + k.hooks.AfterBTCRollBack(ctx, headerInfo) } } // AfterBTCRollForward - call hook if registered -func (k Keeper) AfterBTCRollForward(ctx sdk.Context, hash bbl.BTCHeaderHashBytes, height uint64) { +func (k Keeper) AfterBTCRollForward(ctx sdk.Context, headerInfo *types.BTCHeaderInfo) { if k.hooks != nil { - k.hooks.AfterBTCRollBack(ctx, hash, height) + k.hooks.AfterBTCRollBack(ctx, headerInfo) } } diff --git a/x/btclightclient/keeper/keeper.go b/x/btclightclient/keeper/keeper.go index 983b76e2a..99fe3ad80 100644 --- a/x/btclightclient/keeper/keeper.go +++ b/x/btclightclient/keeper/keeper.go @@ -70,7 +70,7 @@ func (k Keeper) InsertHeader(ctx sdk.Context, header *wire.BlockHeader) error { return types.ErrHeaderParentDoesNotExist.Wrap("parent for provided hash is not maintained") } - height, err := k.HeadersState(ctx).GetHeaderHeight(&header.PrevBlock) + parentHeight, err := k.HeadersState(ctx).GetHeaderHeight(&header.PrevBlock) if err != nil { // Height should always exist if the previous checks have passed panic("Height for parent is not maintained") @@ -87,7 +87,7 @@ func (k Keeper) InsertHeader(ctx sdk.Context, header *wire.BlockHeader) error { previousTip := k.HeadersState(ctx).GetTip() // Create the header - k.HeadersState(ctx).CreateHeader(header, height+1, cumulativeWork) + k.HeadersState(ctx).CreateHeader(header, parentHeight+1, cumulativeWork) // Get the new tip currentTip := k.HeadersState(ctx).GetTip() @@ -96,11 +96,15 @@ func (k Keeper) InsertHeader(ctx sdk.Context, header *wire.BlockHeader) error { var addedToMainChain []*wire.BlockHeader // The tip has changed, we need to send events - if currentTip.BlockHash().String() != previousTip.BlockHash().String() { + if !sameBlock(currentTip, previousTip) { + tipHeight := parentHeight + 1 // The new tip extends the old tip if isParent(currentTip, previousTip) { + if !sameBlock(currentTip, header) { + panic("The header added on top of the previous tip is not the same as the provided header") + } // If the new header extended - addedToMainChain = append(addedToMainChain, currentTip) + addedToMainChain = append(addedToMainChain, header) } else { // There has been a chain re-org // Get the highest common ancestor between @@ -111,13 +115,13 @@ func (k Keeper) InsertHeader(ctx sdk.Context, header *wire.BlockHeader) error { // Find the newly added headers to the main chain addedToMainChain = k.HeadersState(ctx).GetInOrderAncestorsUntil(header, hca) } - } - // Iterate through the headers that were added to the main chain - // and trigger a roll-forward event - for idx, added := range addedToMainChain { - // height + 1 -> height of the tip - // height + 1 - len(addedToMainChain) -> height of highest common ancestor - k.triggerRollForward(ctx, added, height+1-uint64(len(addedToMainChain))+uint64(idx)) + // Iterate through the headers that were added to the main chain + // and trigger a roll-forward event + for idx, added := range addedToMainChain { + // tipHeight + 1 - len(addedToMainChain) -> height of the highest common ancestor + addedHeight := tipHeight - uint64(len(addedToMainChain)) + 1 + uint64(idx) + k.triggerRollForward(ctx, added, addedHeight) + } } return nil @@ -132,3 +136,7 @@ func (k Keeper) BlockHeight(ctx sdk.Context, header *wire.BlockHeader) (uint64, func isParent(child *wire.BlockHeader, parent *wire.BlockHeader) bool { return child.PrevBlock.String() == parent.BlockHash().String() } + +func sameBlock(header1 *wire.BlockHeader, header2 *wire.BlockHeader) bool { + return header1.BlockHash().String() == header2.BlockHash().String() +} diff --git a/x/btclightclient/keeper/triggers.go b/x/btclightclient/keeper/triggers.go index 519b73e25..de058c87d 100644 --- a/x/btclightclient/keeper/triggers.go +++ b/x/btclightclient/keeper/triggers.go @@ -1,32 +1,23 @@ package keeper import ( - bbl "github.com/babylonchain/babylon/types" "github.com/babylonchain/babylon/x/btclightclient/types" "github.com/btcsuite/btcd/wire" sdk "github.com/cosmos/cosmos-sdk/types" ) func (k Keeper) triggerRollBack(ctx sdk.Context, header *wire.BlockHeader, height uint64) { - headerHash := header.BlockHash() - btcHeaderHashBytes := bbl.NewBTCHeaderHashBytesFromChainhash(&headerHash) + headerInfo := types.NewBTCHeaderInfo(header, height) // Trigger AfterBTCRollBack hook - k.AfterBTCRollBack(ctx, btcHeaderHashBytes, height+1) + k.AfterBTCRollBack(ctx, headerInfo) // Emit BTCRollBack event - ctx.EventManager().EmitTypedEvent(&types.EventBTCRollBack{ - Height: height + 1, - Hash: &btcHeaderHashBytes, - }) + ctx.EventManager().EmitTypedEvent(&types.EventBTCRollBack{Header: headerInfo}) } func (k Keeper) triggerRollForward(ctx sdk.Context, header *wire.BlockHeader, height uint64) { - headerHash := header.BlockHash() - btcHeaderHashBytes := bbl.NewBTCHeaderHashBytesFromChainhash(&headerHash) + headerInfo := types.NewBTCHeaderInfo(header, height) // Trigger AfterBTCRollForward hook - k.AfterBTCRollForward(ctx, btcHeaderHashBytes, height+1) + k.AfterBTCRollForward(ctx, headerInfo) // Emit BTCRollForward event - ctx.EventManager().EmitTypedEvent(&types.EventBTCRollForward{ - Height: height + 1, - Hash: &btcHeaderHashBytes, - }) + ctx.EventManager().EmitTypedEvent(&types.EventBTCRollForward{Header: headerInfo}) } diff --git a/x/btclightclient/types/header_info.go b/x/btclightclient/types/btc_header_info.go similarity index 73% rename from x/btclightclient/types/header_info.go rename to x/btclightclient/types/btc_header_info.go index 5d1a5d25b..10d926ff0 100644 --- a/x/btclightclient/types/header_info.go +++ b/x/btclightclient/types/btc_header_info.go @@ -5,14 +5,15 @@ import ( "github.com/btcsuite/btcd/wire" ) -func NewHeaderInfo(header *wire.BlockHeader) *HeaderInfo { +func NewBTCHeaderInfo(header *wire.BlockHeader, height uint64) *BTCHeaderInfo { headerHashCh := header.BlockHash() currentHeaderBytes := bbl.NewBTCHeaderBytesFromBlockHeader(header) headerHash := bbl.NewBTCHeaderHashBytesFromChainhash(&headerHashCh) - return &HeaderInfo{ + return &BTCHeaderInfo{ Header: ¤tHeaderBytes, Hash: &headerHash, + Height: height, } } diff --git a/x/btclightclient/types/header_info_test.go b/x/btclightclient/types/btc_header_info_test.go similarity index 90% rename from x/btclightclient/types/header_info_test.go rename to x/btclightclient/types/btc_header_info_test.go index d61071ae5..9400e15f2 100644 --- a/x/btclightclient/types/header_info_test.go +++ b/x/btclightclient/types/btc_header_info_test.go @@ -19,10 +19,11 @@ func FuzzNewHeaderInfo(f *testing.F) { btcdHeader.Timestamp.Unix(), btcdHeader.PrevBlock.String(), btcdHeader.MerkleRoot.String(), + uint64(42), int64(17)) f.Fuzz(func(t *testing.T, version int32, bits uint32, nonce uint32, - timeInt int64, prevBlockStr string, merkleRootStr string, seed int64) { + timeInt int64, prevBlockStr string, merkleRootStr string, height uint64, seed int64) { // If either of the hash strings is not of appropriate length // or not valid hex, generate a random hex randomly @@ -32,7 +33,7 @@ func FuzzNewHeaderInfo(f *testing.F) { // Get the expected header bytes expectedHeaderBytes := bbl.NewBTCHeaderBytesFromBlockHeader(header) - headerInfo := types.NewHeaderInfo(header) + headerInfo := types.NewBTCHeaderInfo(header, height) if headerInfo == nil { t.Errorf("returned object is nil") } diff --git a/x/btclightclient/types/btclightclient.pb.go b/x/btclightclient/types/btclightclient.pb.go index 29a2d7609..48f3b0718 100644 --- a/x/btclightclient/types/btclightclient.pb.go +++ b/x/btclightclient/types/btclightclient.pb.go @@ -71,23 +71,24 @@ func (m *BaseBTCHeader) GetHeight() uint64 { return 0 } -type HeaderInfo struct { +type BTCHeaderInfo struct { Header *github_com_babylonchain_babylon_types.BTCHeaderBytes `protobuf:"bytes,1,opt,name=header,proto3,customtype=github.com/babylonchain/babylon/types.BTCHeaderBytes" json:"header,omitempty"` Hash *github_com_babylonchain_babylon_types.BTCHeaderHashBytes `protobuf:"bytes,2,opt,name=hash,proto3,customtype=github.com/babylonchain/babylon/types.BTCHeaderHashBytes" json:"hash,omitempty"` + Height uint64 `protobuf:"varint,3,opt,name=height,proto3" json:"height,omitempty"` } -func (m *HeaderInfo) Reset() { *m = HeaderInfo{} } -func (m *HeaderInfo) String() string { return proto.CompactTextString(m) } -func (*HeaderInfo) ProtoMessage() {} -func (*HeaderInfo) Descriptor() ([]byte, []int) { +func (m *BTCHeaderInfo) Reset() { *m = BTCHeaderInfo{} } +func (m *BTCHeaderInfo) String() string { return proto.CompactTextString(m) } +func (*BTCHeaderInfo) ProtoMessage() {} +func (*BTCHeaderInfo) Descriptor() ([]byte, []int) { return fileDescriptor_3313d955a6cadef2, []int{1} } -func (m *HeaderInfo) XXX_Unmarshal(b []byte) error { +func (m *BTCHeaderInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *HeaderInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *BTCHeaderInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_HeaderInfo.Marshal(b, m, deterministic) + return xxx_messageInfo_BTCHeaderInfo.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalToSizedBuffer(b) @@ -97,21 +98,28 @@ func (m *HeaderInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { return b[:n], nil } } -func (m *HeaderInfo) XXX_Merge(src proto.Message) { - xxx_messageInfo_HeaderInfo.Merge(m, src) +func (m *BTCHeaderInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_BTCHeaderInfo.Merge(m, src) } -func (m *HeaderInfo) XXX_Size() int { +func (m *BTCHeaderInfo) XXX_Size() int { return m.Size() } -func (m *HeaderInfo) XXX_DiscardUnknown() { - xxx_messageInfo_HeaderInfo.DiscardUnknown(m) +func (m *BTCHeaderInfo) XXX_DiscardUnknown() { + xxx_messageInfo_BTCHeaderInfo.DiscardUnknown(m) } -var xxx_messageInfo_HeaderInfo proto.InternalMessageInfo +var xxx_messageInfo_BTCHeaderInfo proto.InternalMessageInfo + +func (m *BTCHeaderInfo) GetHeight() uint64 { + if m != nil { + return m.Height + } + return 0 +} func init() { proto.RegisterType((*BaseBTCHeader)(nil), "babylon.btclightclient.v1.BaseBTCHeader") - proto.RegisterType((*HeaderInfo)(nil), "babylon.btclightclient.v1.HeaderInfo") + proto.RegisterType((*BTCHeaderInfo)(nil), "babylon.btclightclient.v1.BTCHeaderInfo") } func init() { @@ -119,7 +127,7 @@ func init() { } var fileDescriptor_3313d955a6cadef2 = []byte{ - // 247 bytes of a gzipped FileDescriptorProto + // 253 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x4e, 0x4a, 0x4c, 0xaa, 0xcc, 0xc9, 0xcf, 0xd3, 0x4f, 0x2a, 0x49, 0xce, 0xc9, 0x4c, 0xcf, 0x00, 0x91, 0xa9, 0x79, 0x25, 0x68, 0x5c, 0xbd, 0x82, 0xa2, 0xfc, 0x92, 0x7c, 0x21, 0x49, 0xa8, 0x62, 0x3d, 0x34, 0xd9, 0x32, @@ -129,13 +137,13 @@ var fileDescriptor_3313d955a6cadef2 = []byte{ 0xde, 0x24, 0x3d, 0xb3, 0x24, 0xa3, 0x34, 0x49, 0x2f, 0x39, 0x3f, 0x57, 0x1f, 0x6a, 0x41, 0x72, 0x46, 0x62, 0x66, 0x1e, 0x8c, 0xa3, 0x5f, 0x52, 0x59, 0x90, 0x5a, 0xac, 0x07, 0x37, 0xc4, 0xa9, 0xb2, 0x24, 0xb5, 0x38, 0x08, 0x6a, 0x8e, 0x90, 0x18, 0xc8, 0x44, 0x90, 0x5b, 0x24, 0x98, 0x14, - 0x18, 0x35, 0x58, 0x82, 0xa0, 0x3c, 0xa5, 0x0d, 0x8c, 0x5c, 0x5c, 0x10, 0xf5, 0x9e, 0x79, 0x69, - 0xf9, 0x34, 0xb0, 0x38, 0x80, 0x8b, 0x25, 0x23, 0xb1, 0x38, 0x03, 0x6c, 0x2d, 0x8f, 0x93, 0xcd, - 0xad, 0x7b, 0xf2, 0x16, 0x24, 0x9a, 0xe7, 0x91, 0x58, 0x9c, 0x01, 0x31, 0x13, 0x6c, 0x92, 0x53, - 0xc0, 0x89, 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x38, 0xe1, 0xb1, - 0x1c, 0xc3, 0x85, 0xc7, 0x72, 0x0c, 0x37, 0x1e, 0xcb, 0x31, 0x44, 0x99, 0x11, 0x32, 0xb9, 0x02, - 0x3d, 0xfe, 0xc0, 0x56, 0x25, 0xb1, 0x81, 0xa3, 0xc1, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0x30, - 0xbb, 0x6f, 0xf3, 0xe6, 0x01, 0x00, 0x00, + 0x18, 0x35, 0x58, 0x82, 0xa0, 0x3c, 0xa5, 0xd3, 0x8c, 0x5c, 0xbc, 0x70, 0x2d, 0x9e, 0x79, 0x69, + 0xf9, 0x34, 0xb0, 0x3b, 0x80, 0x8b, 0x25, 0x23, 0xb1, 0x38, 0x03, 0x6c, 0x33, 0x8f, 0x93, 0xcd, + 0xad, 0x7b, 0xf2, 0x16, 0x24, 0x9a, 0xe7, 0x91, 0x58, 0x9c, 0x01, 0x31, 0x13, 0x6c, 0x12, 0x92, + 0x6f, 0x98, 0x91, 0x7d, 0xe3, 0x14, 0x70, 0xe2, 0x91, 0x1c, 0xe3, 0x85, 0x47, 0x72, 0x8c, 0x0f, + 0x1e, 0xc9, 0x31, 0x4e, 0x78, 0x2c, 0xc7, 0x70, 0xe1, 0xb1, 0x1c, 0xc3, 0x8d, 0xc7, 0x72, 0x0c, + 0x51, 0x66, 0x84, 0x6c, 0xac, 0x40, 0x8f, 0x5a, 0xb0, 0x13, 0x92, 0xd8, 0xc0, 0x31, 0x64, 0x0c, + 0x08, 0x00, 0x00, 0xff, 0xff, 0x73, 0x01, 0xff, 0x5f, 0x01, 0x02, 0x00, 0x00, } func (m *BaseBTCHeader) Marshal() (dAtA []byte, err error) { @@ -178,7 +186,7 @@ func (m *BaseBTCHeader) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } -func (m *HeaderInfo) Marshal() (dAtA []byte, err error) { +func (m *BTCHeaderInfo) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalToSizedBuffer(dAtA[:size]) @@ -188,16 +196,21 @@ func (m *HeaderInfo) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *HeaderInfo) MarshalTo(dAtA []byte) (int, error) { +func (m *BTCHeaderInfo) MarshalTo(dAtA []byte) (int, error) { size := m.Size() return m.MarshalToSizedBuffer(dAtA[:size]) } -func (m *HeaderInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { +func (m *BTCHeaderInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { i := len(dAtA) _ = i var l int _ = l + if m.Height != 0 { + i = encodeVarintBtclightclient(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x18 + } if m.Hash != nil { { size := m.Hash.Size() @@ -252,7 +265,7 @@ func (m *BaseBTCHeader) Size() (n int) { return n } -func (m *HeaderInfo) Size() (n int) { +func (m *BTCHeaderInfo) Size() (n int) { if m == nil { return 0 } @@ -266,6 +279,9 @@ func (m *HeaderInfo) Size() (n int) { l = m.Hash.Size() n += 1 + l + sovBtclightclient(uint64(l)) } + if m.Height != 0 { + n += 1 + sovBtclightclient(uint64(m.Height)) + } return n } @@ -379,7 +395,7 @@ func (m *BaseBTCHeader) Unmarshal(dAtA []byte) error { } return nil } -func (m *HeaderInfo) Unmarshal(dAtA []byte) error { +func (m *BTCHeaderInfo) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -402,10 +418,10 @@ func (m *HeaderInfo) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: HeaderInfo: wiretype end group for non-group") + return fmt.Errorf("proto: BTCHeaderInfo: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: HeaderInfo: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: BTCHeaderInfo: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -478,6 +494,25 @@ func (m *HeaderInfo) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBtclightclient + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipBtclightclient(dAtA[iNdEx:]) diff --git a/x/btclightclient/types/event.pb.go b/x/btclightclient/types/event.pb.go index e74ef5109..46f732761 100644 --- a/x/btclightclient/types/event.pb.go +++ b/x/btclightclient/types/event.pb.go @@ -5,7 +5,6 @@ package types import ( fmt "fmt" - github_com_babylonchain_babylon_types "github.com/babylonchain/babylon/types" _ "github.com/gogo/protobuf/gogoproto" proto "github.com/gogo/protobuf/proto" io "io" @@ -26,10 +25,7 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // EventBTCRollBack is emitted on Msg/InsertHeader type EventBTCRollBack struct { - // The height to which we rolled back - Height uint64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` - // The hash of the header to which we rolled back - Hash *github_com_babylonchain_babylon_types.BTCHeaderHashBytes `protobuf:"bytes,2,opt,name=hash,proto3,customtype=github.com/babylonchain/babylon/types.BTCHeaderHashBytes" json:"hash,omitempty"` + Header *BTCHeaderInfo `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"` } func (m *EventBTCRollBack) Reset() { *m = EventBTCRollBack{} } @@ -65,19 +61,16 @@ func (m *EventBTCRollBack) XXX_DiscardUnknown() { var xxx_messageInfo_EventBTCRollBack proto.InternalMessageInfo -func (m *EventBTCRollBack) GetHeight() uint64 { +func (m *EventBTCRollBack) GetHeader() *BTCHeaderInfo { if m != nil { - return m.Height + return m.Header } - return 0 + return nil } // EventBTCRollForward is emitted on Msg/InsertHeader type EventBTCRollForward struct { - // The height to which we rolled forward - Height uint64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` - // The hash of the header to which we rolled forward - Hash *github_com_babylonchain_babylon_types.BTCHeaderHashBytes `protobuf:"bytes,2,opt,name=hash,proto3,customtype=github.com/babylonchain/babylon/types.BTCHeaderHashBytes" json:"hash,omitempty"` + Header *BTCHeaderInfo `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"` } func (m *EventBTCRollForward) Reset() { *m = EventBTCRollForward{} } @@ -113,11 +106,11 @@ func (m *EventBTCRollForward) XXX_DiscardUnknown() { var xxx_messageInfo_EventBTCRollForward proto.InternalMessageInfo -func (m *EventBTCRollForward) GetHeight() uint64 { +func (m *EventBTCRollForward) GetHeader() *BTCHeaderInfo { if m != nil { - return m.Height + return m.Header } - return 0 + return nil } func init() { @@ -130,23 +123,21 @@ func init() { } var fileDescriptor_dbeb7d7d6407e7ec = []byte{ - // 242 bytes of a gzipped FileDescriptorProto + // 223 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x4a, 0x4a, 0x4c, 0xaa, 0xcc, 0xc9, 0xcf, 0xd3, 0x4f, 0x2a, 0x49, 0xce, 0xc9, 0x4c, 0xcf, 0x00, 0x91, 0xa9, 0x79, 0x25, 0xfa, 0xa9, 0x65, 0xa9, 0x79, 0x25, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0x92, 0x50, 0x35, 0x7a, 0xa8, 0x6a, 0xf4, 0xca, 0x0c, 0xa5, 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0xaa, 0xf4, 0x41, - 0x2c, 0x88, 0x06, 0xa5, 0x1a, 0x2e, 0x01, 0x57, 0x90, 0x7e, 0xa7, 0x10, 0xe7, 0xa0, 0xfc, 0x9c, - 0x1c, 0xa7, 0xc4, 0xe4, 0x6c, 0x21, 0x31, 0x2e, 0xb6, 0x8c, 0x54, 0x90, 0x66, 0x09, 0x46, 0x05, - 0x46, 0x0d, 0x96, 0x20, 0x28, 0x4f, 0x28, 0x80, 0x8b, 0x25, 0x23, 0xb1, 0x38, 0x43, 0x82, 0x49, - 0x81, 0x51, 0x83, 0xc7, 0xc9, 0xe6, 0xd6, 0x3d, 0x79, 0x8b, 0xf4, 0xcc, 0x92, 0x8c, 0xd2, 0x24, - 0xbd, 0xe4, 0xfc, 0x5c, 0x7d, 0xa8, 0xcd, 0xc9, 0x19, 0x89, 0x99, 0x79, 0x30, 0x8e, 0x7e, 0x49, - 0x65, 0x41, 0x6a, 0xb1, 0x9e, 0x53, 0x88, 0xb3, 0x47, 0x6a, 0x62, 0x4a, 0x6a, 0x91, 0x47, 0x62, - 0x71, 0x86, 0x53, 0x65, 0x49, 0x6a, 0x71, 0x10, 0xd8, 0x24, 0xa5, 0x7a, 0x2e, 0x61, 0x64, 0xdb, - 0xdd, 0xf2, 0x8b, 0xca, 0x13, 0x8b, 0x52, 0xe8, 0xe7, 0x00, 0xa7, 0x80, 0x13, 0x8f, 0xe4, 0x18, - 0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63, 0x39, 0x86, 0x0b, 0x8f, 0xe5, - 0x18, 0x6e, 0x3c, 0x96, 0x63, 0x88, 0x32, 0x23, 0x64, 0x72, 0x05, 0x7a, 0x3c, 0x80, 0xad, 0x4a, - 0x62, 0x03, 0x87, 0xab, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0x64, 0x1a, 0xeb, 0xce, 0xae, 0x01, - 0x00, 0x00, + 0x2c, 0x88, 0x06, 0x29, 0x6d, 0x1c, 0x86, 0xa2, 0xe9, 0x07, 0x2b, 0x56, 0x0a, 0xe1, 0x12, 0x70, + 0x05, 0x59, 0xe6, 0x14, 0xe2, 0x1c, 0x94, 0x9f, 0x93, 0xe3, 0x94, 0x98, 0x9c, 0x2d, 0xe4, 0xc0, + 0xc5, 0x96, 0x91, 0x9a, 0x98, 0x92, 0x5a, 0x24, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0x6d, 0xa4, 0xa1, + 0x87, 0xd3, 0x09, 0x7a, 0x4e, 0x21, 0xce, 0x1e, 0x60, 0xb5, 0x9e, 0x79, 0x69, 0xf9, 0x41, 0x50, + 0x7d, 0x4a, 0xe1, 0x5c, 0xc2, 0xc8, 0xa6, 0xba, 0xe5, 0x17, 0x95, 0x27, 0x16, 0xa5, 0x50, 0x6e, + 0xb0, 0x53, 0xc0, 0x89, 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x38, + 0xe1, 0xb1, 0x1c, 0xc3, 0x85, 0xc7, 0x72, 0x0c, 0x37, 0x1e, 0xcb, 0x31, 0x44, 0x99, 0xa5, 0x67, + 0x96, 0x64, 0x94, 0x26, 0xe9, 0x25, 0xe7, 0xe7, 0xea, 0x43, 0x4d, 0x4d, 0xce, 0x48, 0xcc, 0xcc, + 0x83, 0x71, 0xf4, 0x2b, 0xd0, 0xc3, 0xa3, 0xa4, 0xb2, 0x20, 0xb5, 0x38, 0x89, 0x0d, 0x1c, 0x0e, + 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfd, 0xad, 0x58, 0xdc, 0x8b, 0x01, 0x00, 0x00, } func (m *EventBTCRollBack) Marshal() (dAtA []byte, err error) { @@ -169,22 +160,17 @@ func (m *EventBTCRollBack) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if m.Hash != nil { + if m.Header != nil { { - size := m.Hash.Size() - i -= size - if _, err := m.Hash.MarshalTo(dAtA[i:]); err != nil { + size, err := m.Header.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { return 0, err } + i -= size i = encodeVarintEvent(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x12 - } - if m.Height != 0 { - i = encodeVarintEvent(dAtA, i, uint64(m.Height)) - i-- - dAtA[i] = 0x8 + dAtA[i] = 0xa } return len(dAtA) - i, nil } @@ -209,22 +195,17 @@ func (m *EventBTCRollForward) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if m.Hash != nil { + if m.Header != nil { { - size := m.Hash.Size() - i -= size - if _, err := m.Hash.MarshalTo(dAtA[i:]); err != nil { + size, err := m.Header.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { return 0, err } + i -= size i = encodeVarintEvent(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x12 - } - if m.Height != 0 { - i = encodeVarintEvent(dAtA, i, uint64(m.Height)) - i-- - dAtA[i] = 0x8 + dAtA[i] = 0xa } return len(dAtA) - i, nil } @@ -246,11 +227,8 @@ func (m *EventBTCRollBack) Size() (n int) { } var l int _ = l - if m.Height != 0 { - n += 1 + sovEvent(uint64(m.Height)) - } - if m.Hash != nil { - l = m.Hash.Size() + if m.Header != nil { + l = m.Header.Size() n += 1 + l + sovEvent(uint64(l)) } return n @@ -262,11 +240,8 @@ func (m *EventBTCRollForward) Size() (n int) { } var l int _ = l - if m.Height != 0 { - n += 1 + sovEvent(uint64(m.Height)) - } - if m.Hash != nil { - l = m.Hash.Size() + if m.Header != nil { + l = m.Header.Size() n += 1 + l + sovEvent(uint64(l)) } return n @@ -308,29 +283,10 @@ func (m *EventBTCRollBack) Unmarshal(dAtA []byte) error { } switch fieldNum { case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) - } - m.Height = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowEvent - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Height |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Hash", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType) } - var byteLen int + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowEvent @@ -340,24 +296,25 @@ func (m *EventBTCRollBack) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= int(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - if byteLen < 0 { + if msglen < 0 { return ErrInvalidLengthEvent } - postIndex := iNdEx + byteLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthEvent } if postIndex > l { return io.ErrUnexpectedEOF } - var v github_com_babylonchain_babylon_types.BTCHeaderHashBytes - m.Hash = &v - if err := m.Hash.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if m.Header == nil { + m.Header = &BTCHeaderInfo{} + } + if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -412,29 +369,10 @@ func (m *EventBTCRollForward) Unmarshal(dAtA []byte) error { } switch fieldNum { case 1: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) - } - m.Height = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowEvent - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.Height |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - case 2: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Hash", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Header", wireType) } - var byteLen int + var msglen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowEvent @@ -444,24 +382,25 @@ func (m *EventBTCRollForward) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= int(b&0x7F) << shift + msglen |= int(b&0x7F) << shift if b < 0x80 { break } } - if byteLen < 0 { + if msglen < 0 { return ErrInvalidLengthEvent } - postIndex := iNdEx + byteLen + postIndex := iNdEx + msglen if postIndex < 0 { return ErrInvalidLengthEvent } if postIndex > l { return io.ErrUnexpectedEOF } - var v github_com_babylonchain_babylon_types.BTCHeaderHashBytes - m.Hash = &v - if err := m.Hash.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if m.Header == nil { + m.Header = &BTCHeaderInfo{} + } + if err := m.Header.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex diff --git a/x/btclightclient/types/expected_keepers.go b/x/btclightclient/types/expected_keepers.go index 76b3b2294..8a6b9b437 100644 --- a/x/btclightclient/types/expected_keepers.go +++ b/x/btclightclient/types/expected_keepers.go @@ -1,7 +1,6 @@ package types import ( - bbl "github.com/babylonchain/babylon/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/auth/types" ) @@ -19,6 +18,6 @@ type BankKeeper interface { } type BTCLightClientHooks interface { - AfterBTCRollBack(ctx sdk.Context, hash bbl.BTCHeaderHashBytes, height uint64) // Must be called after the chain is rolled back - AfterBTCRollForward(ctx sdk.Context, hash bbl.BTCHeaderHashBytes, height uint64) // Must be called after the chain is rolled forward + AfterBTCRollBack(ctx sdk.Context, headerInfo *BTCHeaderInfo) // Must be called after the chain is rolled back + AfterBTCRollForward(ctx sdk.Context, headerInfo *BTCHeaderInfo) // Must be called after the chain is rolled forward } diff --git a/x/btclightclient/types/hooks.go b/x/btclightclient/types/hooks.go index afe650051..c703b0315 100644 --- a/x/btclightclient/types/hooks.go +++ b/x/btclightclient/types/hooks.go @@ -1,7 +1,6 @@ package types import ( - bbl "github.com/babylonchain/babylon/types" sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -13,14 +12,14 @@ func NewMultiBTCLightClientHooks(hooks ...BTCLightClientHooks) MultiBTCLightClie return hooks } -func (h MultiBTCLightClientHooks) AfterBTCRollBack(ctx sdk.Context, hash bbl.BTCHeaderHashBytes, height uint64) { +func (h MultiBTCLightClientHooks) AfterBTCRollBack(ctx sdk.Context, headerInfo *BTCHeaderInfo) { for i := range h { - h[i].AfterBTCRollBack(ctx, hash, height) + h[i].AfterBTCRollBack(ctx, headerInfo) } } -func (h MultiBTCLightClientHooks) AfterBTCRollForward(ctx sdk.Context, hash bbl.BTCHeaderHashBytes, height uint64) { +func (h MultiBTCLightClientHooks) AfterBTCRollForward(ctx sdk.Context, headerInfo *BTCHeaderInfo) { for i := range h { - h[i].AfterBTCRollForward(ctx, hash, height) + h[i].AfterBTCRollForward(ctx, headerInfo) } } diff --git a/x/btclightclient/types/query.pb.go b/x/btclightclient/types/query.pb.go index ec933fcc1..5ba211ec7 100644 --- a/x/btclightclient/types/query.pb.go +++ b/x/btclightclient/types/query.pb.go @@ -338,7 +338,7 @@ func (m *QueryMainChainRequest) GetPagination() *query.PageRequest { // QueryMainChainResponse is response type for the Query/MainChain RPC method. type QueryMainChainResponse struct { - Headers []*HeaderInfo `protobuf:"bytes,1,rep,name=headers,proto3" json:"headers,omitempty"` + Headers []*BTCHeaderInfo `protobuf:"bytes,1,rep,name=headers,proto3" json:"headers,omitempty"` Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"` } @@ -375,7 +375,7 @@ func (m *QueryMainChainResponse) XXX_DiscardUnknown() { var xxx_messageInfo_QueryMainChainResponse proto.InternalMessageInfo -func (m *QueryMainChainResponse) GetHeaders() []*HeaderInfo { +func (m *QueryMainChainResponse) GetHeaders() []*BTCHeaderInfo { if m != nil { return m.Headers } @@ -405,45 +405,45 @@ func init() { } var fileDescriptor_6293be71fb7ba6c4 = []byte{ - // 602 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x94, 0x41, 0x6f, 0xd3, 0x3c, - 0x18, 0xc7, 0xeb, 0xbd, 0x7d, 0x4b, 0xf1, 0x38, 0x99, 0x82, 0x46, 0x84, 0xb2, 0x2d, 0x65, 0x63, - 0x30, 0xb0, 0x69, 0x27, 0x21, 0x0e, 0x48, 0x48, 0xad, 0x04, 0xe3, 0x80, 0x54, 0xa2, 0x89, 0x03, - 0x42, 0x42, 0x4e, 0x31, 0x49, 0xa4, 0xd6, 0xce, 0x1a, 0xb7, 0xa2, 0x57, 0x3e, 0x01, 0x82, 0x1b, - 0x07, 0x0e, 0x7c, 0x06, 0x3e, 0xc4, 0x8e, 0x93, 0xb8, 0x20, 0x0e, 0x13, 0x6a, 0xb9, 0xf3, 0x15, - 0x50, 0x6c, 0xa7, 0x90, 0xa0, 0x36, 0x45, 0xec, 0x52, 0xd5, 0xce, 0xf3, 0x3c, 0xff, 0x9f, 0x1f, - 0xff, 0x1f, 0x43, 0xc7, 0xa3, 0xde, 0xb8, 0x27, 0x38, 0xf1, 0x64, 0xb7, 0x17, 0xfa, 0x41, 0xf2, - 0xcb, 0xb8, 0x24, 0x87, 0x43, 0x36, 0x18, 0xe3, 0x68, 0x20, 0xa4, 0x40, 0x97, 0x4c, 0x0c, 0xce, - 0xc6, 0xe0, 0x51, 0xc3, 0xaa, 0xf9, 0xc2, 0x17, 0x2a, 0x8a, 0x24, 0xff, 0x74, 0x82, 0x75, 0xd9, - 0x17, 0xc2, 0xef, 0x31, 0x42, 0xa3, 0x90, 0x50, 0xce, 0x85, 0xa4, 0x32, 0x14, 0x3c, 0x36, 0x5f, - 0xaf, 0x77, 0x45, 0xdc, 0x17, 0x31, 0xf1, 0x68, 0xcc, 0xb4, 0x0e, 0x19, 0x35, 0x3c, 0x26, 0x69, - 0x83, 0x44, 0xd4, 0x0f, 0xb9, 0x0a, 0x36, 0xb1, 0xf5, 0x39, 0x78, 0x11, 0x1d, 0xd0, 0x7e, 0x5a, - 0x70, 0x77, 0x4e, 0x50, 0x0e, 0x57, 0x05, 0x3b, 0x35, 0x88, 0x1e, 0x27, 0x9a, 0x1d, 0x55, 0xc1, - 0x65, 0x87, 0x43, 0x16, 0x4b, 0xe7, 0x09, 0x3c, 0x9f, 0xd9, 0x8d, 0x23, 0xc1, 0x63, 0x86, 0xee, - 0xc1, 0x8a, 0x56, 0x5a, 0x03, 0x1b, 0x60, 0x67, 0xb5, 0xb9, 0x89, 0xe7, 0xb6, 0x02, 0xeb, 0xd4, - 0x56, 0xf9, 0xe8, 0x64, 0xbd, 0xe4, 0x9a, 0x34, 0xe7, 0x99, 0x51, 0xdb, 0xa7, 0x71, 0xc0, 0x52, - 0x35, 0x74, 0x1f, 0xc2, 0x5f, 0x27, 0x35, 0xa5, 0xb7, 0xb1, 0x6e, 0x0b, 0x4e, 0xda, 0x82, 0x75, - 0xfb, 0x4d, 0x5b, 0x70, 0x87, 0xfa, 0xcc, 0xe4, 0xba, 0xbf, 0x65, 0x3a, 0x9f, 0x80, 0xc1, 0x4e, - 0xcb, 0x1b, 0xec, 0x03, 0x58, 0x09, 0xd4, 0xce, 0x1a, 0xd8, 0xf8, 0x6f, 0xe7, 0x5c, 0xeb, 0xee, - 0xd7, 0x93, 0xf5, 0x3b, 0x7e, 0x28, 0x83, 0xa1, 0x87, 0xbb, 0xa2, 0x4f, 0xcc, 0x21, 0xba, 0x01, - 0x0d, 0x79, 0xba, 0x20, 0x72, 0x1c, 0xb1, 0x18, 0xb7, 0x0e, 0xda, 0xfb, 0x8c, 0xbe, 0x60, 0x83, - 0xa4, 0x64, 0x6b, 0x2c, 0x59, 0xec, 0x9a, 0x5a, 0xe8, 0x41, 0x86, 0x7a, 0x45, 0x51, 0x5f, 0x2d, - 0xa4, 0xd6, 0x48, 0x19, 0xec, 0x00, 0xd6, 0x14, 0x75, 0x5b, 0x70, 0x49, 0x43, 0x3e, 0x6b, 0x4b, - 0x07, 0x96, 0x13, 0x29, 0xd5, 0x90, 0x7f, 0x85, 0x56, 0x95, 0x9c, 0x3d, 0x78, 0x21, 0xa7, 0x64, - 0x3a, 0x64, 0xc1, 0x6a, 0xd7, 0xec, 0x29, 0xb9, 0xaa, 0x3b, 0x5b, 0x3b, 0xcf, 0x4d, 0xd2, 0x23, - 0x1a, 0xf2, 0x76, 0xa2, 0x76, 0xda, 0xd7, 0xf6, 0x11, 0xc0, 0x8b, 0x79, 0x85, 0x99, 0xe1, 0xce, - 0x04, 0xea, 0x24, 0xfa, 0xea, 0x56, 0x9b, 0x5b, 0x0b, 0x1c, 0xa7, 0xcf, 0xfc, 0x90, 0xbf, 0x14, - 0x6e, 0x9a, 0x75, 0x6a, 0x97, 0xd4, 0xfc, 0x51, 0x86, 0xff, 0x2b, 0x48, 0xf4, 0x16, 0xc0, 0x8a, - 0x36, 0x37, 0xba, 0xb9, 0x80, 0xe6, 0xcf, 0xa9, 0xb2, 0xf0, 0xb2, 0xe1, 0x5a, 0xdf, 0xb9, 0xf6, - 0xfa, 0xf3, 0xf7, 0x77, 0x2b, 0x75, 0xb4, 0x49, 0xe6, 0x4c, 0xf4, 0xa8, 0x61, 0x26, 0x5f, 0x41, - 0x69, 0xd7, 0x17, 0x43, 0x65, 0x86, 0xaf, 0x18, 0x2a, 0x3b, 0x4c, 0x4b, 0x41, 0x99, 0x09, 0x79, - 0x0f, 0x60, 0x35, 0xb5, 0x1a, 0x22, 0x45, 0x3a, 0x39, 0xfb, 0x5b, 0xb7, 0x96, 0x4f, 0x30, 0x68, - 0xbb, 0x0a, 0x6d, 0x0b, 0xd5, 0x17, 0xa0, 0xa5, 0xb6, 0x46, 0x1f, 0x00, 0x3c, 0x3b, 0x33, 0x1c, - 0x2a, 0x14, 0xcb, 0xbb, 0xdf, 0x6a, 0xfc, 0x45, 0x86, 0xe1, 0xbb, 0xa1, 0xf8, 0xb6, 0xd1, 0x95, - 0x05, 0x7c, 0x7d, 0x1a, 0xea, 0x99, 0x6e, 0x75, 0x8e, 0x26, 0x36, 0x38, 0x9e, 0xd8, 0xe0, 0xdb, - 0xc4, 0x06, 0x6f, 0xa6, 0x76, 0xe9, 0x78, 0x6a, 0x97, 0xbe, 0x4c, 0xed, 0xd2, 0xd3, 0xdb, 0x45, - 0xcf, 0xc0, 0xab, 0x7c, 0x61, 0xf5, 0x2e, 0x78, 0x15, 0xf5, 0xe4, 0xef, 0xfd, 0x0c, 0x00, 0x00, - 0xff, 0xff, 0x52, 0x94, 0xb8, 0xb6, 0xe5, 0x06, 0x00, 0x00, + // 601 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x94, 0x41, 0x6f, 0xd3, 0x30, + 0x14, 0xc7, 0xeb, 0x31, 0xca, 0xf0, 0x38, 0x99, 0x81, 0x46, 0x84, 0xb2, 0x2d, 0x85, 0x51, 0x18, + 0xd8, 0xb4, 0x93, 0x10, 0x07, 0x24, 0xa4, 0x54, 0x82, 0x71, 0x40, 0x2a, 0xd1, 0xc4, 0x01, 0x21, + 0x21, 0xa7, 0x98, 0x24, 0x52, 0x6b, 0x67, 0x8d, 0x5b, 0xd1, 0x2b, 0x9f, 0x00, 0xc1, 0x8d, 0x03, + 0x37, 0xbe, 0x01, 0x1f, 0x62, 0xc7, 0x49, 0x5c, 0x10, 0x87, 0x09, 0xb5, 0xdc, 0xf9, 0x0a, 0x28, + 0xb6, 0x53, 0x48, 0x50, 0x9b, 0x22, 0x76, 0xa9, 0x6a, 0xe7, 0xbd, 0xf7, 0xff, 0xf9, 0xf9, 0xfd, + 0x0d, 0x1d, 0x9f, 0xfa, 0xa3, 0xae, 0xe0, 0xc4, 0x97, 0x9d, 0x6e, 0x14, 0x84, 0xe9, 0x2f, 0xe3, + 0x92, 0x1c, 0x0c, 0x58, 0x7f, 0x84, 0xe3, 0xbe, 0x90, 0x02, 0x5d, 0x32, 0x31, 0x38, 0x1f, 0x83, + 0x87, 0x0d, 0x6b, 0x2d, 0x10, 0x81, 0x50, 0x51, 0x24, 0xfd, 0xa7, 0x13, 0xac, 0xcb, 0x81, 0x10, + 0x41, 0x97, 0x11, 0x1a, 0x47, 0x84, 0x72, 0x2e, 0x24, 0x95, 0x91, 0xe0, 0x89, 0xf9, 0x7a, 0xa3, + 0x23, 0x92, 0x9e, 0x48, 0x88, 0x4f, 0x13, 0xa6, 0x75, 0xc8, 0xb0, 0xe1, 0x33, 0x49, 0x1b, 0x24, + 0xa6, 0x41, 0xc4, 0x55, 0xb0, 0x89, 0xad, 0xcd, 0xc0, 0x8b, 0x69, 0x9f, 0xf6, 0xb2, 0x82, 0x3b, + 0x33, 0x82, 0x0a, 0xb8, 0x2a, 0xd8, 0x59, 0x83, 0xe8, 0x49, 0xaa, 0xd9, 0x56, 0x15, 0x3c, 0x76, + 0x30, 0x60, 0x89, 0x74, 0x9e, 0xc2, 0xf3, 0xb9, 0xdd, 0x24, 0x16, 0x3c, 0x61, 0xe8, 0x3e, 0xac, + 0x6a, 0xa5, 0x75, 0xb0, 0x09, 0xea, 0xab, 0xcd, 0x2d, 0x3c, 0xb3, 0x15, 0x58, 0xa7, 0xba, 0xcb, + 0x87, 0xc7, 0x1b, 0x15, 0xcf, 0xa4, 0x39, 0xcf, 0x8d, 0xda, 0x1e, 0x4d, 0x42, 0x96, 0xa9, 0xa1, + 0x07, 0x10, 0xfe, 0x3e, 0xa9, 0x29, 0xbd, 0x8d, 0x75, 0x5b, 0x70, 0xda, 0x16, 0xac, 0xdb, 0x6f, + 0xda, 0x82, 0xdb, 0x34, 0x60, 0x26, 0xd7, 0xfb, 0x23, 0xd3, 0xf9, 0x0c, 0x0c, 0x76, 0x56, 0xde, + 0x60, 0xef, 0xc3, 0x6a, 0xa8, 0x76, 0xd6, 0xc1, 0xe6, 0xa9, 0xfa, 0x39, 0xf7, 0xde, 0xb7, 0xe3, + 0x8d, 0xbb, 0x41, 0x24, 0xc3, 0x81, 0x8f, 0x3b, 0xa2, 0x47, 0xcc, 0x21, 0x3a, 0x21, 0x8d, 0x78, + 0xb6, 0x20, 0x72, 0x14, 0xb3, 0x04, 0xbb, 0xfb, 0xad, 0x3d, 0x46, 0x5f, 0xb2, 0x7e, 0x5a, 0xd2, + 0x1d, 0x49, 0x96, 0x78, 0xa6, 0x16, 0x7a, 0x98, 0xa3, 0x5e, 0x52, 0xd4, 0xd7, 0x4a, 0xa9, 0x35, + 0x52, 0x0e, 0x3b, 0x84, 0x6b, 0x8a, 0xba, 0x25, 0xb8, 0xa4, 0x11, 0x9f, 0xb6, 0xa5, 0x0d, 0x97, + 0x53, 0x29, 0xd5, 0x90, 0xff, 0x85, 0x56, 0x95, 0x9c, 0x5d, 0x78, 0xa1, 0xa0, 0x64, 0x3a, 0x64, + 0xc1, 0x95, 0x8e, 0xd9, 0x53, 0x72, 0x2b, 0xde, 0x74, 0xed, 0xbc, 0x30, 0x49, 0x8f, 0x69, 0xc4, + 0x5b, 0xa9, 0xda, 0x49, 0x5f, 0xdb, 0x27, 0x00, 0x2f, 0x16, 0x15, 0x0c, 0x97, 0x0b, 0xcf, 0x84, + 0xea, 0x24, 0xfa, 0xea, 0x56, 0x9b, 0xf5, 0x39, 0x13, 0x37, 0x3d, 0xf6, 0x23, 0xfe, 0x4a, 0x78, + 0x59, 0xe2, 0x89, 0xdd, 0x53, 0xf3, 0xe7, 0x32, 0x3c, 0xad, 0x38, 0xd1, 0x3b, 0x00, 0xab, 0x7a, + 0xbe, 0xd1, 0xad, 0x39, 0x40, 0x7f, 0x1b, 0xcb, 0xc2, 0x8b, 0x86, 0x6b, 0x7d, 0xe7, 0xfa, 0x9b, + 0x2f, 0x3f, 0xde, 0x2f, 0xd5, 0xd0, 0x16, 0x99, 0x61, 0xea, 0x61, 0xc3, 0x98, 0x5f, 0x41, 0xe9, + 0xc1, 0x2f, 0x87, 0xca, 0xf9, 0xaf, 0x1c, 0x2a, 0xef, 0xa7, 0x85, 0xa0, 0x8c, 0x49, 0x3e, 0x00, + 0xb8, 0x92, 0x4d, 0x1b, 0x22, 0x65, 0x3a, 0x05, 0x07, 0x58, 0xb7, 0x17, 0x4f, 0x30, 0x68, 0x3b, + 0x0a, 0xed, 0x2a, 0xaa, 0xcd, 0x41, 0xcb, 0x26, 0x1b, 0x7d, 0x04, 0xf0, 0xec, 0x74, 0xe6, 0x50, + 0xa9, 0x58, 0xd1, 0x00, 0x56, 0xe3, 0x1f, 0x32, 0x0c, 0xdf, 0x4d, 0xc5, 0xb7, 0x8d, 0xae, 0xcc, + 0xe1, 0xeb, 0xd1, 0x48, 0xdb, 0xda, 0x6d, 0x1f, 0x8e, 0x6d, 0x70, 0x34, 0xb6, 0xc1, 0xf7, 0xb1, + 0x0d, 0xde, 0x4e, 0xec, 0xca, 0xd1, 0xc4, 0xae, 0x7c, 0x9d, 0xd8, 0x95, 0x67, 0x77, 0xca, 0x5e, + 0x82, 0xd7, 0xc5, 0xc2, 0xea, 0x69, 0xf0, 0xab, 0xea, 0xd5, 0xdf, 0xfd, 0x15, 0x00, 0x00, 0xff, + 0xff, 0x06, 0x17, 0x60, 0x3b, 0xe8, 0x06, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1699,7 +1699,7 @@ func (m *QueryMainChainResponse) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Headers = append(m.Headers, &HeaderInfo{}) + m.Headers = append(m.Headers, &BTCHeaderInfo{}) if err := m.Headers[len(m.Headers)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } From a1a6aac4902ac6d943188d62707e3a2590115ee4 Mon Sep 17 00:00:00 2001 From: Vitalis Salis Date: Sat, 9 Jul 2022 14:34:54 +0300 Subject: [PATCH 09/16] Update comment --- x/btclightclient/keeper/state.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x/btclightclient/keeper/state.go b/x/btclightclient/keeper/state.go index 79c55437b..229c42d3c 100644 --- a/x/btclightclient/keeper/state.go +++ b/x/btclightclient/keeper/state.go @@ -266,7 +266,7 @@ func (s HeadersState) GetHighestCommonAncestor(header1 *wire.BlockHeader, header return resHeader, resHeight } -// GetInOrderAncestorsUntil returns the list of nodes starting from the node after `parent` until the `child` +// GetInOrderAncestorsUntil returns the list of nodes starting from the child and ending with the block *before* the `ancestor`. func (s HeadersState) GetInOrderAncestorsUntil(child *wire.BlockHeader, parent *wire.BlockHeader) []*wire.BlockHeader { currentHeader := child @@ -308,7 +308,7 @@ func (s HeadersState) TipExists() bool { // updateLongestChain checks whether the tip should be updated and returns true if it does func (s HeadersState) updateLongestChain(header *wire.BlockHeader, cumulativeWork *big.Int) { - // If there is no existing tip, then the header is set as the tip + // If there is no existing tip, then the header is set as the tiBTCp if !s.TipExists() { s.CreateTip(header) return From bd2a57127c6dc6a2016b5248dcaa11f0ab16178f Mon Sep 17 00:00:00 2001 From: Vitalis Salis Date: Sat, 9 Jul 2022 14:47:30 +0300 Subject: [PATCH 10/16] Rework logic and add corner cases --- x/btclightclient/keeper/keeper.go | 46 ++++++++++++++----------------- x/btclightclient/keeper/state.go | 19 +++++++------ x/btclightclient/keeper/utils.go | 8 ++++++ 3 files changed, 40 insertions(+), 33 deletions(-) diff --git a/x/btclightclient/keeper/keeper.go b/x/btclightclient/keeper/keeper.go index 99fe3ad80..59c64c02b 100644 --- a/x/btclightclient/keeper/keeper.go +++ b/x/btclightclient/keeper/keeper.go @@ -97,26 +97,30 @@ func (k Keeper) InsertHeader(ctx sdk.Context, header *wire.BlockHeader) error { // The tip has changed, we need to send events if !sameBlock(currentTip, previousTip) { + if !sameBlock(currentTip, header) { + panic("The tip was updated but with a different header than the one provided") + } tipHeight := parentHeight + 1 - // The new tip extends the old tip - if isParent(currentTip, previousTip) { - if !sameBlock(currentTip, header) { - panic("The header added on top of the previous tip is not the same as the provided header") - } - // If the new header extended - addedToMainChain = append(addedToMainChain, header) - } else { - // There has been a chain re-org - // Get the highest common ancestor between - hca, hcaHeight := k.HeadersState(ctx).GetHighestCommonAncestor(previousTip, header) - // Trigger a roll-back event to that ancestor + // Get the highest common ancestor between the new tip and the old tip + // There are two cases: + // 1. The new tip extends the old tip + // - The highest common ancestor is the old tip + // - No need to send a roll-back event + // 2. There has been a chain re-org + // - Need to send a roll-back event + hca := k.HeadersState(ctx).GetHighestCommonAncestor(previousTip, currentTip) + hcaHash := hca.BlockHash() + hcaHeight, err := k.HeadersState(ctx).GetHeaderHeight(&hcaHash) + if err != nil { + panic("Height for maintained header not available in storage") + } + if !isParent(currentTip, previousTip) { + // chain re-org: trigger a roll-back event to the highest common ancestor k.triggerRollBack(ctx, hca, hcaHeight) - - // Find the newly added headers to the main chain - addedToMainChain = k.HeadersState(ctx).GetInOrderAncestorsUntil(header, hca) } - // Iterate through the headers that were added to the main chain - // and trigger a roll-forward event + // Find the newly added headers to the main chain + addedToMainChain = k.HeadersState(ctx).GetInOrderAncestorsUntil(header, hca) + // Iterate through the added headers and trigger a roll-forward event for idx, added := range addedToMainChain { // tipHeight + 1 - len(addedToMainChain) -> height of the highest common ancestor addedHeight := tipHeight - uint64(len(addedToMainChain)) + 1 + uint64(idx) @@ -132,11 +136,3 @@ func (k Keeper) BlockHeight(ctx sdk.Context, header *wire.BlockHeader) (uint64, headerHash := header.BlockHash() return k.HeadersState(ctx).GetHeaderHeight(&headerHash) } - -func isParent(child *wire.BlockHeader, parent *wire.BlockHeader) bool { - return child.PrevBlock.String() == parent.BlockHash().String() -} - -func sameBlock(header1 *wire.BlockHeader, header2 *wire.BlockHeader) bool { - return header1.BlockHash().String() == header2.BlockHash().String() -} diff --git a/x/btclightclient/keeper/state.go b/x/btclightclient/keeper/state.go index 229c42d3c..518a3b9ed 100644 --- a/x/btclightclient/keeper/state.go +++ b/x/btclightclient/keeper/state.go @@ -211,7 +211,7 @@ func (s HeadersState) GetMainChain() []*wire.BlockHeader { // GetHighestCommonAncestor traverses the ancestors of both headers // to identify the common ancestor with the highest height -func (s HeadersState) GetHighestCommonAncestor(header1 *wire.BlockHeader, header2 *wire.BlockHeader) (*wire.BlockHeader, uint64) { +func (s HeadersState) GetHighestCommonAncestor(header1 *wire.BlockHeader, header2 *wire.BlockHeader) *wire.BlockHeader { // The algorithm works as follows: // 1. Initialize a hashmap hash -> bool denoting whether the hash // of an ancestor of either header1 or header2 has been encountered @@ -223,6 +223,12 @@ func (s HeadersState) GetHighestCommonAncestor(header1 *wire.BlockHeader, header // then that's the hash of the earliest ancestor // 5. Using the hash of the heighest ancestor wait until we get the header bytes // in order to avoid an extra access. + if isParent(header1, header2) { + return header2 + } + if isParent(header2, header1) { + return header1 + } ancestor1 := header1.BlockHash() ancestor2 := header2.BlockHash() var encountered map[string]bool @@ -231,18 +237,12 @@ func (s HeadersState) GetHighestCommonAncestor(header1 *wire.BlockHeader, header var found *chainhash.Hash = nil var resHeader *wire.BlockHeader = nil - var resHeight uint64 = 0 s.iterateReverseHeaders(func(btcdHeader *wire.BlockHeader) bool { // We have already found what we're looking for, no need to proceed further if found != nil { if *found == btcdHeader.BlockHash() { - height, err := s.GetHeaderHeight(found) - if err != nil { - panic("Height for header existing in storage not maintained") - } resHeader = btcdHeader - resHeight = height return true } } else { @@ -263,7 +263,7 @@ func (s HeadersState) GetHighestCommonAncestor(header1 *wire.BlockHeader, header } return false }) - return resHeader, resHeight + return resHeader } // GetInOrderAncestorsUntil returns the list of nodes starting from the child and ending with the block *before* the `ancestor`. @@ -272,6 +272,9 @@ func (s HeadersState) GetInOrderAncestorsUntil(child *wire.BlockHeader, parent * var ancestors []*wire.BlockHeader ancestors = append(ancestors, child) + if isParent(child, parent) { + return ancestors + } s.iterateReverseHeaders(func(header *wire.BlockHeader) bool { if header.BlockHash() == parent.BlockHash() { return true diff --git a/x/btclightclient/keeper/utils.go b/x/btclightclient/keeper/utils.go index 3c1b1035b..9a4261ceb 100644 --- a/x/btclightclient/keeper/utils.go +++ b/x/btclightclient/keeper/utils.go @@ -14,3 +14,11 @@ func blockHeaderFromStoredBytes(bz []byte) *wire.BlockHeader { // Convert the BTCHeaderBytes object into a *wire.BlockHeader object return headerBytes.ToBlockHeader() } + +func isParent(child *wire.BlockHeader, parent *wire.BlockHeader) bool { + return child.PrevBlock.String() == parent.BlockHash().String() +} + +func sameBlock(header1 *wire.BlockHeader, header2 *wire.BlockHeader) bool { + return header1.BlockHash().String() == header2.BlockHash().String() +} From e472254f837283fbfd0bcc39f9eed4402a6fd06f Mon Sep 17 00:00:00 2001 From: Vitalis Salis Date: Sat, 9 Jul 2022 14:50:33 +0300 Subject: [PATCH 11/16] Rework logic a little bit more --- x/btclightclient/keeper/keeper.go | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/x/btclightclient/keeper/keeper.go b/x/btclightclient/keeper/keeper.go index 59c64c02b..5d983b527 100644 --- a/x/btclightclient/keeper/keeper.go +++ b/x/btclightclient/keeper/keeper.go @@ -108,18 +108,23 @@ func (k Keeper) InsertHeader(ctx sdk.Context, header *wire.BlockHeader) error { // - No need to send a roll-back event // 2. There has been a chain re-org // - Need to send a roll-back event - hca := k.HeadersState(ctx).GetHighestCommonAncestor(previousTip, currentTip) - hcaHash := hca.BlockHash() - hcaHeight, err := k.HeadersState(ctx).GetHeaderHeight(&hcaHash) - if err != nil { - panic("Height for maintained header not available in storage") - } - if !isParent(currentTip, previousTip) { + var hca *wire.BlockHeader + var hcaHeight uint64 + if isParent(currentTip, previousTip) { + hca = previousTip + hcaHeight = parentHeight + } else { + hca := k.HeadersState(ctx).GetHighestCommonAncestor(previousTip, currentTip) + hcaHash := hca.BlockHash() + hcaHeight, err = k.HeadersState(ctx).GetHeaderHeight(&hcaHash) + if err != nil { + panic("Height for maintained header not available in storage") + } // chain re-org: trigger a roll-back event to the highest common ancestor k.triggerRollBack(ctx, hca, hcaHeight) } // Find the newly added headers to the main chain - addedToMainChain = k.HeadersState(ctx).GetInOrderAncestorsUntil(header, hca) + addedToMainChain = k.HeadersState(ctx).GetInOrderAncestorsUntil(currentTip, hca) // Iterate through the added headers and trigger a roll-forward event for idx, added := range addedToMainChain { // tipHeight + 1 - len(addedToMainChain) -> height of the highest common ancestor From 7abbb2657d74773accb621a7c6b965b9338f1a41 Mon Sep 17 00:00:00 2001 From: Vitalis Salis Date: Sat, 9 Jul 2022 15:19:29 +0300 Subject: [PATCH 12/16] Add explanatory comment --- x/btclightclient/keeper/state.go | 13 +++++++------ x/btclightclient/keeper/utils.go | 9 +++++++-- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/x/btclightclient/keeper/state.go b/x/btclightclient/keeper/state.go index 518a3b9ed..5ba22c7d6 100644 --- a/x/btclightclient/keeper/state.go +++ b/x/btclightclient/keeper/state.go @@ -239,12 +239,13 @@ func (s HeadersState) GetHighestCommonAncestor(header1 *wire.BlockHeader, header var resHeader *wire.BlockHeader = nil s.iterateReverseHeaders(func(btcdHeader *wire.BlockHeader) bool { - // We have already found what we're looking for, no need to proceed further - if found != nil { - if *found == btcdHeader.BlockHash() { - resHeader = btcdHeader - return true - } + // During iteration, we will encounter an ancestor for which its header hash + // has been set on the hash map. + // However, we do not have the entry yet, so we set the found flag to that hash + // and when we encounter it during iteration we return it. + if found != nil && sameHash(*found, btcdHeader.BlockHash()) { + resHeader = btcdHeader + return true } else { if ancestor1 == btcdHeader.BlockHash() { ancestor1 = btcdHeader.PrevBlock diff --git a/x/btclightclient/keeper/utils.go b/x/btclightclient/keeper/utils.go index 9a4261ceb..37b5175a5 100644 --- a/x/btclightclient/keeper/utils.go +++ b/x/btclightclient/keeper/utils.go @@ -2,6 +2,7 @@ package keeper import ( bbl "github.com/babylonchain/babylon/types" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" ) @@ -16,9 +17,13 @@ func blockHeaderFromStoredBytes(bz []byte) *wire.BlockHeader { } func isParent(child *wire.BlockHeader, parent *wire.BlockHeader) bool { - return child.PrevBlock.String() == parent.BlockHash().String() + return sameHash(child.PrevBlock, parent.BlockHash()) } func sameBlock(header1 *wire.BlockHeader, header2 *wire.BlockHeader) bool { - return header1.BlockHash().String() == header2.BlockHash().String() + return sameHash(header1.BlockHash(), header2.BlockHash()) +} + +func sameHash(hash1 chainhash.Hash, hash2 chainhash.Hash) bool { + return hash1.String() == hash2.String() } From 29df7cd536b9efa7cff03e54d987ee7f2297ca94 Mon Sep 17 00:00:00 2001 From: Vitalis Salis Date: Sat, 9 Jul 2022 15:25:04 +0300 Subject: [PATCH 13/16] Add HeaderKDeep method --- x/btclightclient/keeper/keeper.go | 14 ++++++++++++++ x/btclightclient/keeper/state.go | 1 + 2 files changed, 15 insertions(+) diff --git a/x/btclightclient/keeper/keeper.go b/x/btclightclient/keeper/keeper.go index 5d983b527..4c6a43b65 100644 --- a/x/btclightclient/keeper/keeper.go +++ b/x/btclightclient/keeper/keeper.go @@ -141,3 +141,17 @@ func (k Keeper) BlockHeight(ctx sdk.Context, header *wire.BlockHeader) (uint64, headerHash := header.BlockHash() return k.HeadersState(ctx).GetHeaderHeight(&headerHash) } + +// HeaderKDeep returns true if a header is at least k-deep on the main chain +func (k Keeper) HeaderKDeep(ctx sdk.Context, header *wire.BlockHeader, depth uint64) bool { + mainchain := k.HeadersState(ctx).GetMainChain() + // k-deep -> k headers built on top of the BTC header + // Discard the first `depth` headers + kDeepMainChain := mainchain[depth:] + for _, mainChainHeader := range kDeepMainChain { + if sameBlock(header, mainChainHeader) { + return true + } + } + return false +} diff --git a/x/btclightclient/keeper/state.go b/x/btclightclient/keeper/state.go index 5ba22c7d6..e42b37058 100644 --- a/x/btclightclient/keeper/state.go +++ b/x/btclightclient/keeper/state.go @@ -182,6 +182,7 @@ func (s HeadersState) GetDescendingHeaders() []*wire.BlockHeader { } // GetMainChain returns the current canonical chain as a collection of block headers +// starting from the tip and ending on the base header func (s HeadersState) GetMainChain() []*wire.BlockHeader { // If there is no tip, there is no base header if !s.TipExists() { From 3a4edab1191222f5109cb403c15f7bab6b49a51a Mon Sep 17 00:00:00 2001 From: Vitalis Salis Date: Sat, 9 Jul 2022 15:27:26 +0300 Subject: [PATCH 14/16] Check for longer length than mainchain --- x/btclightclient/keeper/keeper.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/x/btclightclient/keeper/keeper.go b/x/btclightclient/keeper/keeper.go index 4c6a43b65..36b22ca00 100644 --- a/x/btclightclient/keeper/keeper.go +++ b/x/btclightclient/keeper/keeper.go @@ -145,6 +145,9 @@ func (k Keeper) BlockHeight(ctx sdk.Context, header *wire.BlockHeader) (uint64, // HeaderKDeep returns true if a header is at least k-deep on the main chain func (k Keeper) HeaderKDeep(ctx sdk.Context, header *wire.BlockHeader, depth uint64) bool { mainchain := k.HeadersState(ctx).GetMainChain() + if depth > uint64(len(mainchain)) { + return false + } // k-deep -> k headers built on top of the BTC header // Discard the first `depth` headers kDeepMainChain := mainchain[depth:] From f764d2b2a93302a6f31d5b85c65011a4a7a712c3 Mon Sep 17 00:00:00 2001 From: Vitalis Salis Date: Sat, 9 Jul 2022 15:29:32 +0300 Subject: [PATCH 15/16] Add TODO --- x/btclightclient/keeper/keeper.go | 1 + 1 file changed, 1 insertion(+) diff --git a/x/btclightclient/keeper/keeper.go b/x/btclightclient/keeper/keeper.go index 36b22ca00..638032435 100644 --- a/x/btclightclient/keeper/keeper.go +++ b/x/btclightclient/keeper/keeper.go @@ -144,6 +144,7 @@ func (k Keeper) BlockHeight(ctx sdk.Context, header *wire.BlockHeader) (uint64, // HeaderKDeep returns true if a header is at least k-deep on the main chain func (k Keeper) HeaderKDeep(ctx sdk.Context, header *wire.BlockHeader, depth uint64) bool { + // TODO: optimize to not traverse the entire mainchain by storing the height along with the header mainchain := k.HeadersState(ctx).GetMainChain() if depth > uint64(len(mainchain)) { return false From ff8aaaf68dc9b717c6a9fb76740be6181e7d2d64 Mon Sep 17 00:00:00 2001 From: Vitalis Salis Date: Mon, 11 Jul 2022 14:50:27 +0300 Subject: [PATCH 16/16] Minor fixes --- proto/babylon/btclightclient/event.proto | 8 +++++++- x/btclightclient/keeper/state.go | 8 ++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/proto/babylon/btclightclient/event.proto b/proto/babylon/btclightclient/event.proto index 60d005996..4a4fb9e74 100644 --- a/proto/babylon/btclightclient/event.proto +++ b/proto/babylon/btclightclient/event.proto @@ -6,12 +6,18 @@ import "babylon/btclightclient/btclightclient.proto"; option go_package = "github.com/babylonchain/babylon/x/btclightclient/types"; -// EventBTCRollBack is emitted on Msg/InsertHeader +// The header included in the event is the block in the history +// of the current mainchain to which we are rolling back to. +// In other words, there is one rollback event emitted per re-org, to the +// greatest common ancestor of the old and the new fork. message EventBTCRollBack { BTCHeaderInfo header = 1; } // EventBTCRollForward is emitted on Msg/InsertHeader +// The header included in the event is the one the main chain is extended with. +// In the event of a reorg, each block on the new fork that comes after +// the greatest common ancestor will have a corresponding roll forward event. message EventBTCRollForward { BTCHeaderInfo header = 1; } diff --git a/x/btclightclient/keeper/state.go b/x/btclightclient/keeper/state.go index e42b37058..8910058f8 100644 --- a/x/btclightclient/keeper/state.go +++ b/x/btclightclient/keeper/state.go @@ -269,16 +269,16 @@ func (s HeadersState) GetHighestCommonAncestor(header1 *wire.BlockHeader, header } // GetInOrderAncestorsUntil returns the list of nodes starting from the child and ending with the block *before* the `ancestor`. -func (s HeadersState) GetInOrderAncestorsUntil(child *wire.BlockHeader, parent *wire.BlockHeader) []*wire.BlockHeader { +func (s HeadersState) GetInOrderAncestorsUntil(child *wire.BlockHeader, ancestor *wire.BlockHeader) []*wire.BlockHeader { currentHeader := child var ancestors []*wire.BlockHeader ancestors = append(ancestors, child) - if isParent(child, parent) { + if isParent(child, ancestor) { return ancestors } s.iterateReverseHeaders(func(header *wire.BlockHeader) bool { - if header.BlockHash() == parent.BlockHash() { + if header.BlockHash() == ancestor.BlockHash() { return true } if header.BlockHash().String() == currentHeader.PrevBlock.String() { @@ -313,7 +313,7 @@ func (s HeadersState) TipExists() bool { // updateLongestChain checks whether the tip should be updated and returns true if it does func (s HeadersState) updateLongestChain(header *wire.BlockHeader, cumulativeWork *big.Int) { - // If there is no existing tip, then the header is set as the tiBTCp + // If there is no existing tip, then the header is set as the tip if !s.TipExists() { s.CreateTip(header) return