Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: eots-manager add eots verifier #360

Open
wants to merge 13 commits into
base: base/consumer-chain-support
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,31 @@ orbs:
aws-ecr: circleci/[email protected]

jobs:
check-mock-gen:
machine:
image: ubuntu-2204:2024.01.1
steps:
- checkout
- go/install:
version: "1.21.4"
- go/load-cache:
key: go-mod-v6-{{ checksum "go.sum" }}
- go/mod-download
- go/save-cache:
key: go-mod-v6-{{ checksum "go.sum" }}
path: "/home/circleci/.go_workspace/pkg/mod"
- run:
name: Run make mock-gen
command: |
make mock-gen
- run:
name: Check for uncommitted changes
command: |
if ! git diff --exit-code; then
echo "Uncommitted changes detected. Please run 'make mock-gen' before committing."
exit 1
fi

build_lint_test:
machine:
image: ubuntu-2204:2024.01.1
Expand All @@ -18,6 +43,7 @@ jobs:
command: "go env"
- go/load-cache:
key: go-mod-v6-{{ checksum "go.sum" }}
- add_ssh_keys
- go/mod-download
- go/save-cache:
key: go-mod-v6-{{ checksum "go.sum" }}
Expand Down Expand Up @@ -45,13 +71,15 @@ jobs:
resource_class: large
steps:
- checkout
- add_ssh_keys
- aws-ecr/build-image:
push-image: false
dockerfile: Dockerfile
path: ./
build-path: ./
tag: "$CIRCLE_SHA1,$CIRCLE_TAG"
repo: "$CIRCLE_PROJECT_REPONAME"
extra-build-args: "--secret id=sshKey,src=/home/circleci/.ssh/$DEPLOY_KEY_NAME"
- run:
name: Save Docker image to export it to workspace
command: |
Expand Down Expand Up @@ -85,6 +113,7 @@ jobs:
workflows:
CI:
jobs:
- check-mock-gen
- build_lint_test
- build_docker:
filters:
Expand All @@ -100,3 +129,4 @@ workflows:
only:
- main
- dev
- base/consumer-chain-support
6 changes: 5 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,15 @@ RUN apk add --no-cache --update openssh git make build-base linux-headers libc-d
libzmq-static libsodium-static gcc


RUN mkdir -p /root/.ssh && ssh-keyscan github.com >> /root/.ssh/known_hosts
RUN git config --global url."[email protected]:".insteadOf "https://github.com/"
ENV GOPRIVATE=github.com/babylonchain/*

# Build
WORKDIR /go/src/github.com/babylonchain/finality-provider
# Cache dependencies
COPY go.mod go.sum /go/src/github.com/babylonchain/finality-provider/
RUN go mod download
RUN --mount=type=secret,id=sshKey,target=/root/.ssh/id_rsa go mod download
# Copy the rest of the files
COPY ./ /go/src/github.com/babylonchain/finality-provider/

Expand Down
5 changes: 3 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,12 @@ $(BUILDDIR)/:
mkdir -p $(BUILDDIR)/

build-docker:
$(DOCKER) build --tag babylonchain/finality-provider -f Dockerfile \
$(DOCKER) build --secret id=sshKey,src=${BBN_PRIV_DEPLOY_KEY} --tag babylonchain/finality-provider -f Dockerfile \
$(shell git rev-parse --show-toplevel)

.PHONY: build build-docker

.PHONY: test
test:
go test ./...

Expand All @@ -75,7 +76,7 @@ proto-gen:

mock-gen:
mkdir -p $(MOCKS_DIR)
$(MOCKGEN_CMD) -source=clientcontroller/interface.go -package mocks -destination $(MOCKS_DIR)/babylon.go
$(MOCKGEN_CMD) -source=clientcontroller/interface.go -package mocks -destination $(MOCKS_DIR)/clientcontroller.go

.PHONY: mock-gen

Expand Down
185 changes: 20 additions & 165 deletions clientcontroller/babylon.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package clientcontroller
import (
"context"
"fmt"
"time"

sdkErr "cosmossdk.io/errors"
"github.com/btcsuite/btcd/btcec/v2"
Expand All @@ -15,8 +14,8 @@ import (
btcctypes "github.com/babylonchain/babylon/x/btccheckpoint/types"
btclctypes "github.com/babylonchain/babylon/x/btclightclient/types"
btcstakingtypes "github.com/babylonchain/babylon/x/btcstaking/types"
bsctypes "github.com/babylonchain/babylon/x/btcstkconsumer/types"
ckpttypes "github.com/babylonchain/babylon/x/checkpointing/types"
finalitytypes "github.com/babylonchain/babylon/x/finality/types"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/btcsuite/btcd/chaincfg"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
Expand Down Expand Up @@ -112,6 +111,7 @@ func (bc *BabylonController) reliablySendMsgs(msgs []sdk.Msg, expectedErrs []*sd
// RegisterFinalityProvider registers a finality provider via a MsgCreateFinalityProvider to Babylon
// it returns tx hash, registered epoch, and error
func (bc *BabylonController) RegisterFinalityProvider(
chainID string,
chainPk []byte,
fpPk *btcec.PublicKey,
pop []byte,
Expand All @@ -137,6 +137,7 @@ func (bc *BabylonController) RegisterFinalityProvider(
Commission: commission,
Description: &sdkDescription,
MasterPubRand: masterPubRand,
ConsumerId: chainID,
}

res, err := bc.reliablySendMsg(msg, emptyErrs, emptyErrs)
Expand All @@ -152,62 +153,6 @@ func (bc *BabylonController) RegisterFinalityProvider(
return &types.TxResponse{TxHash: res.TxHash, Events: res.Events}, registeredEpoch, nil
}

// SubmitFinalitySig submits the finality signature via a MsgAddVote to Babylon
func (bc *BabylonController) SubmitFinalitySig(fpPk *btcec.PublicKey, blockHeight uint64, blockHash []byte, sig *btcec.ModNScalar) (*types.TxResponse, error) {
msg := &finalitytypes.MsgAddFinalitySig{
Signer: bc.mustGetTxSigner(),
FpBtcPk: bbntypes.NewBIP340PubKeyFromBTCPK(fpPk),
BlockHeight: blockHeight,
BlockAppHash: blockHash,
FinalitySig: bbntypes.NewSchnorrEOTSSigFromModNScalar(sig),
}

unrecoverableErrs := []*sdkErr.Error{
finalitytypes.ErrInvalidFinalitySig,
finalitytypes.ErrPubRandNotFound,
btcstakingtypes.ErrFpAlreadySlashed,
}

res, err := bc.reliablySendMsg(msg, emptyErrs, unrecoverableErrs)
if err != nil {
return nil, err
}

return &types.TxResponse{TxHash: res.TxHash, Events: res.Events}, nil
}

// SubmitBatchFinalitySigs submits a batch of finality signatures to Babylon
func (bc *BabylonController) SubmitBatchFinalitySigs(fpPk *btcec.PublicKey, blocks []*types.BlockInfo, sigs []*btcec.ModNScalar) (*types.TxResponse, error) {
if len(blocks) != len(sigs) {
return nil, fmt.Errorf("the number of blocks %v should match the number of finality signatures %v", len(blocks), len(sigs))
}

msgs := make([]sdk.Msg, 0, len(blocks))
for i, b := range blocks {
msg := &finalitytypes.MsgAddFinalitySig{
Signer: bc.mustGetTxSigner(),
FpBtcPk: bbntypes.NewBIP340PubKeyFromBTCPK(fpPk),
BlockHeight: b.Height,
BlockAppHash: b.Hash,
FinalitySig: bbntypes.NewSchnorrEOTSSigFromModNScalar(sigs[i]),
}
msgs = append(msgs, msg)
}

unrecoverableErrs := []*sdkErr.Error{
finalitytypes.ErrInvalidFinalitySig,
finalitytypes.ErrPubRandNotFound,
btcstakingtypes.ErrFpAlreadySlashed,
}

res, err := bc.reliablySendMsgs(msgs, emptyErrs, unrecoverableErrs)
if err != nil {
return nil, err
}

return &types.TxResponse{TxHash: res.TxHash, Events: res.Events}, nil
}

func (bc *BabylonController) QueryFinalityProviderSlashed(fpPk *btcec.PublicKey) (bool, error) {
fpPubKey := bbntypes.NewBIP340PubKeyFromBTCPK(fpPk)
res, err := bc.bbnClient.QueryClient.FinalityProvider(fpPubKey.MarshalHex())
Expand All @@ -220,19 +165,6 @@ func (bc *BabylonController) QueryFinalityProviderSlashed(fpPk *btcec.PublicKey)
return slashed, nil
}

// QueryFinalityProviderVotingPower queries the voting power of the finality provider at a given height
func (bc *BabylonController) QueryFinalityProviderVotingPower(fpPk *btcec.PublicKey, blockHeight uint64) (uint64, error) {
res, err := bc.bbnClient.QueryClient.FinalityProviderPowerAtHeight(
bbntypes.NewBIP340PubKeyFromBTCPK(fpPk).MarshalHex(),
blockHeight,
)
if err != nil {
return 0, fmt.Errorf("failed to query the finality provider's voting power at height %d: %w", blockHeight, err)
}

return res.VotingPower, nil
}

// QueryFinalityProviderRegisteredEpoch queries the registered epoch of the finality provider
func (bc *BabylonController) QueryFinalityProviderRegisteredEpoch(fpPk *btcec.PublicKey) (uint64, error) {
res, err := bc.bbnClient.QueryClient.FinalityProvider(
Expand All @@ -245,10 +177,6 @@ func (bc *BabylonController) QueryFinalityProviderRegisteredEpoch(fpPk *btcec.Pu
return res.FinalityProvider.RegisteredEpoch, nil
}

func (bc *BabylonController) QueryLatestFinalizedBlocks(count uint64) ([]*types.BlockInfo, error) {
return bc.queryLatestBlocks(nil, count, finalitytypes.QueriedBlockStatus_FINALIZED, true)
}

func (bc *BabylonController) QueryLastFinalizedEpoch() (uint64, error) {
resp, err := bc.bbnClient.LatestEpochFromStatus(ckpttypes.Finalized)
if err != nil {
Expand All @@ -257,96 +185,6 @@ func (bc *BabylonController) QueryLastFinalizedEpoch() (uint64, error) {
return resp.RawCheckpoint.EpochNum, nil
}

func (bc *BabylonController) QueryBlocks(startHeight, endHeight, limit uint64) ([]*types.BlockInfo, error) {
if endHeight < startHeight {
return nil, fmt.Errorf("the startHeight %v should not be higher than the endHeight %v", startHeight, endHeight)
}
count := endHeight - startHeight + 1
if count > limit {
count = limit
}
return bc.queryLatestBlocks(sdk.Uint64ToBigEndian(startHeight), count, finalitytypes.QueriedBlockStatus_ANY, false)
}

func (bc *BabylonController) queryLatestBlocks(startKey []byte, count uint64, status finalitytypes.QueriedBlockStatus, reverse bool) ([]*types.BlockInfo, error) {
var blocks []*types.BlockInfo
pagination := &sdkquery.PageRequest{
Limit: count,
Reverse: reverse,
Key: startKey,
}

res, err := bc.bbnClient.QueryClient.ListBlocks(status, pagination)
if err != nil {
return nil, fmt.Errorf("failed to query finalized blocks: %v", err)
}

for _, b := range res.Blocks {
ib := &types.BlockInfo{
Height: b.Height,
Hash: b.AppHash,
}
blocks = append(blocks, ib)
}

return blocks, nil
}

func getContextWithCancel(timeout time.Duration) (context.Context, context.CancelFunc) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
return ctx, cancel
}

func (bc *BabylonController) QueryBlock(height uint64) (*types.BlockInfo, error) {
res, err := bc.bbnClient.QueryClient.Block(height)
if err != nil {
return nil, fmt.Errorf("failed to query indexed block at height %v: %w", height, err)
}

return &types.BlockInfo{
Height: height,
Hash: res.Block.AppHash,
Finalized: res.Block.Finalized,
}, nil
}

func (bc *BabylonController) QueryActivatedHeight() (uint64, error) {
res, err := bc.bbnClient.QueryClient.ActivatedHeight()
if err != nil {
return 0, fmt.Errorf("failed to query activated height: %w", err)
}

return res.Height, nil
}

func (bc *BabylonController) QueryBestBlock() (*types.BlockInfo, error) {
blocks, err := bc.queryLatestBlocks(nil, 1, finalitytypes.QueriedBlockStatus_ANY, true)
if err != nil || len(blocks) != 1 {
// try query comet block if the index block query is not available
return bc.queryCometBestBlock()
}

return blocks[0], nil
}

func (bc *BabylonController) queryCometBestBlock() (*types.BlockInfo, error) {
ctx, cancel := getContextWithCancel(bc.cfg.Timeout)
// this will return 20 items at max in the descending order (highest first)
chainInfo, err := bc.bbnClient.RPCClient.BlockchainInfo(ctx, 0, 0)
defer cancel()

if err != nil {
return nil, err
}

// Returning response directly, if header with specified number did not exist
// at request will contain nil header
return &types.BlockInfo{
Height: uint64(chainInfo.BlockMetas[0].Header.Height),
Hash: chainInfo.BlockMetas[0].Header.AppHash,
}, nil
}

func (bc *BabylonController) Close() error {
if !bc.bbnClient.IsRunning() {
return nil
Expand Down Expand Up @@ -564,3 +402,20 @@ func (bc *BabylonController) InsertSpvProofs(submitter string, proofs []*btcctyp

return res, nil
}

// RegisterConsumerChain registers a consumer chain via a MsgRegisterChain to Babylon
func (bc *BabylonController) RegisterConsumerChain(id, name, description string) (*types.TxResponse, error) {
msg := &bsctypes.MsgRegisterConsumer{
Signer: bc.mustGetTxSigner(),
ConsumerId: id,
ConsumerName: name,
ConsumerDescription: description,
}

res, err := bc.reliablySendMsg(msg, emptyErrs, emptyErrs)
if err != nil {
return nil, err
}

return &types.TxResponse{TxHash: res.TxHash, Events: res.Events}, nil
}
Loading