From 60e7b507cdc66a3dc03e68e4434b1688c6801244 Mon Sep 17 00:00:00 2001 From: Randy Grok Date: Thu, 3 Jul 2025 11:47:14 +0200 Subject: [PATCH 01/17] include attester system commit rpc --- pkg/adapter/adapter.go | 4 - pkg/rpc/core/blocks.go | 228 +++++++++++++++++++++++++++++------- pkg/rpc/core/blocks_test.go | 2 +- 3 files changed, 188 insertions(+), 46 deletions(-) diff --git a/pkg/adapter/adapter.go b/pkg/adapter/adapter.go index b204c2bc..6f73ef29 100644 --- a/pkg/adapter/adapter.go +++ b/pkg/adapter/adapter.go @@ -731,7 +731,3 @@ outerLoop: a.Logger.Debug("remaining stack after soft consensus", "count", len(a.stackedEvents), "soft_consensus", softCommitHeight) return nil } - -func (a *Adapter) GetExecutionMode() execution.ExecutionMode { - return execution.ExecutionModeDelayed -} diff --git a/pkg/rpc/core/blocks.go b/pkg/rpc/core/blocks.go index a05738a8..69b7e925 100644 --- a/pkg/rpc/core/blocks.go +++ b/pkg/rpc/core/blocks.go @@ -1,6 +1,7 @@ package core import ( + "context" "encoding/binary" "errors" "fmt" @@ -16,6 +17,8 @@ import ( storepkg "github.com/rollkit/rollkit/pkg/store" rlktypes "github.com/rollkit/rollkit/types" + abci "github.com/cometbft/cometbft/abci/types" + networktypes "github.com/rollkit/go-execution-abci/modules/network/types" "github.com/rollkit/go-execution-abci/pkg/cometcompat" ) @@ -214,57 +217,30 @@ func Commit(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultCommit, erro return nil, err } - header, rollkitData, err := env.Adapter.RollkitStore.GetBlockData(ctx.Context(), height) + // Check if the block has soft confirmation first + isSoftConfirmed, softConfirmationData, err := checkSoftConfirmation(ctx.Context(), height) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to check soft confirmation status: %w", err) } - // Create a proper commit that will be used for ToABCIBlock - abciCommit := &cmttypes.Commit{ - Height: int64(header.Height()), //nolint:gosec - Round: 0, - BlockID: cmttypes.BlockID{ - Hash: cmbytes.HexBytes(header.Hash()), // This will be updated after ToABCIBlock - PartSetHeader: cmttypes.PartSetHeader{}, - }, - Signatures: []cmttypes.CommitSig{{ - BlockIDFlag: cmttypes.BlockIDFlagCommit, - Signature: header.Signature, // This will be updated if we have a signer - ValidatorAddress: header.ProposerAddress, - Timestamp: header.Time(), - }}, + if !isSoftConfirmed { + return nil, fmt.Errorf("commit for height %d does not exist (block not soft confirmed)", height) } - block, err := cometcompat.ToABCIBlock(header, rollkitData, abciCommit) + header, rollkitData, err := env.Adapter.RollkitStore.GetBlockData(ctx.Context(), height) if err != nil { return nil, err } - // Then re-sign the final ABCI header if we have a signer - if env.Signer != nil { - // Create a vote for the final ABCI header - vote := cmtproto.Vote{ - Type: cmtproto.PrecommitType, - Height: int64(header.Height()), //nolint:gosec - Round: 0, - BlockID: cmtproto.BlockID{ - Hash: block.Header.Hash(), - PartSetHeader: cmtproto.PartSetHeader{}, - }, - Timestamp: block.Time, - ValidatorAddress: header.ProposerAddress, - ValidatorIndex: 0, - } - chainID := header.ChainID() - finalSignBytes := cmttypes.VoteSignBytes(chainID, &vote) - - newSignature, err := env.Signer.Sign(finalSignBytes) - if err != nil { - return nil, fmt.Errorf("failed to sign final ABCI header: %w", err) - } + // Build commit with attestations from soft confirmation data + commit, err := buildCommitFromAttestations(ctx.Context(), header, softConfirmationData) + if err != nil { + return nil, fmt.Errorf("failed to build commit from attestations: %w", err) + } - // Update the commit with the new signature - block.LastCommit.Signatures[0].Signature = newSignature + block, err := cometcompat.ToABCIBlock(header, rollkitData, commit) + if err != nil { + return nil, err } // Update the commit's BlockID to match the final ABCI block hash @@ -391,3 +367,173 @@ func BlockchainInfo(ctx *rpctypes.Context, minHeight, maxHeight int64) (*ctypes. BlockMetas: blocks, }, nil } + +// checkSoftConfirmation checks if a block has soft confirmation and returns the attestation data +func checkSoftConfirmation(ctx context.Context, height uint64) (bool, *networktypes.QueryAttestationBitmapResponse, error) { + // Check soft confirmation status + softConfirmReq := &networktypes.QuerySoftConfirmationStatusRequest{ + Height: int64(height), + } + reqData, err := softConfirmReq.Marshal() + if err != nil { + return false, nil, fmt.Errorf("failed to marshal soft confirmation request: %w", err) + } + + // Query soft confirmation status + abciReq := &abci.RequestQuery{ + Path: "/rollkitsdk.network.v1.Query/SoftConfirmationStatus", + Data: reqData, + } + + abciRes, err := env.Adapter.App.Query(ctx, abciReq) + if err != nil || abciRes.Code != 0 { + var msg string + if abciRes != nil { + msg = abciRes.Log + } + env.Logger.Error("query soft confirmation status", "height", height, "error", err, "log", msg) + return false, nil, fmt.Errorf("failed to query soft confirmation status: %w", err) + } + + softConfirmResp := &networktypes.QuerySoftConfirmationStatusResponse{} + if err := softConfirmResp.Unmarshal(abciRes.Value); err != nil { + return false, nil, fmt.Errorf("failed to unmarshal soft confirmation response: %w", err) + } + + if !softConfirmResp.IsSoftConfirmed { + return false, nil, nil + } + + // Get attestation bitmap data + attestationReq := &networktypes.QueryAttestationBitmapRequest{ + Height: int64(height), + } + reqData, err = attestationReq.Marshal() + if err != nil { + return false, nil, fmt.Errorf("failed to marshal attestation bitmap request: %w", err) + } + + abciReq = &abci.RequestQuery{ + Path: "/rollkitsdk.network.v1.Query/AttestationBitmap", + Data: reqData, + } + + abciRes, err = env.Adapter.App.Query(ctx, abciReq) + if err != nil || abciRes.Code != 0 { + var msg string + if abciRes != nil { + msg = abciRes.Log + } + env.Logger.Error("query attestation bitmap", "height", height, "error", err, "log", msg) + return false, nil, fmt.Errorf("failed to query attestation bitmap: %w", err) + } + + attestationResp := &networktypes.QueryAttestationBitmapResponse{} + if err := attestationResp.Unmarshal(abciRes.Value); err != nil { + return false, nil, fmt.Errorf("failed to unmarshal attestation bitmap response: %w", err) + } + + return true, attestationResp, nil +} + +// buildCommitFromAttestations constructs a CometBFT commit using the attestations from the network module +func buildCommitFromAttestations(ctx context.Context, header *rlktypes.SignedHeader, attestationData *networktypes.QueryAttestationBitmapResponse) (*cmttypes.Commit, error) { + if attestationData == nil || attestationData.Bitmap == nil { + return nil, fmt.Errorf("no attestation data available") + } + + // Get validator set to build signatures + validators, err := getValidatorSet(ctx, header.Height()) + if err != nil { + return nil, fmt.Errorf("failed to get validator set: %w", err) + } + + bitmap := attestationData.Bitmap.Bitmap + signatures := make([]cmttypes.CommitSig, len(validators)) + + // Build signatures from attestations + for i, validator := range validators { + // Check if validator attested (bit is set in bitmap) + byteIndex := i / 8 + bitIndex := i % 8 + + if byteIndex >= len(bitmap) { + // Validator didn't attest - create absent signature + signatures[i] = cmttypes.CommitSig{ + BlockIDFlag: cmttypes.BlockIDFlagAbsent, + ValidatorAddress: validator.Address, + Timestamp: header.Time(), + Signature: nil, + } + continue + } + + if (bitmap[byteIndex] & (1 << bitIndex)) != 0 { + // Validator attested - get their signature + signature, err := getValidatorSignature(ctx, int64(header.Height()), validator.Address.String()) + if err != nil { + env.Logger.Error("failed to get validator signature", "height", header.Height(), "validator", validator.Address, "error", err) + // Create absent signature if we can't get the actual one + signatures[i] = cmttypes.CommitSig{ + BlockIDFlag: cmttypes.BlockIDFlagAbsent, + ValidatorAddress: validator.Address, + Timestamp: header.Time(), + Signature: nil, + } + continue + } + + signatures[i] = cmttypes.CommitSig{ + BlockIDFlag: cmttypes.BlockIDFlagCommit, + ValidatorAddress: validator.Address, + Timestamp: header.Time(), + Signature: signature, + } + } else { + // Validator didn't attest + signatures[i] = cmttypes.CommitSig{ + BlockIDFlag: cmttypes.BlockIDFlagAbsent, + ValidatorAddress: validator.Address, + Timestamp: header.Time(), + Signature: nil, + } + } + } + + return &cmttypes.Commit{ + Height: int64(header.Height()), + Round: 0, + BlockID: cmttypes.BlockID{ + Hash: cmbytes.HexBytes(header.Hash()), + PartSetHeader: cmttypes.PartSetHeader{}, + }, + Signatures: signatures, + }, nil +} + +// getValidatorSet retrieves the validator set for a given height +func getValidatorSet(ctx context.Context, height uint64) ([]*cmttypes.Validator, error) { + // For now, we'll use the genesis validator set as a simple implementation + // In a full implementation, this should get the actual validator set at the given height + genesisValidators := env.Adapter.AppGenesis.Consensus.Validators + validators := make([]*cmttypes.Validator, len(genesisValidators)) + + for i, gval := range genesisValidators { + validators[i] = &cmttypes.Validator{ + Address: gval.Address, + PubKey: gval.PubKey, + VotingPower: gval.Power, + } + } + + return validators, nil +} + +// getValidatorSignature retrieves the signature for a specific validator at a given height +func getValidatorSignature(ctx context.Context, height int64, validatorAddr string) ([]byte, error) { + // This would query the network module to get the stored signature for this validator + // Since we don't have direct access to the keeper, we'd need to implement a query for this + // For now, return an empty signature - this should be implemented with proper signature retrieval + env.Logger.Debug("getting validator signature", "height", height, "validator", validatorAddr) + return []byte{}, nil +} diff --git a/pkg/rpc/core/blocks_test.go b/pkg/rpc/core/blocks_test.go index c2e7bece..125267d8 100644 --- a/pkg/rpc/core/blocks_test.go +++ b/pkg/rpc/core/blocks_test.go @@ -256,7 +256,7 @@ func signBlock(t *testing.T, header types.Header, data *types.Data, privKey cryp Signature: types.Signature(make([]byte, 64)), } - abciBlock, err := cometcompat.ToABCIBlock(tempSignedHeader, data, tempCommit, nil) // todo (Alex): set correct valset + abciBlock, err := cometcompat.ToABCIBlock(tempSignedHeader, data, tempCommit) require.NoError(t, err) vote := cmtproto.Vote{ From ebbca01c7958c8f3d213a19d62008e36f3aca251 Mon Sep 17 00:00:00 2001 From: Randy Grok Date: Fri, 4 Jul 2025 09:32:58 +0200 Subject: [PATCH 02/17] build commit from attestations! --- modules/network/keeper/grpc_query.go | 25 + modules/network/types/query.pb.go | 570 ++++++++++++++++-- modules/network/types/query.pb.gw.go | 123 ++++ .../proto/rollkitsdk/network/v1/query.proto | 33 +- pkg/rpc/core/blocks.go | 175 +++--- 5 files changed, 784 insertions(+), 142 deletions(-) diff --git a/modules/network/keeper/grpc_query.go b/modules/network/keeper/grpc_query.go index ca53e7b0..8b22072d 100644 --- a/modules/network/keeper/grpc_query.go +++ b/modules/network/keeper/grpc_query.go @@ -190,3 +190,28 @@ func (q *queryServer) SoftConfirmationStatus(c context.Context, req *types.Query QuorumFraction: q.keeper.GetParams(ctx).QuorumFraction, }, nil } + +// ValidatorSignature queries the signature of a validator for a specific height +func (q *queryServer) ValidatorSignature(c context.Context, req *types.QueryValidatorSignatureRequest) (*types.QueryValidatorSignatureResponse, error) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + ctx := sdk.UnwrapSDKContext(c) + + signature, err := q.keeper.GetSignature(ctx, req.Height, req.Validator) + if err != nil { + if errors.Is(err, collections.ErrNotFound) { + return &types.QueryValidatorSignatureResponse{ + Signature: nil, + Found: false, + }, nil + } + return nil, status.Error(codes.Internal, fmt.Sprintf("failed to get signature: %v", err)) + } + + return &types.QueryValidatorSignatureResponse{ + Signature: signature, + Found: true, + }, nil +} diff --git a/modules/network/types/query.pb.go b/modules/network/types/query.pb.go index 0428c662..d2519c01 100644 --- a/modules/network/types/query.pb.go +++ b/modules/network/types/query.pb.go @@ -537,6 +537,112 @@ func (m *QuerySoftConfirmationStatusResponse) GetQuorumFraction() string { return "" } +// QueryValidatorSignatureRequest is the request type for the Query/ValidatorSignature RPC method. +type QueryValidatorSignatureRequest struct { + Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` + Validator string `protobuf:"bytes,2,opt,name=validator,proto3" json:"validator,omitempty"` +} + +func (m *QueryValidatorSignatureRequest) Reset() { *m = QueryValidatorSignatureRequest{} } +func (m *QueryValidatorSignatureRequest) String() string { return proto.CompactTextString(m) } +func (*QueryValidatorSignatureRequest) ProtoMessage() {} +func (*QueryValidatorSignatureRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_bae17cb7b1e48f90, []int{10} +} +func (m *QueryValidatorSignatureRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryValidatorSignatureRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryValidatorSignatureRequest.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 *QueryValidatorSignatureRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryValidatorSignatureRequest.Merge(m, src) +} +func (m *QueryValidatorSignatureRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryValidatorSignatureRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryValidatorSignatureRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryValidatorSignatureRequest proto.InternalMessageInfo + +func (m *QueryValidatorSignatureRequest) GetHeight() int64 { + if m != nil { + return m.Height + } + return 0 +} + +func (m *QueryValidatorSignatureRequest) GetValidator() string { + if m != nil { + return m.Validator + } + return "" +} + +// QueryValidatorSignatureResponse is the response type for the Query/ValidatorSignature RPC method. +type QueryValidatorSignatureResponse struct { + Signature []byte `protobuf:"bytes,1,opt,name=signature,proto3" json:"signature,omitempty"` + Found bool `protobuf:"varint,2,opt,name=found,proto3" json:"found,omitempty"` +} + +func (m *QueryValidatorSignatureResponse) Reset() { *m = QueryValidatorSignatureResponse{} } +func (m *QueryValidatorSignatureResponse) String() string { return proto.CompactTextString(m) } +func (*QueryValidatorSignatureResponse) ProtoMessage() {} +func (*QueryValidatorSignatureResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_bae17cb7b1e48f90, []int{11} +} +func (m *QueryValidatorSignatureResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryValidatorSignatureResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryValidatorSignatureResponse.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 *QueryValidatorSignatureResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryValidatorSignatureResponse.Merge(m, src) +} +func (m *QueryValidatorSignatureResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryValidatorSignatureResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryValidatorSignatureResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryValidatorSignatureResponse proto.InternalMessageInfo + +func (m *QueryValidatorSignatureResponse) GetSignature() []byte { + if m != nil { + return m.Signature + } + return nil +} + +func (m *QueryValidatorSignatureResponse) GetFound() bool { + if m != nil { + return m.Found + } + return false +} + func init() { proto.RegisterType((*QueryParamsRequest)(nil), "rollkitsdk.network.v1.QueryParamsRequest") proto.RegisterType((*QueryParamsResponse)(nil), "rollkitsdk.network.v1.QueryParamsResponse") @@ -548,64 +654,72 @@ func init() { proto.RegisterType((*QueryValidatorIndexResponse)(nil), "rollkitsdk.network.v1.QueryValidatorIndexResponse") proto.RegisterType((*QuerySoftConfirmationStatusRequest)(nil), "rollkitsdk.network.v1.QuerySoftConfirmationStatusRequest") proto.RegisterType((*QuerySoftConfirmationStatusResponse)(nil), "rollkitsdk.network.v1.QuerySoftConfirmationStatusResponse") + proto.RegisterType((*QueryValidatorSignatureRequest)(nil), "rollkitsdk.network.v1.QueryValidatorSignatureRequest") + proto.RegisterType((*QueryValidatorSignatureResponse)(nil), "rollkitsdk.network.v1.QueryValidatorSignatureResponse") } func init() { proto.RegisterFile("rollkitsdk/network/v1/query.proto", fileDescriptor_bae17cb7b1e48f90) } var fileDescriptor_bae17cb7b1e48f90 = []byte{ - // 831 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x55, 0x41, 0x6f, 0x1b, 0x45, - 0x14, 0xf6, 0x26, 0xb6, 0xc1, 0x2f, 0x55, 0x4b, 0xa6, 0x6e, 0xb0, 0x96, 0xc6, 0x6d, 0xb6, 0x42, - 0x75, 0x0c, 0xf6, 0xd6, 0x01, 0x8a, 0x5a, 0x38, 0x40, 0x10, 0x88, 0xde, 0xca, 0x16, 0x71, 0xe8, - 0xc5, 0x1a, 0xef, 0x8e, 0xd7, 0xa3, 0xd8, 0x3b, 0x9b, 0x9d, 0x59, 0xb7, 0x55, 0x94, 0x0b, 0x1c, - 0xb9, 0x20, 0xf5, 0x4f, 0x70, 0xe6, 0xcc, 0x9d, 0x5e, 0x90, 0x2a, 0x71, 0xe1, 0x84, 0x50, 0xc2, - 0x0f, 0x41, 0xfb, 0x66, 0xbc, 0xb1, 0x93, 0xb5, 0x69, 0x39, 0xd9, 0xf3, 0xcd, 0xf7, 0xbd, 0xf7, - 0xbd, 0x99, 0xf7, 0x66, 0x61, 0x27, 0x11, 0xe3, 0xf1, 0x01, 0x57, 0x32, 0x38, 0x70, 0x23, 0xa6, - 0x9e, 0x88, 0xe4, 0xc0, 0x9d, 0xf6, 0xdc, 0xc3, 0x94, 0x25, 0xcf, 0xba, 0x71, 0x22, 0x94, 0x20, - 0xd7, 0xce, 0x28, 0x5d, 0x43, 0xe9, 0x4e, 0x7b, 0x76, 0x3d, 0x14, 0xa1, 0x40, 0x86, 0x9b, 0xfd, - 0xd3, 0x64, 0xfb, 0x7a, 0x28, 0x44, 0x38, 0x66, 0x2e, 0x8d, 0xb9, 0x4b, 0xa3, 0x48, 0x28, 0xaa, - 0xb8, 0x88, 0xa4, 0xd9, 0x6d, 0xfb, 0x42, 0x4e, 0x84, 0x74, 0x07, 0x54, 0x32, 0x9d, 0xc3, 0x9d, - 0xf6, 0x06, 0x4c, 0xd1, 0x9e, 0x1b, 0xd3, 0x90, 0x47, 0x48, 0x36, 0xdc, 0x25, 0xce, 0xd4, 0xb3, - 0x98, 0x99, 0x70, 0x4e, 0x1d, 0xc8, 0x37, 0x59, 0x90, 0x87, 0x34, 0xa1, 0x13, 0xe9, 0xb1, 0xc3, - 0x94, 0x49, 0xe5, 0x78, 0x70, 0x75, 0x01, 0x95, 0xb1, 0x88, 0x24, 0x23, 0x9f, 0x40, 0x35, 0x46, - 0xa4, 0x61, 0xdd, 0xb4, 0x5a, 0x1b, 0x7b, 0xdb, 0xdd, 0xc2, 0xba, 0xba, 0x5a, 0xb6, 0x5f, 0x7e, - 0xf1, 0xd7, 0x8d, 0x92, 0x67, 0x24, 0xce, 0xc7, 0xb0, 0x8d, 0x31, 0x3f, 0x57, 0x8a, 0x49, 0x5d, - 0xd3, 0x3e, 0x57, 0x13, 0x1a, 0x9b, 0xa4, 0x64, 0x0b, 0xaa, 0x23, 0xc6, 0xc3, 0x91, 0xc2, 0xe8, - 0xeb, 0x9e, 0x59, 0x39, 0x03, 0x68, 0x2e, 0x13, 0x1a, 0x5f, 0x9f, 0x41, 0x75, 0x80, 0x88, 0xf1, - 0xd5, 0x5a, 0xe2, 0xeb, 0x62, 0x04, 0xa3, 0x73, 0x3a, 0x70, 0x0d, 0x73, 0x7c, 0x19, 0x0b, 0x7f, - 0xf4, 0x20, 0x1a, 0x8a, 0x99, 0xa9, 0x3a, 0x54, 0x58, 0x86, 0x61, 0xe4, 0xb2, 0xa7, 0x17, 0xce, - 0x8f, 0x6b, 0xb0, 0x75, 0x9e, 0x6f, 0xbc, 0x14, 0x0a, 0xc8, 0x0e, 0x5c, 0x92, 0x8a, 0x26, 0xaa, - 0x6f, 0x2a, 0x5c, 0xc3, 0x0a, 0x37, 0x10, 0xfb, 0x1a, 0x21, 0xb2, 0x0d, 0xc0, 0xa2, 0x60, 0x46, - 0x58, 0x47, 0x42, 0x8d, 0x45, 0x81, 0xd9, 0xee, 0x41, 0x3d, 0xa6, 0x89, 0xe2, 0x3e, 0x8f, 0xb1, - 0x80, 0xbe, 0xa9, 0xb8, 0x7c, 0xd3, 0x6a, 0x5d, 0xf2, 0xae, 0x2e, 0xec, 0xe9, 0xe2, 0xc8, 0x7b, - 0xb0, 0x49, 0x7d, 0xc5, 0xa7, 0xac, 0x3f, 0xa5, 0x63, 0x1e, 0x50, 0x25, 0x12, 0xd9, 0xa8, 0xa0, - 0xad, 0xb7, 0xf4, 0xc6, 0x77, 0x39, 0x4e, 0xee, 0x41, 0x63, 0x2e, 0x46, 0x14, 0xce, 0x6b, 0xaa, - 0xa8, 0x79, 0x7b, 0x61, 0xff, 0x4c, 0xea, 0xdc, 0x05, 0x1b, 0x0f, 0x23, 0x87, 0x1e, 0x44, 0x01, - 0x7b, 0x3a, 0x3b, 0xc1, 0x06, 0xbc, 0x41, 0x83, 0x20, 0x61, 0x52, 0x77, 0x4d, 0xcd, 0x9b, 0x2d, - 0x9d, 0xc7, 0xf0, 0x4e, 0xa1, 0x2e, 0xef, 0xb6, 0x0a, 0xcf, 0x00, 0x73, 0xa9, 0xef, 0x2e, 0xb9, - 0xd4, 0x73, 0x6a, 0xad, 0x71, 0x3e, 0x05, 0x07, 0x63, 0x3f, 0x12, 0x43, 0xf5, 0x85, 0x88, 0x86, - 0x3c, 0x99, 0xe0, 0xd1, 0x3c, 0x52, 0x54, 0xa5, 0xf2, 0xbf, 0x5a, 0xee, 0x57, 0x0b, 0x6e, 0xad, - 0x94, 0x1b, 0x8b, 0x6d, 0xd8, 0xe4, 0xb2, 0x2f, 0xc5, 0x50, 0xf5, 0x7d, 0xcd, 0x62, 0x01, 0x86, - 0x7a, 0xd3, 0xbb, 0xc2, 0xe5, 0x9c, 0x98, 0x05, 0xe4, 0x06, 0x6c, 0x4c, 0x85, 0x62, 0x41, 0x3f, - 0x16, 0x4f, 0x58, 0x82, 0x1d, 0x50, 0xf6, 0x00, 0xa1, 0x87, 0x19, 0x92, 0x11, 0x94, 0x50, 0x74, - 0x6c, 0x08, 0xeb, 0x9a, 0x80, 0x90, 0x26, 0xdc, 0x86, 0x2b, 0x87, 0xa9, 0x48, 0xd2, 0x49, 0x7f, - 0x98, 0x64, 0xf7, 0x27, 0x22, 0xbc, 0xfd, 0x9a, 0x77, 0x59, 0xc3, 0x5f, 0x19, 0x74, 0xef, 0xf7, - 0x2a, 0x54, 0xd0, 0x3e, 0xf9, 0xc1, 0x82, 0xaa, 0x9e, 0x46, 0xb2, 0xbb, 0xe4, 0xfc, 0x2e, 0x8e, - 0xbf, 0xdd, 0x7e, 0x15, 0xaa, 0x3e, 0x02, 0xc7, 0xf9, 0xfe, 0x8f, 0x7f, 0x9e, 0xaf, 0x5d, 0x27, - 0xb6, 0x6b, 0x34, 0xf3, 0x2f, 0x8d, 0x1e, 0x7d, 0xf2, 0x8b, 0x05, 0x9b, 0x17, 0x66, 0x8f, 0x7c, - 0xb8, 0x2a, 0xcb, 0xb2, 0x57, 0xc2, 0xfe, 0xe8, 0x35, 0x55, 0xc6, 0xe6, 0x1d, 0xb4, 0xd9, 0x26, - 0xad, 0x22, 0x9b, 0xf4, 0x4c, 0xe6, 0x1e, 0xe9, 0x16, 0x38, 0x26, 0xcf, 0x2d, 0xa8, 0xe5, 0xe3, - 0x4d, 0xde, 0x5f, 0x95, 0xf6, 0xfc, 0xab, 0x61, 0x77, 0x5e, 0x91, 0x6d, 0xcc, 0xed, 0xa2, 0xb9, - 0x5b, 0x64, 0xa7, 0xc8, 0x1c, 0x3e, 0x20, 0xee, 0x11, 0xfe, 0x1c, 0x93, 0x9f, 0x2d, 0xb8, 0xbc, - 0xd8, 0xf1, 0xa4, 0xb7, 0x2a, 0x59, 0xe1, 0x4c, 0xda, 0x7b, 0xaf, 0x23, 0x31, 0x26, 0x5d, 0x34, - 0xb9, 0x4b, 0x6e, 0x17, 0x99, 0xcc, 0x1f, 0x0b, 0xf7, 0xc8, 0x4c, 0xf7, 0x31, 0xf9, 0xcd, 0x82, - 0xad, 0xe2, 0xf9, 0x21, 0xf7, 0x56, 0xe5, 0x5f, 0x39, 0xb2, 0xf6, 0xfd, 0xff, 0x23, 0x35, 0x25, - 0xdc, 0xc5, 0x12, 0xee, 0x90, 0x6e, 0x51, 0x09, 0xd9, 0x14, 0x77, 0xfc, 0x39, 0x71, 0xde, 0x0a, - 0xfb, 0xdf, 0xbe, 0x38, 0x69, 0x5a, 0x2f, 0x4f, 0x9a, 0xd6, 0xdf, 0x27, 0x4d, 0xeb, 0xa7, 0xd3, - 0x66, 0xe9, 0xe5, 0x69, 0xb3, 0xf4, 0xe7, 0x69, 0xb3, 0xf4, 0xf8, 0x7e, 0xc8, 0xd5, 0x28, 0x1d, - 0x74, 0x7d, 0x31, 0xc9, 0x63, 0x86, 0xa2, 0xc3, 0x9e, 0x32, 0x3f, 0xcd, 0x62, 0x74, 0xe8, 0xc0, - 0xe7, 0xee, 0x44, 0x04, 0xe9, 0x98, 0xc9, 0x3c, 0x1b, 0x7e, 0x80, 0x07, 0x55, 0xfc, 0x02, 0x7f, - 0xf0, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe2, 0x10, 0xc6, 0x5b, 0x40, 0x08, 0x00, 0x00, + // 922 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0xcf, 0x6f, 0x1b, 0x45, + 0x14, 0xce, 0xe6, 0x87, 0x89, 0x5f, 0xa2, 0x96, 0x4c, 0xd3, 0x10, 0x2d, 0x89, 0xd3, 0x6c, 0x85, + 0x9a, 0x04, 0xec, 0xad, 0x43, 0x1b, 0xd4, 0xc0, 0x01, 0x82, 0x40, 0xf4, 0x56, 0x36, 0xd0, 0x43, + 0x2f, 0xd6, 0x78, 0x77, 0xbc, 0x19, 0xc5, 0xde, 0xd9, 0xec, 0xcc, 0xba, 0xad, 0xa2, 0x5c, 0xe0, + 0xc8, 0x01, 0xa4, 0xfe, 0x13, 0x9c, 0x39, 0x23, 0x71, 0xa4, 0xc7, 0x4a, 0x5c, 0x38, 0x21, 0x94, + 0xf0, 0x87, 0xa0, 0x7d, 0x33, 0x5e, 0x3b, 0xf1, 0xda, 0x49, 0x7a, 0xb2, 0xe7, 0x9b, 0xef, 0x7b, + 0xef, 0x7b, 0xf3, 0x66, 0x9e, 0x16, 0xd6, 0x13, 0xd1, 0x6e, 0x1f, 0x72, 0x25, 0x83, 0x43, 0x37, + 0x62, 0xea, 0xb9, 0x48, 0x0e, 0xdd, 0x6e, 0xdd, 0x3d, 0x4a, 0x59, 0xf2, 0xb2, 0x16, 0x27, 0x42, + 0x09, 0x72, 0xbb, 0x4f, 0xa9, 0x19, 0x4a, 0xad, 0x5b, 0xb7, 0xb7, 0x7c, 0x21, 0x3b, 0x42, 0xba, + 0x4d, 0x2a, 0x99, 0xe6, 0xbb, 0xdd, 0x7a, 0x93, 0x29, 0x5a, 0x77, 0x63, 0x1a, 0xf2, 0x88, 0x2a, + 0x2e, 0x22, 0x1d, 0xc2, 0x5e, 0x0c, 0x45, 0x28, 0xf0, 0xaf, 0x9b, 0xfd, 0x33, 0xe8, 0x4a, 0x28, + 0x44, 0xd8, 0x66, 0x2e, 0x8d, 0xb9, 0x4b, 0xa3, 0x48, 0x28, 0x94, 0x48, 0xb3, 0x3b, 0xc2, 0x99, + 0x7a, 0x19, 0x33, 0x43, 0x71, 0x16, 0x81, 0x7c, 0x9b, 0x25, 0x7e, 0x42, 0x13, 0xda, 0x91, 0x1e, + 0x3b, 0x4a, 0x99, 0x54, 0x8e, 0x07, 0xb7, 0xce, 0xa1, 0x32, 0x16, 0x91, 0x64, 0xe4, 0x53, 0x28, + 0xc5, 0x88, 0x2c, 0x5b, 0x77, 0xac, 0x8d, 0xb9, 0xed, 0xd5, 0x5a, 0x61, 0x5d, 0x35, 0x2d, 0xdb, + 0x9b, 0x7e, 0xfd, 0xcf, 0xda, 0x84, 0x67, 0x24, 0xce, 0x27, 0xb0, 0x8a, 0x31, 0xbf, 0x50, 0x8a, + 0x49, 0xed, 0x73, 0x8f, 0xab, 0x0e, 0x8d, 0x4d, 0x52, 0xb2, 0x04, 0xa5, 0x03, 0xc6, 0xc3, 0x03, + 0x85, 0xd1, 0xa7, 0x3c, 0xb3, 0x72, 0x9a, 0x50, 0x19, 0x25, 0x34, 0xbe, 0x3e, 0x87, 0x52, 0x13, + 0x11, 0xe3, 0x6b, 0x63, 0x84, 0xaf, 0xe1, 0x08, 0x46, 0xe7, 0x54, 0xe1, 0x36, 0xe6, 0xf8, 0x2a, + 0x16, 0xfe, 0xc1, 0xe3, 0xa8, 0x25, 0x7a, 0xa6, 0x16, 0x61, 0x86, 0x65, 0x18, 0x46, 0x9e, 0xf6, + 0xf4, 0xc2, 0xf9, 0x69, 0x12, 0x96, 0x2e, 0xf2, 0x8d, 0x97, 0x42, 0x01, 0x59, 0x87, 0x79, 0xa9, + 0x68, 0xa2, 0x1a, 0xa6, 0xc2, 0x49, 0xac, 0x70, 0x0e, 0xb1, 0x6f, 0x10, 0x22, 0xab, 0x00, 0x2c, + 0x0a, 0x7a, 0x84, 0x29, 0x24, 0x94, 0x59, 0x14, 0x98, 0xed, 0x3a, 0x2c, 0xc6, 0x34, 0x51, 0xdc, + 0xe7, 0x31, 0x16, 0xd0, 0x30, 0x15, 0x4f, 0xdf, 0xb1, 0x36, 0xe6, 0xbd, 0x5b, 0xe7, 0xf6, 0x74, + 0x71, 0xe4, 0x43, 0x58, 0xa0, 0xbe, 0xe2, 0x5d, 0xd6, 0xe8, 0xd2, 0x36, 0x0f, 0xa8, 0x12, 0x89, + 0x5c, 0x9e, 0x41, 0x5b, 0xef, 0xea, 0x8d, 0xa7, 0x39, 0x4e, 0x1e, 0xc1, 0xf2, 0x40, 0x8c, 0x28, + 0x1c, 0xd4, 0x94, 0x50, 0xf3, 0xde, 0xb9, 0xfd, 0xbe, 0xd4, 0xd9, 0x01, 0x1b, 0x0f, 0x23, 0x87, + 0x1e, 0x47, 0x01, 0x7b, 0xd1, 0x3b, 0xc1, 0x65, 0x78, 0x87, 0x06, 0x41, 0xc2, 0xa4, 0xbe, 0x35, + 0x65, 0xaf, 0xb7, 0x74, 0x9e, 0xc1, 0xfb, 0x85, 0xba, 0xfc, 0xb6, 0xcd, 0xf0, 0x0c, 0x30, 0x4d, + 0xfd, 0x60, 0x44, 0x53, 0x2f, 0xa8, 0xb5, 0xc6, 0xf9, 0x0c, 0x1c, 0x8c, 0xbd, 0x2f, 0x5a, 0xea, + 0x4b, 0x11, 0xb5, 0x78, 0xd2, 0xc1, 0xa3, 0xd9, 0x57, 0x54, 0xa5, 0xf2, 0xb2, 0x2b, 0xf7, 0xbb, + 0x05, 0x77, 0xc7, 0xca, 0x8d, 0xc5, 0x2d, 0x58, 0xe0, 0xb2, 0x21, 0x45, 0x4b, 0x35, 0x7c, 0xcd, + 0x62, 0x01, 0x86, 0x9a, 0xf5, 0x6e, 0x72, 0x39, 0x20, 0x66, 0x01, 0x59, 0x83, 0xb9, 0xae, 0x50, + 0x2c, 0x68, 0xc4, 0xe2, 0x39, 0x4b, 0xf0, 0x06, 0x4c, 0x7b, 0x80, 0xd0, 0x93, 0x0c, 0xc9, 0x08, + 0x4a, 0x28, 0xda, 0x36, 0x84, 0x29, 0x4d, 0x40, 0x48, 0x13, 0xee, 0xc1, 0xcd, 0xa3, 0x54, 0x24, + 0x69, 0xa7, 0xd1, 0x4a, 0xb2, 0xfe, 0x89, 0x08, 0xbb, 0x5f, 0xf6, 0x6e, 0x68, 0xf8, 0x6b, 0x83, + 0x3a, 0x4f, 0xcd, 0x8b, 0xc9, 0x8f, 0x66, 0x9f, 0x87, 0x11, 0x55, 0x69, 0xc2, 0x2e, 0x29, 0x9c, + 0xac, 0x40, 0x39, 0xef, 0x3b, 0x5a, 0x2c, 0x7b, 0x7d, 0xc0, 0xf9, 0x1e, 0xd6, 0x46, 0xc6, 0x35, + 0x27, 0xb2, 0x02, 0x65, 0xd9, 0x03, 0x31, 0xf6, 0xbc, 0xd7, 0x07, 0xb2, 0xc7, 0xd1, 0x12, 0x69, + 0x14, 0x60, 0xe8, 0x59, 0x4f, 0x2f, 0xb6, 0x7f, 0x9e, 0x85, 0x19, 0x8c, 0x4b, 0x7e, 0xb4, 0xa0, + 0xa4, 0x87, 0x07, 0xd9, 0x1c, 0xd1, 0xee, 0xe1, 0x69, 0x65, 0x6f, 0x5d, 0x85, 0xaa, 0xfd, 0x39, + 0xce, 0x0f, 0x7f, 0xfd, 0xf7, 0x6a, 0x72, 0x85, 0xd8, 0xae, 0xd1, 0x0c, 0x0e, 0x46, 0x3d, 0xa9, + 0xc8, 0x6f, 0x16, 0x2c, 0x0c, 0x8d, 0x0a, 0xf2, 0x60, 0x5c, 0x96, 0x51, 0x43, 0xcd, 0x7e, 0x78, + 0x4d, 0x95, 0xb1, 0x79, 0x1f, 0x6d, 0x6e, 0x91, 0x8d, 0x22, 0x9b, 0xb4, 0x2f, 0x73, 0x8f, 0x75, + 0xe3, 0x4e, 0xc8, 0x2b, 0x0b, 0xca, 0xf9, 0x34, 0x22, 0x1f, 0x8d, 0x4b, 0x7b, 0x71, 0xc8, 0xd9, + 0xd5, 0x2b, 0xb2, 0x8d, 0xb9, 0x4d, 0x34, 0x77, 0x97, 0xac, 0x17, 0x99, 0xc3, 0x79, 0xe7, 0x1e, + 0xe3, 0xcf, 0x09, 0xf9, 0xd5, 0x82, 0x1b, 0xe7, 0x1f, 0x28, 0xa9, 0x8f, 0x4b, 0x56, 0x38, 0x42, + 0xec, 0xed, 0xeb, 0x48, 0x8c, 0x49, 0x17, 0x4d, 0x6e, 0x92, 0x7b, 0x45, 0x26, 0xf3, 0x2b, 0xed, + 0x1e, 0x9b, 0x61, 0x74, 0x42, 0xfe, 0xb4, 0x60, 0xa9, 0xf8, 0xb9, 0x93, 0x47, 0xe3, 0xf2, 0x8f, + 0x9d, 0x30, 0xf6, 0xee, 0xdb, 0x48, 0x4d, 0x09, 0x3b, 0x58, 0xc2, 0x7d, 0x52, 0x2b, 0x2a, 0x21, + 0x1b, 0x3a, 0x55, 0x7f, 0x40, 0xdc, 0xbf, 0x0a, 0x7f, 0x58, 0x40, 0x86, 0x9f, 0x28, 0x79, 0x78, + 0xa5, 0x53, 0xbc, 0x38, 0x2a, 0xec, 0x9d, 0xeb, 0xca, 0x8c, 0xfb, 0x5d, 0x74, 0xff, 0x80, 0x6c, + 0x17, 0xba, 0xef, 0xd1, 0x73, 0xd7, 0xee, 0x71, 0xde, 0x94, 0x93, 0xbd, 0xef, 0x5e, 0x9f, 0x56, + 0xac, 0x37, 0xa7, 0x15, 0xeb, 0xdf, 0xd3, 0x8a, 0xf5, 0xcb, 0x59, 0x65, 0xe2, 0xcd, 0x59, 0x65, + 0xe2, 0xef, 0xb3, 0xca, 0xc4, 0xb3, 0xdd, 0x90, 0xab, 0x83, 0xb4, 0x59, 0xf3, 0x45, 0x27, 0x8f, + 0x1b, 0x8a, 0x2a, 0x7b, 0xc1, 0xfc, 0x34, 0x3b, 0x85, 0x2a, 0x6d, 0xfa, 0xdc, 0xed, 0x88, 0x20, + 0x6d, 0x33, 0x99, 0x67, 0xc4, 0x2f, 0x9e, 0x66, 0x09, 0x3f, 0x79, 0x3e, 0xfe, 0x3f, 0x00, 0x00, + 0xff, 0xff, 0xf4, 0x71, 0xc5, 0x63, 0xb1, 0x09, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -630,6 +744,8 @@ type QueryClient interface { ValidatorIndex(ctx context.Context, in *QueryValidatorIndexRequest, opts ...grpc.CallOption) (*QueryValidatorIndexResponse, error) // SoftConfirmationStatus queries if a height is soft-confirmed SoftConfirmationStatus(ctx context.Context, in *QuerySoftConfirmationStatusRequest, opts ...grpc.CallOption) (*QuerySoftConfirmationStatusResponse, error) + // ValidatorSignature queries the signature of a validator for a specific height + ValidatorSignature(ctx context.Context, in *QueryValidatorSignatureRequest, opts ...grpc.CallOption) (*QueryValidatorSignatureResponse, error) } type queryClient struct { @@ -685,6 +801,15 @@ func (c *queryClient) SoftConfirmationStatus(ctx context.Context, in *QuerySoftC return out, nil } +func (c *queryClient) ValidatorSignature(ctx context.Context, in *QueryValidatorSignatureRequest, opts ...grpc.CallOption) (*QueryValidatorSignatureResponse, error) { + out := new(QueryValidatorSignatureResponse) + err := c.cc.Invoke(ctx, "/rollkitsdk.network.v1.Query/ValidatorSignature", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // QueryServer is the server API for Query service. type QueryServer interface { // Params queries the module parameters @@ -697,6 +822,8 @@ type QueryServer interface { ValidatorIndex(context.Context, *QueryValidatorIndexRequest) (*QueryValidatorIndexResponse, error) // SoftConfirmationStatus queries if a height is soft-confirmed SoftConfirmationStatus(context.Context, *QuerySoftConfirmationStatusRequest) (*QuerySoftConfirmationStatusResponse, error) + // ValidatorSignature queries the signature of a validator for a specific height + ValidatorSignature(context.Context, *QueryValidatorSignatureRequest) (*QueryValidatorSignatureResponse, error) } // UnimplementedQueryServer can be embedded to have forward compatible implementations. @@ -718,6 +845,9 @@ func (*UnimplementedQueryServer) ValidatorIndex(ctx context.Context, req *QueryV func (*UnimplementedQueryServer) SoftConfirmationStatus(ctx context.Context, req *QuerySoftConfirmationStatusRequest) (*QuerySoftConfirmationStatusResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SoftConfirmationStatus not implemented") } +func (*UnimplementedQueryServer) ValidatorSignature(ctx context.Context, req *QueryValidatorSignatureRequest) (*QueryValidatorSignatureResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ValidatorSignature not implemented") +} func RegisterQueryServer(s grpc1.Server, srv QueryServer) { s.RegisterService(&_Query_serviceDesc, srv) @@ -813,6 +943,24 @@ func _Query_SoftConfirmationStatus_Handler(srv interface{}, ctx context.Context, return interceptor(ctx, in, info, handler) } +func _Query_ValidatorSignature_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryValidatorSignatureRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).ValidatorSignature(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/rollkitsdk.network.v1.Query/ValidatorSignature", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).ValidatorSignature(ctx, req.(*QueryValidatorSignatureRequest)) + } + return interceptor(ctx, in, info, handler) +} + var Query_serviceDesc = _Query_serviceDesc var _Query_serviceDesc = grpc.ServiceDesc{ ServiceName: "rollkitsdk.network.v1.Query", @@ -838,6 +986,10 @@ var _Query_serviceDesc = grpc.ServiceDesc{ MethodName: "SoftConfirmationStatus", Handler: _Query_SoftConfirmationStatus_Handler, }, + { + MethodName: "ValidatorSignature", + Handler: _Query_ValidatorSignature_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "rollkitsdk/network/v1/query.proto", @@ -1188,6 +1340,81 @@ func (m *QuerySoftConfirmationStatusResponse) MarshalToSizedBuffer(dAtA []byte) return len(dAtA) - i, nil } +func (m *QueryValidatorSignatureRequest) 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 *QueryValidatorSignatureRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryValidatorSignatureRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Validator) > 0 { + i -= len(m.Validator) + copy(dAtA[i:], m.Validator) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Validator))) + i-- + dAtA[i] = 0x12 + } + if m.Height != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryValidatorSignatureResponse) 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 *QueryValidatorSignatureResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryValidatorSignatureResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Found { + i-- + if m.Found { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x10 + } + if len(m.Signature) > 0 { + i -= len(m.Signature) + copy(dAtA[i:], m.Signature) + i = encodeVarintQuery(dAtA, i, uint64(len(m.Signature))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { offset -= sovQuery(v) base := offset @@ -1344,6 +1571,38 @@ func (m *QuerySoftConfirmationStatusResponse) Size() (n int) { return n } +func (m *QueryValidatorSignatureRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Height != 0 { + n += 1 + sovQuery(uint64(m.Height)) + } + l = len(m.Validator) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + +func (m *QueryValidatorSignatureResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Signature) + if l > 0 { + n += 1 + l + sovQuery(uint64(l)) + } + if m.Found { + n += 2 + } + return n +} + func sovQuery(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -2263,6 +2522,211 @@ func (m *QuerySoftConfirmationStatusResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *QueryValidatorSignatureRequest) 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 ErrIntOverflowQuery + } + 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: QueryValidatorSignatureRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryValidatorSignatureRequest: 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 ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Validator", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Validator = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryValidatorSignatureResponse) 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 ErrIntOverflowQuery + } + 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: QueryValidatorSignatureResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryValidatorSignatureResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Signature", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Signature = append(m.Signature[:0], dAtA[iNdEx:postIndex]...) + if m.Signature == nil { + m.Signature = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Found", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.Found = bool(v != 0) + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipQuery(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/modules/network/types/query.pb.gw.go b/modules/network/types/query.pb.gw.go index ca6c1a74..8d72787f 100644 --- a/modules/network/types/query.pb.gw.go +++ b/modules/network/types/query.pb.gw.go @@ -267,6 +267,82 @@ func local_request_Query_SoftConfirmationStatus_0(ctx context.Context, marshaler } +func request_Query_ValidatorSignature_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryValidatorSignatureRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["height"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "height") + } + + protoReq.Height, err = runtime.Int64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "height", err) + } + + val, ok = pathParams["validator"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "validator") + } + + protoReq.Validator, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "validator", err) + } + + msg, err := client.ValidatorSignature(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_ValidatorSignature_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryValidatorSignatureRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["height"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "height") + } + + protoReq.Height, err = runtime.Int64(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "height", err) + } + + val, ok = pathParams["validator"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "validator") + } + + protoReq.Validator, err = runtime.String(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "validator", err) + } + + msg, err := server.ValidatorSignature(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -388,6 +464,29 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) + mux.Handle("GET", pattern_Query_ValidatorSignature_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_ValidatorSignature_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_ValidatorSignature_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -529,6 +628,26 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) + mux.Handle("GET", pattern_Query_ValidatorSignature_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_ValidatorSignature_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_ValidatorSignature_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -542,6 +661,8 @@ var ( pattern_Query_ValidatorIndex_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"rollkit", "network", "v1", "validator", "address"}, "", runtime.AssumeColonVerbOpt(false))) pattern_Query_SoftConfirmationStatus_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"rollkit", "network", "v1", "soft-confirmation", "height"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_ValidatorSignature_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 1, 0, 4, 1, 5, 5}, []string{"rollkit", "network", "v1", "signature", "height", "validator"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( @@ -554,4 +675,6 @@ var ( forward_Query_ValidatorIndex_0 = runtime.ForwardResponseMessage forward_Query_SoftConfirmationStatus_0 = runtime.ForwardResponseMessage + + forward_Query_ValidatorSignature_0 = runtime.ForwardResponseMessage ) diff --git a/modules/proto/rollkitsdk/network/v1/query.proto b/modules/proto/rollkitsdk/network/v1/query.proto index aeaf3321..46770667 100644 --- a/modules/proto/rollkitsdk/network/v1/query.proto +++ b/modules/proto/rollkitsdk/network/v1/query.proto @@ -2,39 +2,44 @@ syntax = "proto3"; package rollkitsdk.network.v1; -option go_package = "github.com/rollkit/go-execution-abci/modules/network/types"; - +import "cosmos/base/query/v1beta1/pagination.proto"; import "gogoproto/gogo.proto"; import "google/api/annotations.proto"; -import "cosmos/base/query/v1beta1/pagination.proto"; import "rollkitsdk/network/v1/types.proto"; +option go_package = "github.com/rollkit/go-execution-abci/modules/network/types"; + // Query defines the gRPC querier service for the network module. service Query { // Params queries the module parameters rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { option (google.api.http).get = "/rollkit/network/v1/params"; } - + // AttestationBitmap queries the attestation bitmap for a specific height rpc AttestationBitmap(QueryAttestationBitmapRequest) returns (QueryAttestationBitmapResponse) { option (google.api.http).get = "/rollkit/network/v1/attestation/{height}"; } - + // EpochInfo queries information about a specific epoch rpc EpochInfo(QueryEpochInfoRequest) returns (QueryEpochInfoResponse) { option (google.api.http).get = "/rollkit/network/v1/epoch/{epoch}"; } - + // ValidatorIndex queries the bitmap index for a validator rpc ValidatorIndex(QueryValidatorIndexRequest) returns (QueryValidatorIndexResponse) { option (google.api.http).get = "/rollkit/network/v1/validator/{address}"; } - + // SoftConfirmationStatus queries if a height is soft-confirmed rpc SoftConfirmationStatus(QuerySoftConfirmationStatusRequest) returns (QuerySoftConfirmationStatusResponse) { option (google.api.http).get = "/rollkit/network/v1/soft-confirmation/{height}"; } + + // ValidatorSignature queries the signature of a validator for a specific height + rpc ValidatorSignature(QueryValidatorSignatureRequest) returns (QueryValidatorSignatureResponse) { + option (google.api.http).get = "/rollkit/network/v1/signature/{height}/{validator}"; + } } // QueryParamsRequest is the request type for the Query/Params RPC method. @@ -92,4 +97,16 @@ message QuerySoftConfirmationStatusResponse { uint64 voted_power = 2; uint64 total_power = 3; string quorum_fraction = 4; -} \ No newline at end of file +} + +// QueryValidatorSignatureRequest is the request type for the Query/ValidatorSignature RPC method. +message QueryValidatorSignatureRequest { + int64 height = 1; + string validator = 2; +} + +// QueryValidatorSignatureResponse is the response type for the Query/ValidatorSignature RPC method. +message QueryValidatorSignatureResponse { + bytes signature = 1; + bool found = 2; +} diff --git a/pkg/rpc/core/blocks.go b/pkg/rpc/core/blocks.go index 69b7e925..e400b243 100644 --- a/pkg/rpc/core/blocks.go +++ b/pkg/rpc/core/blocks.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "sort" + "time" cmbytes "github.com/cometbft/cometbft/libs/bytes" cmquery "github.com/cometbft/cometbft/libs/pubsub/query" @@ -233,7 +234,7 @@ func Commit(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultCommit, erro } // Build commit with attestations from soft confirmation data - commit, err := buildCommitFromAttestations(ctx.Context(), header, softConfirmationData) + commit, err := buildCommitFromAttestations(ctx.Context(), height, softConfirmationData) if err != nil { return nil, fmt.Errorf("failed to build commit from attestations: %w", err) } @@ -436,104 +437,116 @@ func checkSoftConfirmation(ctx context.Context, height uint64) (bool, *networkty return true, attestationResp, nil } -// buildCommitFromAttestations constructs a CometBFT commit using the attestations from the network module -func buildCommitFromAttestations(ctx context.Context, header *rlktypes.SignedHeader, attestationData *networktypes.QueryAttestationBitmapResponse) (*cmttypes.Commit, error) { - if attestationData == nil || attestationData.Bitmap == nil { - return nil, fmt.Errorf("no attestation data available") +// buildCommitFromAttestations constructs a commit with real signatures from attestations +func buildCommitFromAttestations(ctx context.Context, height uint64, attestationData *networktypes.QueryAttestationBitmapResponse) (*cmttypes.Commit, error) { + // Get the attestation bitmap + bitmap := attestationData.Bitmap.Bitmap + if bitmap == nil { + return nil, fmt.Errorf("no attestation bitmap found for height %d", height) + } + + // Query all validators to get their addresses + queryReq := &abci.RequestQuery{ + Path: "/cosmos.staking.v1beta1.Query/Validators", + Data: []byte{}, // Empty request to get all validators } - // Get validator set to build signatures - validators, err := getValidatorSet(ctx, header.Height()) + valQueryRes, err := env.Adapter.App.Query(ctx, queryReq) if err != nil { - return nil, fmt.Errorf("failed to get validator set: %w", err) + return nil, fmt.Errorf("failed to query validators: %w", err) + } + if valQueryRes.Code != 0 { + return nil, fmt.Errorf("failed to query validators: %s", valQueryRes.Log) } - bitmap := attestationData.Bitmap.Bitmap - signatures := make([]cmttypes.CommitSig, len(validators)) - - // Build signatures from attestations - for i, validator := range validators { - // Check if validator attested (bit is set in bitmap) - byteIndex := i / 8 - bitIndex := i % 8 - - if byteIndex >= len(bitmap) { - // Validator didn't attest - create absent signature - signatures[i] = cmttypes.CommitSig{ - BlockIDFlag: cmttypes.BlockIDFlagAbsent, - ValidatorAddress: validator.Address, - Timestamp: header.Time(), - Signature: nil, - } - continue - } + // For now, we'll construct a basic commit structure with the available data + // In a real implementation, you'd need to decode the validator response properly - if (bitmap[byteIndex] & (1 << bitIndex)) != 0 { - // Validator attested - get their signature - signature, err := getValidatorSignature(ctx, int64(header.Height()), validator.Address.String()) - if err != nil { - env.Logger.Error("failed to get validator signature", "height", header.Height(), "validator", validator.Address, "error", err) - // Create absent signature if we can't get the actual one - signatures[i] = cmttypes.CommitSig{ - BlockIDFlag: cmttypes.BlockIDFlagAbsent, - ValidatorAddress: validator.Address, - Timestamp: header.Time(), - Signature: nil, - } - continue - } + votes := make([]cmttypes.CommitSig, 0) - signatures[i] = cmttypes.CommitSig{ + // We need to iterate through the bitmap and for each set bit, get the validator signature + for i := 0; i < len(bitmap)*8; i++ { + // Check if validator at index i voted (bit is set) + if (bitmap[i/8] & (1 << (i % 8))) != 0 { + // This validator voted, let's try to get their signature + // For this we need the validator address corresponding to index i + // This would require a proper validator index mapping + + // For now, we'll create a placeholder vote with empty signature + // In a real implementation, you'd: + // 1. Get validator address from index i + // 2. Query the signature using ValidatorSignature query + // 3. Construct the proper CommitSig + + vote := cmttypes.CommitSig{ BlockIDFlag: cmttypes.BlockIDFlagCommit, - ValidatorAddress: validator.Address, - Timestamp: header.Time(), - Signature: signature, + ValidatorAddress: make([]byte, 20), // Placeholder validator address + Timestamp: time.Now(), // Should be actual timestamp + Signature: nil, // We'll get this from the query below } - } else { - // Validator didn't attest - signatures[i] = cmttypes.CommitSig{ - BlockIDFlag: cmttypes.BlockIDFlagAbsent, - ValidatorAddress: validator.Address, - Timestamp: header.Time(), - Signature: nil, + + // Try to get the real signature (this is a simplified approach) + // In practice, you'd need to map the bitmap index to validator address + validatorAddr := fmt.Sprintf("validator_%d", i) // Placeholder + signature, err := getValidatorSignatureFromQuery(ctx, int64(height), validatorAddr) + if err == nil { + vote.Signature = signature } + + votes = append(votes, vote) + } else { + // Validator didn't vote, add nil vote + votes = append(votes, cmttypes.CommitSig{ + BlockIDFlag: cmttypes.BlockIDFlagAbsent, + }) } } - return &cmttypes.Commit{ - Height: int64(header.Height()), - Round: 0, + commit := &cmttypes.Commit{ + Height: int64(height), + Round: 0, // Default round BlockID: cmttypes.BlockID{ - Hash: cmbytes.HexBytes(header.Hash()), - PartSetHeader: cmttypes.PartSetHeader{}, + Hash: make([]byte, 32), // Should be actual block hash + PartSetHeader: cmttypes.PartSetHeader{ + Total: 1, + Hash: make([]byte, 32), + }, }, - Signatures: signatures, - }, nil + Signatures: votes, + } + + return commit, nil } -// getValidatorSet retrieves the validator set for a given height -func getValidatorSet(ctx context.Context, height uint64) ([]*cmttypes.Validator, error) { - // For now, we'll use the genesis validator set as a simple implementation - // In a full implementation, this should get the actual validator set at the given height - genesisValidators := env.Adapter.AppGenesis.Consensus.Validators - validators := make([]*cmttypes.Validator, len(genesisValidators)) - - for i, gval := range genesisValidators { - validators[i] = &cmttypes.Validator{ - Address: gval.Address, - PubKey: gval.PubKey, - VotingPower: gval.Power, - } +// getValidatorSignatureFromQuery queries the signature for a specific validator +func getValidatorSignatureFromQuery(ctx context.Context, height int64, validatorAddr string) ([]byte, error) { + sigReq := &networktypes.QueryValidatorSignatureRequest{ + Height: height, + Validator: validatorAddr, } - return validators, nil -} + reqData, err := sigReq.Marshal() + if err != nil { + return nil, fmt.Errorf("failed to marshal signature request: %w", err) + } + + abciReq := &abci.RequestQuery{ + Path: "/rollkitsdk.network.v1.Query/ValidatorSignature", + Data: reqData, + } + + res, err := env.Adapter.App.Query(ctx, abciReq) + if err != nil { + return nil, fmt.Errorf("signature query failed: %w", err) + } + if res.Code != 0 { + return nil, fmt.Errorf("signature query failed: %s", res.Log) + } + + var sigResp networktypes.QueryValidatorSignatureResponse + if err := sigResp.Unmarshal(res.Value); err != nil { + return nil, fmt.Errorf("failed to unmarshal signature response: %w", err) + } -// getValidatorSignature retrieves the signature for a specific validator at a given height -func getValidatorSignature(ctx context.Context, height int64, validatorAddr string) ([]byte, error) { - // This would query the network module to get the stored signature for this validator - // Since we don't have direct access to the keeper, we'd need to implement a query for this - // For now, return an empty signature - this should be implemented with proper signature retrieval - env.Logger.Debug("getting validator signature", "height", height, "validator", validatorAddr) - return []byte{}, nil + return sigResp.Signature, nil } From e3e1ce049c4495c3dd6616dae8fbfcda56f84b65 Mon Sep 17 00:00:00 2001 From: Randy Grok Date: Fri, 4 Jul 2025 12:13:12 +0200 Subject: [PATCH 03/17] Rename field --- modules/network/keeper/grpc_query.go | 2 +- modules/network/types/query.pb.go | 137 ++++++++++++++------------- modules/network/types/query.pb.gw.go | 18 ++-- pkg/rpc/core/blocks.go | 4 +- 4 files changed, 81 insertions(+), 80 deletions(-) diff --git a/modules/network/keeper/grpc_query.go b/modules/network/keeper/grpc_query.go index 8b22072d..5135a8f0 100644 --- a/modules/network/keeper/grpc_query.go +++ b/modules/network/keeper/grpc_query.go @@ -199,7 +199,7 @@ func (q *queryServer) ValidatorSignature(c context.Context, req *types.QueryVali ctx := sdk.UnwrapSDKContext(c) - signature, err := q.keeper.GetSignature(ctx, req.Height, req.Validator) + signature, err := q.keeper.GetSignature(ctx, req.BlockHeight, req.Validator) if err != nil { if errors.Is(err, collections.ErrNotFound) { return &types.QueryValidatorSignatureResponse{ diff --git a/modules/network/types/query.pb.go b/modules/network/types/query.pb.go index d2519c01..7be1ccd9 100644 --- a/modules/network/types/query.pb.go +++ b/modules/network/types/query.pb.go @@ -539,8 +539,8 @@ func (m *QuerySoftConfirmationStatusResponse) GetQuorumFraction() string { // QueryValidatorSignatureRequest is the request type for the Query/ValidatorSignature RPC method. type QueryValidatorSignatureRequest struct { - Height int64 `protobuf:"varint,1,opt,name=height,proto3" json:"height,omitempty"` - Validator string `protobuf:"bytes,2,opt,name=validator,proto3" json:"validator,omitempty"` + BlockHeight int64 `protobuf:"varint,1,opt,name=block_height,json=blockHeight,proto3" json:"block_height,omitempty"` + Validator string `protobuf:"bytes,2,opt,name=validator,proto3" json:"validator,omitempty"` } func (m *QueryValidatorSignatureRequest) Reset() { *m = QueryValidatorSignatureRequest{} } @@ -576,9 +576,9 @@ func (m *QueryValidatorSignatureRequest) XXX_DiscardUnknown() { var xxx_messageInfo_QueryValidatorSignatureRequest proto.InternalMessageInfo -func (m *QueryValidatorSignatureRequest) GetHeight() int64 { +func (m *QueryValidatorSignatureRequest) GetBlockHeight() int64 { if m != nil { - return m.Height + return m.BlockHeight } return 0 } @@ -661,65 +661,66 @@ func init() { func init() { proto.RegisterFile("rollkitsdk/network/v1/query.proto", fileDescriptor_bae17cb7b1e48f90) } var fileDescriptor_bae17cb7b1e48f90 = []byte{ - // 922 bytes of a gzipped FileDescriptorProto + // 938 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x56, 0xcf, 0x6f, 0x1b, 0x45, 0x14, 0xce, 0xe6, 0x87, 0x89, 0x5f, 0xa2, 0x96, 0x4c, 0xd3, 0x10, 0x2d, 0x89, 0xd3, 0x6c, 0x85, - 0x9a, 0x04, 0xec, 0xad, 0x43, 0x1b, 0xd4, 0xc0, 0x01, 0x82, 0x40, 0xf4, 0x56, 0x36, 0xd0, 0x43, - 0x2f, 0xd6, 0x78, 0x77, 0xbc, 0x19, 0xc5, 0xde, 0xd9, 0xec, 0xcc, 0xba, 0xad, 0xa2, 0x5c, 0xe0, - 0xc8, 0x01, 0xa4, 0xfe, 0x13, 0x9c, 0x39, 0x23, 0x71, 0xa4, 0xc7, 0x4a, 0x5c, 0x38, 0x21, 0x94, - 0xf0, 0x87, 0xa0, 0x7d, 0x33, 0x5e, 0x3b, 0xf1, 0xda, 0x49, 0x7a, 0xb2, 0xe7, 0x9b, 0xef, 0x7b, - 0xef, 0x7b, 0xf3, 0x66, 0x9e, 0x16, 0xd6, 0x13, 0xd1, 0x6e, 0x1f, 0x72, 0x25, 0x83, 0x43, 0x37, - 0x62, 0xea, 0xb9, 0x48, 0x0e, 0xdd, 0x6e, 0xdd, 0x3d, 0x4a, 0x59, 0xf2, 0xb2, 0x16, 0x27, 0x42, - 0x09, 0x72, 0xbb, 0x4f, 0xa9, 0x19, 0x4a, 0xad, 0x5b, 0xb7, 0xb7, 0x7c, 0x21, 0x3b, 0x42, 0xba, - 0x4d, 0x2a, 0x99, 0xe6, 0xbb, 0xdd, 0x7a, 0x93, 0x29, 0x5a, 0x77, 0x63, 0x1a, 0xf2, 0x88, 0x2a, - 0x2e, 0x22, 0x1d, 0xc2, 0x5e, 0x0c, 0x45, 0x28, 0xf0, 0xaf, 0x9b, 0xfd, 0x33, 0xe8, 0x4a, 0x28, - 0x44, 0xd8, 0x66, 0x2e, 0x8d, 0xb9, 0x4b, 0xa3, 0x48, 0x28, 0x94, 0x48, 0xb3, 0x3b, 0xc2, 0x99, - 0x7a, 0x19, 0x33, 0x43, 0x71, 0x16, 0x81, 0x7c, 0x9b, 0x25, 0x7e, 0x42, 0x13, 0xda, 0x91, 0x1e, - 0x3b, 0x4a, 0x99, 0x54, 0x8e, 0x07, 0xb7, 0xce, 0xa1, 0x32, 0x16, 0x91, 0x64, 0xe4, 0x53, 0x28, - 0xc5, 0x88, 0x2c, 0x5b, 0x77, 0xac, 0x8d, 0xb9, 0xed, 0xd5, 0x5a, 0x61, 0x5d, 0x35, 0x2d, 0xdb, - 0x9b, 0x7e, 0xfd, 0xcf, 0xda, 0x84, 0x67, 0x24, 0xce, 0x27, 0xb0, 0x8a, 0x31, 0xbf, 0x50, 0x8a, - 0x49, 0xed, 0x73, 0x8f, 0xab, 0x0e, 0x8d, 0x4d, 0x52, 0xb2, 0x04, 0xa5, 0x03, 0xc6, 0xc3, 0x03, - 0x85, 0xd1, 0xa7, 0x3c, 0xb3, 0x72, 0x9a, 0x50, 0x19, 0x25, 0x34, 0xbe, 0x3e, 0x87, 0x52, 0x13, - 0x11, 0xe3, 0x6b, 0x63, 0x84, 0xaf, 0xe1, 0x08, 0x46, 0xe7, 0x54, 0xe1, 0x36, 0xe6, 0xf8, 0x2a, - 0x16, 0xfe, 0xc1, 0xe3, 0xa8, 0x25, 0x7a, 0xa6, 0x16, 0x61, 0x86, 0x65, 0x18, 0x46, 0x9e, 0xf6, - 0xf4, 0xc2, 0xf9, 0x69, 0x12, 0x96, 0x2e, 0xf2, 0x8d, 0x97, 0x42, 0x01, 0x59, 0x87, 0x79, 0xa9, - 0x68, 0xa2, 0x1a, 0xa6, 0xc2, 0x49, 0xac, 0x70, 0x0e, 0xb1, 0x6f, 0x10, 0x22, 0xab, 0x00, 0x2c, - 0x0a, 0x7a, 0x84, 0x29, 0x24, 0x94, 0x59, 0x14, 0x98, 0xed, 0x3a, 0x2c, 0xc6, 0x34, 0x51, 0xdc, - 0xe7, 0x31, 0x16, 0xd0, 0x30, 0x15, 0x4f, 0xdf, 0xb1, 0x36, 0xe6, 0xbd, 0x5b, 0xe7, 0xf6, 0x74, - 0x71, 0xe4, 0x43, 0x58, 0xa0, 0xbe, 0xe2, 0x5d, 0xd6, 0xe8, 0xd2, 0x36, 0x0f, 0xa8, 0x12, 0x89, - 0x5c, 0x9e, 0x41, 0x5b, 0xef, 0xea, 0x8d, 0xa7, 0x39, 0x4e, 0x1e, 0xc1, 0xf2, 0x40, 0x8c, 0x28, - 0x1c, 0xd4, 0x94, 0x50, 0xf3, 0xde, 0xb9, 0xfd, 0xbe, 0xd4, 0xd9, 0x01, 0x1b, 0x0f, 0x23, 0x87, - 0x1e, 0x47, 0x01, 0x7b, 0xd1, 0x3b, 0xc1, 0x65, 0x78, 0x87, 0x06, 0x41, 0xc2, 0xa4, 0xbe, 0x35, - 0x65, 0xaf, 0xb7, 0x74, 0x9e, 0xc1, 0xfb, 0x85, 0xba, 0xfc, 0xb6, 0xcd, 0xf0, 0x0c, 0x30, 0x4d, - 0xfd, 0x60, 0x44, 0x53, 0x2f, 0xa8, 0xb5, 0xc6, 0xf9, 0x0c, 0x1c, 0x8c, 0xbd, 0x2f, 0x5a, 0xea, - 0x4b, 0x11, 0xb5, 0x78, 0xd2, 0xc1, 0xa3, 0xd9, 0x57, 0x54, 0xa5, 0xf2, 0xb2, 0x2b, 0xf7, 0xbb, - 0x05, 0x77, 0xc7, 0xca, 0x8d, 0xc5, 0x2d, 0x58, 0xe0, 0xb2, 0x21, 0x45, 0x4b, 0x35, 0x7c, 0xcd, - 0x62, 0x01, 0x86, 0x9a, 0xf5, 0x6e, 0x72, 0x39, 0x20, 0x66, 0x01, 0x59, 0x83, 0xb9, 0xae, 0x50, - 0x2c, 0x68, 0xc4, 0xe2, 0x39, 0x4b, 0xf0, 0x06, 0x4c, 0x7b, 0x80, 0xd0, 0x93, 0x0c, 0xc9, 0x08, - 0x4a, 0x28, 0xda, 0x36, 0x84, 0x29, 0x4d, 0x40, 0x48, 0x13, 0xee, 0xc1, 0xcd, 0xa3, 0x54, 0x24, - 0x69, 0xa7, 0xd1, 0x4a, 0xb2, 0xfe, 0x89, 0x08, 0xbb, 0x5f, 0xf6, 0x6e, 0x68, 0xf8, 0x6b, 0x83, - 0x3a, 0x4f, 0xcd, 0x8b, 0xc9, 0x8f, 0x66, 0x9f, 0x87, 0x11, 0x55, 0x69, 0xc2, 0x2e, 0x29, 0x9c, - 0xac, 0x40, 0x39, 0xef, 0x3b, 0x5a, 0x2c, 0x7b, 0x7d, 0xc0, 0xf9, 0x1e, 0xd6, 0x46, 0xc6, 0x35, - 0x27, 0xb2, 0x02, 0x65, 0xd9, 0x03, 0x31, 0xf6, 0xbc, 0xd7, 0x07, 0xb2, 0xc7, 0xd1, 0x12, 0x69, - 0x14, 0x60, 0xe8, 0x59, 0x4f, 0x2f, 0xb6, 0x7f, 0x9e, 0x85, 0x19, 0x8c, 0x4b, 0x7e, 0xb4, 0xa0, - 0xa4, 0x87, 0x07, 0xd9, 0x1c, 0xd1, 0xee, 0xe1, 0x69, 0x65, 0x6f, 0x5d, 0x85, 0xaa, 0xfd, 0x39, - 0xce, 0x0f, 0x7f, 0xfd, 0xf7, 0x6a, 0x72, 0x85, 0xd8, 0xae, 0xd1, 0x0c, 0x0e, 0x46, 0x3d, 0xa9, - 0xc8, 0x6f, 0x16, 0x2c, 0x0c, 0x8d, 0x0a, 0xf2, 0x60, 0x5c, 0x96, 0x51, 0x43, 0xcd, 0x7e, 0x78, - 0x4d, 0x95, 0xb1, 0x79, 0x1f, 0x6d, 0x6e, 0x91, 0x8d, 0x22, 0x9b, 0xb4, 0x2f, 0x73, 0x8f, 0x75, - 0xe3, 0x4e, 0xc8, 0x2b, 0x0b, 0xca, 0xf9, 0x34, 0x22, 0x1f, 0x8d, 0x4b, 0x7b, 0x71, 0xc8, 0xd9, - 0xd5, 0x2b, 0xb2, 0x8d, 0xb9, 0x4d, 0x34, 0x77, 0x97, 0xac, 0x17, 0x99, 0xc3, 0x79, 0xe7, 0x1e, - 0xe3, 0xcf, 0x09, 0xf9, 0xd5, 0x82, 0x1b, 0xe7, 0x1f, 0x28, 0xa9, 0x8f, 0x4b, 0x56, 0x38, 0x42, - 0xec, 0xed, 0xeb, 0x48, 0x8c, 0x49, 0x17, 0x4d, 0x6e, 0x92, 0x7b, 0x45, 0x26, 0xf3, 0x2b, 0xed, - 0x1e, 0x9b, 0x61, 0x74, 0x42, 0xfe, 0xb4, 0x60, 0xa9, 0xf8, 0xb9, 0x93, 0x47, 0xe3, 0xf2, 0x8f, - 0x9d, 0x30, 0xf6, 0xee, 0xdb, 0x48, 0x4d, 0x09, 0x3b, 0x58, 0xc2, 0x7d, 0x52, 0x2b, 0x2a, 0x21, - 0x1b, 0x3a, 0x55, 0x7f, 0x40, 0xdc, 0xbf, 0x0a, 0x7f, 0x58, 0x40, 0x86, 0x9f, 0x28, 0x79, 0x78, - 0xa5, 0x53, 0xbc, 0x38, 0x2a, 0xec, 0x9d, 0xeb, 0xca, 0x8c, 0xfb, 0x5d, 0x74, 0xff, 0x80, 0x6c, - 0x17, 0xba, 0xef, 0xd1, 0x73, 0xd7, 0xee, 0x71, 0xde, 0x94, 0x93, 0xbd, 0xef, 0x5e, 0x9f, 0x56, - 0xac, 0x37, 0xa7, 0x15, 0xeb, 0xdf, 0xd3, 0x8a, 0xf5, 0xcb, 0x59, 0x65, 0xe2, 0xcd, 0x59, 0x65, - 0xe2, 0xef, 0xb3, 0xca, 0xc4, 0xb3, 0xdd, 0x90, 0xab, 0x83, 0xb4, 0x59, 0xf3, 0x45, 0x27, 0x8f, - 0x1b, 0x8a, 0x2a, 0x7b, 0xc1, 0xfc, 0x34, 0x3b, 0x85, 0x2a, 0x6d, 0xfa, 0xdc, 0xed, 0x88, 0x20, - 0x6d, 0x33, 0x99, 0x67, 0xc4, 0x2f, 0x9e, 0x66, 0x09, 0x3f, 0x79, 0x3e, 0xfe, 0x3f, 0x00, 0x00, - 0xff, 0xff, 0xf4, 0x71, 0xc5, 0x63, 0xb1, 0x09, 0x00, 0x00, + 0x9a, 0x04, 0xe2, 0xad, 0x03, 0x0d, 0x34, 0x70, 0x28, 0x41, 0x20, 0x7a, 0x2b, 0x1b, 0xe0, 0xd0, + 0x8b, 0x35, 0xde, 0x1d, 0x6f, 0x46, 0xb1, 0x77, 0x36, 0x3b, 0xb3, 0x6e, 0x2b, 0xcb, 0x17, 0x38, + 0x72, 0x41, 0xaa, 0xf8, 0x1f, 0x38, 0x73, 0xe6, 0x8a, 0xe8, 0xb1, 0x12, 0x17, 0x4e, 0x08, 0x25, + 0xfc, 0x21, 0x68, 0xdf, 0x8c, 0xd7, 0x76, 0xb2, 0x36, 0x09, 0x27, 0x7b, 0xbe, 0x79, 0xdf, 0x7b, + 0xdf, 0x9b, 0x79, 0xf3, 0x69, 0x61, 0x33, 0x11, 0xad, 0xd6, 0x09, 0x57, 0x32, 0x38, 0x71, 0x23, + 0xa6, 0x9e, 0x89, 0xe4, 0xc4, 0xed, 0xd4, 0xdc, 0xd3, 0x94, 0x25, 0x2f, 0xaa, 0x71, 0x22, 0x94, + 0x20, 0xb7, 0x07, 0x21, 0x55, 0x13, 0x52, 0xed, 0xd4, 0xec, 0x1d, 0x5f, 0xc8, 0xb6, 0x90, 0x6e, + 0x83, 0x4a, 0xa6, 0xe3, 0xdd, 0x4e, 0xad, 0xc1, 0x14, 0xad, 0xb9, 0x31, 0x0d, 0x79, 0x44, 0x15, + 0x17, 0x91, 0x4e, 0x61, 0x2f, 0x87, 0x22, 0x14, 0xf8, 0xd7, 0xcd, 0xfe, 0x19, 0x74, 0x2d, 0x14, + 0x22, 0x6c, 0x31, 0x97, 0xc6, 0xdc, 0xa5, 0x51, 0x24, 0x14, 0x52, 0xa4, 0xd9, 0x1d, 0xa3, 0x4c, + 0xbd, 0x88, 0x99, 0x09, 0x71, 0x96, 0x81, 0x7c, 0x95, 0x15, 0x7e, 0x42, 0x13, 0xda, 0x96, 0x1e, + 0x3b, 0x4d, 0x99, 0x54, 0x8e, 0x07, 0xb7, 0x46, 0x50, 0x19, 0x8b, 0x48, 0x32, 0xf2, 0x31, 0x94, + 0x62, 0x44, 0x56, 0xad, 0x3b, 0xd6, 0xd6, 0xc2, 0xde, 0x7a, 0xb5, 0xb0, 0xaf, 0xaa, 0xa6, 0x1d, + 0xce, 0xbe, 0xfa, 0x6b, 0x63, 0xca, 0x33, 0x14, 0xe7, 0x43, 0x58, 0xc7, 0x9c, 0x9f, 0x2a, 0xc5, + 0xa4, 0xd6, 0x79, 0xc8, 0x55, 0x9b, 0xc6, 0xa6, 0x28, 0x59, 0x81, 0xd2, 0x31, 0xe3, 0xe1, 0xb1, + 0xc2, 0xec, 0x33, 0x9e, 0x59, 0x39, 0x0d, 0xa8, 0x8c, 0x23, 0x1a, 0x5d, 0x8f, 0xa0, 0xd4, 0x40, + 0xc4, 0xe8, 0xda, 0x1a, 0xa3, 0xeb, 0x72, 0x06, 0xc3, 0x73, 0x76, 0xe1, 0x36, 0xd6, 0xf8, 0x3c, + 0x16, 0xfe, 0xf1, 0xe3, 0xa8, 0x29, 0xfa, 0xa2, 0x96, 0x61, 0x8e, 0x65, 0x18, 0x66, 0x9e, 0xf5, + 0xf4, 0xc2, 0xf9, 0x61, 0x1a, 0x56, 0x2e, 0xc6, 0x1b, 0x2d, 0x85, 0x04, 0xb2, 0x09, 0x8b, 0x52, + 0xd1, 0x44, 0xd5, 0x4d, 0x87, 0xd3, 0xd8, 0xe1, 0x02, 0x62, 0x5f, 0x22, 0x44, 0xd6, 0x01, 0x58, + 0x14, 0xf4, 0x03, 0x66, 0x30, 0xa0, 0xcc, 0xa2, 0xc0, 0x6c, 0xd7, 0x60, 0x39, 0xa6, 0x89, 0xe2, + 0x3e, 0x8f, 0xb1, 0x81, 0xba, 0xe9, 0x78, 0xf6, 0x8e, 0xb5, 0xb5, 0xe8, 0xdd, 0x1a, 0xd9, 0xd3, + 0xcd, 0x91, 0x77, 0x61, 0x89, 0xfa, 0x8a, 0x77, 0x58, 0xbd, 0x43, 0x5b, 0x3c, 0xa0, 0x4a, 0x24, + 0x72, 0x75, 0x0e, 0x65, 0xbd, 0xa9, 0x37, 0xbe, 0xcd, 0x71, 0xf2, 0x10, 0x56, 0x87, 0x72, 0x44, + 0xe1, 0x30, 0xa7, 0x84, 0x9c, 0xb7, 0x46, 0xf6, 0x07, 0x54, 0x67, 0x1f, 0x6c, 0x3c, 0x8c, 0x1c, + 0x7a, 0x1c, 0x05, 0xec, 0x79, 0xff, 0x04, 0x57, 0xe1, 0x0d, 0x1a, 0x04, 0x09, 0x93, 0x7a, 0x6a, + 0xca, 0x5e, 0x7f, 0xe9, 0x3c, 0x85, 0xb7, 0x0b, 0x79, 0xf9, 0xb4, 0xcd, 0xf1, 0x0c, 0x30, 0x97, + 0xfa, 0xce, 0x98, 0x4b, 0xbd, 0xc0, 0xd6, 0x1c, 0xe7, 0x13, 0x70, 0x30, 0xf7, 0x91, 0x68, 0xaa, + 0xcf, 0x44, 0xd4, 0xe4, 0x49, 0x1b, 0x8f, 0xe6, 0x48, 0x51, 0x95, 0xca, 0xff, 0x1a, 0xb9, 0x5f, + 0x2d, 0xb8, 0x3b, 0x91, 0x6e, 0x24, 0xee, 0xc0, 0x12, 0x97, 0x75, 0x29, 0x9a, 0xaa, 0xee, 0xeb, + 0x28, 0x16, 0x60, 0xaa, 0x79, 0xef, 0x26, 0x97, 0x43, 0x64, 0x16, 0x90, 0x0d, 0x58, 0xe8, 0x08, + 0xc5, 0x82, 0x7a, 0x2c, 0x9e, 0xb1, 0x04, 0x27, 0x60, 0xd6, 0x03, 0x84, 0x9e, 0x64, 0x48, 0x16, + 0xa0, 0x84, 0xa2, 0x2d, 0x13, 0x30, 0xa3, 0x03, 0x10, 0xd2, 0x01, 0xf7, 0xe0, 0xe6, 0x69, 0x2a, + 0x92, 0xb4, 0x5d, 0x6f, 0x26, 0xd9, 0xfd, 0x89, 0x08, 0x6f, 0xbf, 0xec, 0xdd, 0xd0, 0xf0, 0x17, + 0x06, 0x75, 0xa8, 0x79, 0x31, 0xf9, 0xd1, 0x1c, 0xf1, 0x30, 0xa2, 0x2a, 0x4d, 0x58, 0xbf, 0xf1, + 0x4d, 0x58, 0x6c, 0xb4, 0x84, 0x7f, 0x52, 0x1f, 0x69, 0x7f, 0x01, 0x31, 0x33, 0x70, 0x6b, 0x50, + 0xce, 0x47, 0x00, 0xd5, 0x96, 0xbd, 0x01, 0xe0, 0x7c, 0x03, 0x1b, 0x63, 0x4b, 0x98, 0xc3, 0x59, + 0x83, 0xb2, 0xec, 0x83, 0x58, 0x60, 0xd1, 0x1b, 0x00, 0xd9, 0x3b, 0x69, 0x8a, 0x34, 0x0a, 0x30, + 0xf5, 0xbc, 0xa7, 0x17, 0x7b, 0x3f, 0xcd, 0xc3, 0x1c, 0xe6, 0x25, 0xdf, 0x5b, 0x50, 0xd2, 0x3e, + 0x42, 0xb6, 0xc7, 0xdc, 0xfc, 0x65, 0xe3, 0xb2, 0x77, 0xae, 0x12, 0xaa, 0xf5, 0x39, 0xce, 0x77, + 0x7f, 0xfc, 0xf3, 0x72, 0x7a, 0x8d, 0xd8, 0xae, 0xe1, 0x0c, 0x7b, 0xa4, 0x36, 0x2d, 0xf2, 0x8b, + 0x05, 0x4b, 0x97, 0x5c, 0x83, 0x7c, 0x30, 0xa9, 0xca, 0x38, 0x7f, 0xb3, 0x1f, 0x5c, 0x93, 0x65, + 0x64, 0xde, 0x47, 0x99, 0x3b, 0x64, 0xab, 0x48, 0x26, 0x1d, 0xd0, 0xdc, 0xae, 0xbe, 0xcb, 0x1e, + 0x79, 0x69, 0x41, 0x39, 0x37, 0x26, 0xf2, 0xde, 0xa4, 0xb2, 0x17, 0xfd, 0xce, 0xde, 0xbd, 0x62, + 0xb4, 0x11, 0xb7, 0x8d, 0xe2, 0xee, 0x92, 0xcd, 0x22, 0x71, 0x68, 0x7d, 0x6e, 0x17, 0x7f, 0x7a, + 0xe4, 0x67, 0x0b, 0x6e, 0x8c, 0xbe, 0x55, 0x52, 0x9b, 0x54, 0xac, 0xd0, 0x4d, 0xec, 0xbd, 0xeb, + 0x50, 0x8c, 0x48, 0x17, 0x45, 0x6e, 0x93, 0x7b, 0x45, 0x22, 0xf3, 0x91, 0x76, 0xbb, 0xc6, 0x97, + 0x7a, 0xe4, 0x77, 0x0b, 0x56, 0x8a, 0x5f, 0x3e, 0x79, 0x38, 0xa9, 0xfe, 0x44, 0xb3, 0xb1, 0x0f, + 0xfe, 0x0f, 0xd5, 0xb4, 0xb0, 0x8f, 0x2d, 0xdc, 0x27, 0xd5, 0xa2, 0x16, 0x32, 0xff, 0xd9, 0xf5, + 0x87, 0xc8, 0x83, 0x51, 0xf8, 0xcd, 0x02, 0x72, 0xf9, 0x89, 0x92, 0x07, 0x57, 0x3a, 0xc5, 0x8b, + 0xae, 0x61, 0xef, 0x5f, 0x97, 0x66, 0xd4, 0x3f, 0x42, 0xf5, 0x07, 0xe4, 0xa3, 0x42, 0xf5, 0xfd, + 0x70, 0xb7, 0x3b, 0x6c, 0x49, 0x3d, 0xb7, 0x9b, 0x5f, 0x4d, 0xef, 0xf0, 0xeb, 0x57, 0x67, 0x15, + 0xeb, 0xf5, 0x59, 0xc5, 0xfa, 0xfb, 0xac, 0x62, 0xfd, 0x78, 0x5e, 0x99, 0x7a, 0x7d, 0x5e, 0x99, + 0xfa, 0xf3, 0xbc, 0x32, 0xf5, 0xf4, 0x20, 0xe4, 0xea, 0x38, 0x6d, 0x54, 0x7d, 0xd1, 0xce, 0xb3, + 0x87, 0x62, 0x97, 0x3d, 0x67, 0x7e, 0x9a, 0x9d, 0xc5, 0x2e, 0x6d, 0xf8, 0xdc, 0x6d, 0x8b, 0x20, + 0x6d, 0x31, 0x99, 0xd7, 0xc5, 0x4f, 0xa0, 0x46, 0x09, 0xbf, 0x81, 0xde, 0xff, 0x37, 0x00, 0x00, + 0xff, 0xff, 0x65, 0xa2, 0x2b, 0x9c, 0xc2, 0x09, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1367,8 +1368,8 @@ func (m *QueryValidatorSignatureRequest) MarshalToSizedBuffer(dAtA []byte) (int, i-- dAtA[i] = 0x12 } - if m.Height != 0 { - i = encodeVarintQuery(dAtA, i, uint64(m.Height)) + if m.BlockHeight != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.BlockHeight)) i-- dAtA[i] = 0x8 } @@ -1577,8 +1578,8 @@ func (m *QueryValidatorSignatureRequest) Size() (n int) { } var l int _ = l - if m.Height != 0 { - n += 1 + sovQuery(uint64(m.Height)) + if m.BlockHeight != 0 { + n += 1 + sovQuery(uint64(m.BlockHeight)) } l = len(m.Validator) if l > 0 { @@ -2553,9 +2554,9 @@ func (m *QueryValidatorSignatureRequest) Unmarshal(dAtA []byte) error { switch fieldNum { case 1: if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field BlockHeight", wireType) } - m.Height = 0 + m.BlockHeight = 0 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowQuery @@ -2565,7 +2566,7 @@ func (m *QueryValidatorSignatureRequest) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Height |= int64(b&0x7F) << shift + m.BlockHeight |= int64(b&0x7F) << shift if b < 0x80 { break } diff --git a/modules/network/types/query.pb.gw.go b/modules/network/types/query.pb.gw.go index 8d72787f..d4959f6d 100644 --- a/modules/network/types/query.pb.gw.go +++ b/modules/network/types/query.pb.gw.go @@ -278,15 +278,15 @@ func request_Query_ValidatorSignature_0(ctx context.Context, marshaler runtime.M _ = err ) - val, ok = pathParams["height"] + val, ok = pathParams["block_height"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "height") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "block_height") } - protoReq.Height, err = runtime.Int64(val) + protoReq.BlockHeight, err = runtime.Int64(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "height", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "block_height", err) } val, ok = pathParams["validator"] @@ -316,15 +316,15 @@ func local_request_Query_ValidatorSignature_0(ctx context.Context, marshaler run _ = err ) - val, ok = pathParams["height"] + val, ok = pathParams["block_height"] if !ok { - return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "height") + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "block_height") } - protoReq.Height, err = runtime.Int64(val) + protoReq.BlockHeight, err = runtime.Int64(val) if err != nil { - return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "height", err) + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "block_height", err) } val, ok = pathParams["validator"] @@ -662,7 +662,7 @@ var ( pattern_Query_SoftConfirmationStatus_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"rollkit", "network", "v1", "soft-confirmation", "height"}, "", runtime.AssumeColonVerbOpt(false))) - pattern_Query_ValidatorSignature_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 1, 0, 4, 1, 5, 5}, []string{"rollkit", "network", "v1", "signature", "height", "validator"}, "", runtime.AssumeColonVerbOpt(false))) + pattern_Query_ValidatorSignature_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4, 1, 0, 4, 1, 5, 5}, []string{"rollkit", "network", "v1", "signature", "block_height", "validator"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( diff --git a/pkg/rpc/core/blocks.go b/pkg/rpc/core/blocks.go index e400b243..46b84709 100644 --- a/pkg/rpc/core/blocks.go +++ b/pkg/rpc/core/blocks.go @@ -521,8 +521,8 @@ func buildCommitFromAttestations(ctx context.Context, height uint64, attestation // getValidatorSignatureFromQuery queries the signature for a specific validator func getValidatorSignatureFromQuery(ctx context.Context, height int64, validatorAddr string) ([]byte, error) { sigReq := &networktypes.QueryValidatorSignatureRequest{ - Height: height, - Validator: validatorAddr, + BlockHeight: height, + Validator: validatorAddr, } reqData, err := sigReq.Marshal() From a813fe6f4b94b3564ec6aea31e088ec2cf8c398f Mon Sep 17 00:00:00 2001 From: Randy Grok Date: Fri, 4 Jul 2025 12:33:12 +0200 Subject: [PATCH 04/17] use real validators --- pkg/rpc/core/blocks.go | 59 ++++++++++++++---------------------------- 1 file changed, 19 insertions(+), 40 deletions(-) diff --git a/pkg/rpc/core/blocks.go b/pkg/rpc/core/blocks.go index 46b84709..0933b38f 100644 --- a/pkg/rpc/core/blocks.go +++ b/pkg/rpc/core/blocks.go @@ -445,60 +445,39 @@ func buildCommitFromAttestations(ctx context.Context, height uint64, attestation return nil, fmt.Errorf("no attestation bitmap found for height %d", height) } - // Query all validators to get their addresses - queryReq := &abci.RequestQuery{ - Path: "/cosmos.staking.v1beta1.Query/Validators", - Data: []byte{}, // Empty request to get all validators + // Get validators from genesis (since we know there's exactly one validator) + genesisValidators := env.Adapter.AppGenesis.Consensus.Validators + if len(genesisValidators) == 0 { + return nil, fmt.Errorf("no validators found in genesis") } - valQueryRes, err := env.Adapter.App.Query(ctx, queryReq) - if err != nil { - return nil, fmt.Errorf("failed to query validators: %w", err) - } - if valQueryRes.Code != 0 { - return nil, fmt.Errorf("failed to query validators: %s", valQueryRes.Log) - } - - // For now, we'll construct a basic commit structure with the available data - // In a real implementation, you'd need to decode the validator response properly - - votes := make([]cmttypes.CommitSig, 0) - - // We need to iterate through the bitmap and for each set bit, get the validator signature - for i := 0; i < len(bitmap)*8; i++ { - // Check if validator at index i voted (bit is set) - if (bitmap[i/8] & (1 << (i % 8))) != 0 { - // This validator voted, let's try to get their signature - // For this we need the validator address corresponding to index i - // This would require a proper validator index mapping - - // For now, we'll create a placeholder vote with empty signature - // In a real implementation, you'd: - // 1. Get validator address from index i - // 2. Query the signature using ValidatorSignature query - // 3. Construct the proper CommitSig + votes := make([]cmttypes.CommitSig, len(genesisValidators)) + // Iterate only through the actual validators (not all bits in bitmap) + for i, genesisValidator := range genesisValidators { + // Check if this validator voted (bit is set in bitmap) + if i < len(bitmap)*8 && (bitmap[i/8]&(1<<(i%8))) != 0 { + // This validator voted, get their signature vote := cmttypes.CommitSig{ BlockIDFlag: cmttypes.BlockIDFlagCommit, - ValidatorAddress: make([]byte, 20), // Placeholder validator address - Timestamp: time.Now(), // Should be actual timestamp - Signature: nil, // We'll get this from the query below + ValidatorAddress: genesisValidator.Address, // Use real validator address + Timestamp: time.Now(), // Should be actual timestamp from attestation + Signature: nil, // We'll get this from the query below } - // Try to get the real signature (this is a simplified approach) - // In practice, you'd need to map the bitmap index to validator address - validatorAddr := fmt.Sprintf("validator_%d", i) // Placeholder + // Try to get the real signature using the validator's address + validatorAddr := genesisValidator.Address.String() signature, err := getValidatorSignatureFromQuery(ctx, int64(height), validatorAddr) if err == nil { vote.Signature = signature } - votes = append(votes, vote) + votes[i] = vote } else { - // Validator didn't vote, add nil vote - votes = append(votes, cmttypes.CommitSig{ + // Validator didn't vote, add absent vote + votes[i] = cmttypes.CommitSig{ BlockIDFlag: cmttypes.BlockIDFlagAbsent, - }) + } } } From cdd4e91e04bf1e6060945058842022a7ce24847f Mon Sep 17 00:00:00 2001 From: Alex Peters Date: Thu, 3 Jul 2025 16:43:07 +0200 Subject: [PATCH 05/17] x --- go.mod | 47 ++- go.sum | 101 +++---- hack/.gitignore | 3 + hack/README.md | 21 ++ hack/attester/.gitignore | 1 + hack/attester/README.md | 3 + hack/attester/main.go | 544 ++++++++++++++++++++++++++++++++++ hack/download.sh | 101 +++++++ hack/ibc-connection-hermes.sh | 131 ++++++++ hack/ibc-connection-rly.sh | 133 +++++++++ hack/ics20-token-transfer.sh | 166 +++++++++++ hack/init-gaia.sh | 98 ++++++ hack/run_gmd.sh | 147 +++++++++ pkg/adapter/adapter.go | 8 +- server/start.go | 3 + 15 files changed, 1417 insertions(+), 90 deletions(-) create mode 100644 hack/.gitignore create mode 100644 hack/README.md create mode 100644 hack/attester/.gitignore create mode 100644 hack/attester/README.md create mode 100644 hack/attester/main.go create mode 100755 hack/download.sh create mode 100755 hack/ibc-connection-hermes.sh create mode 100755 hack/ibc-connection-rly.sh create mode 100755 hack/ics20-token-transfer.sh create mode 100755 hack/init-gaia.sh create mode 100755 hack/run_gmd.sh diff --git a/go.mod b/go.mod index 5a17336e..2550e450 100644 --- a/go.mod +++ b/go.mod @@ -31,10 +31,10 @@ require ( github.com/libp2p/go-libp2p-pubsub v0.14.1 github.com/multiformats/go-multiaddr v0.16.0 github.com/prometheus/client_golang v1.22.0 - github.com/rollkit/rollkit v0.14.2-0.20250630141726-fa03f78d8121 - github.com/rollkit/rollkit/core v0.0.0-20250630141726-fa03f78d8121 - github.com/rollkit/rollkit/da v0.0.0-20250630141726-fa03f78d8121 - github.com/rollkit/rollkit/sequencers/single v0.0.0-20250630141726-fa03f78d8121 + github.com/rollkit/rollkit v0.14.2-0.20250703132945-e191259d4cac + github.com/rollkit/rollkit/core v0.0.0-20250703132945-e191259d4cac + github.com/rollkit/rollkit/da v0.0.0-20250703132945-e191259d4cac + github.com/rollkit/rollkit/sequencers/single v0.0.0-20250703132945-e191259d4cac github.com/rs/cors v1.11.1 github.com/spf13/cobra v1.9.1 github.com/stretchr/testify v1.10.0 @@ -125,7 +125,7 @@ require ( github.com/filecoin-project/go-jsonrpc v0.7.1 // indirect github.com/flynn/noise v1.1.0 // indirect github.com/francoispqt/gojay v1.2.13 // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/getsentry/sentry-go v0.27.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/go-chi/chi/v5 v5.2.2 // indirect @@ -134,6 +134,7 @@ require ( github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect + github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/goccy/go-yaml v1.18.0 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect @@ -158,14 +159,11 @@ require ( github.com/gorilla/websocket v1.5.3 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect - github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-hclog v1.6.3 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-plugin v1.6.3 // indirect github.com/hashicorp/golang-lru v1.0.2 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/yamux v0.1.1 // indirect github.com/hdevalence/ed25519consensus v0.1.0 // indirect github.com/huandu/skiplist v1.2.0 // indirect @@ -173,10 +171,9 @@ require ( github.com/iancoleman/strcase v0.3.0 // indirect github.com/improbable-eng/grpc-web v0.15.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/ipfs/boxo v0.27.4 // indirect + github.com/ipfs/boxo v0.30.0 // indirect github.com/ipfs/go-cid v0.5.0 // indirect github.com/ipfs/go-ds-badger4 v0.1.8 // indirect - github.com/ipfs/go-log v1.0.5 // indirect github.com/ipld/go-ipld-prime v0.21.0 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect @@ -193,20 +190,19 @@ require ( github.com/libp2p/go-cidranger v1.1.0 // indirect github.com/libp2p/go-flow-metrics v0.2.0 // indirect github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect - github.com/libp2p/go-libp2p-kad-dht v0.29.1 // indirect - github.com/libp2p/go-libp2p-kbucket v0.6.5 // indirect + github.com/libp2p/go-libp2p-kad-dht v0.33.1 // indirect + github.com/libp2p/go-libp2p-kbucket v0.7.0 // indirect github.com/libp2p/go-libp2p-record v0.3.1 // indirect - github.com/libp2p/go-libp2p-routing-helpers v0.7.4 // indirect + github.com/libp2p/go-libp2p-routing-helpers v0.7.5 // indirect github.com/libp2p/go-msgio v0.3.0 // indirect github.com/libp2p/go-netroute v0.2.2 // indirect github.com/libp2p/go-reuseport v0.4.0 // indirect github.com/libp2p/go-yamux/v5 v5.0.0 // indirect github.com/linxGnu/grocksdb v1.8.14 // indirect - github.com/magiconair/properties v1.8.7 // indirect github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/miekg/dns v1.1.64 // indirect + github.com/miekg/dns v1.1.66 // indirect github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect github.com/minio/highwayhash v1.0.3 // indirect @@ -244,9 +240,8 @@ require ( github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect github.com/opencontainers/runtime-spec v1.2.1 // indirect - github.com/opentracing/opentracing-go v1.2.0 // indirect github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect - github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect github.com/pion/datachannel v1.5.10 // indirect github.com/pion/dtls/v2 v2.2.12 // indirect @@ -271,9 +266,9 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/polydawn/refmt v0.89.0 // indirect - github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.63.0 // indirect - github.com/prometheus/procfs v0.16.0 // indirect + github.com/prometheus/procfs v0.16.1 // indirect github.com/quic-go/qpack v0.5.1 // indirect github.com/quic-go/quic-go v0.51.0 // indirect github.com/quic-go/webtransport-go v0.8.1-0.20241018022711-4ac2c9250e66 // indirect @@ -282,18 +277,17 @@ require ( github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/rs/zerolog v1.34.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/sagikazarmark/locafero v0.4.0 // indirect - github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sagikazarmark/locafero v0.7.0 // indirect github.com/sasha-s/go-deadlock v0.3.5 // indirect github.com/segmentio/asm v1.2.0 // indirect github.com/segmentio/encoding v0.4.1 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect - github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/afero v1.12.0 // indirect github.com/spf13/cast v1.7.1 // indirect github.com/spf13/pflag v1.0.6 // indirect - github.com/spf13/viper v1.19.0 // indirect + github.com/spf13/viper v1.20.1 // indirect github.com/stoewer/go-strcase v1.3.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect @@ -334,14 +328,13 @@ require ( golang.org/x/text v0.26.0 // indirect golang.org/x/tools v0.33.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect - gonum.org/v1/gonum v0.15.1 // indirect - google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect + gonum.org/v1/gonum v0.16.0 // indirect + google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a // indirect - gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools/v3 v3.5.2 // indirect - lukechampine.com/blake3 v1.4.0 // indirect + lukechampine.com/blake3 v1.4.1 // indirect nhooyr.io/websocket v1.8.6 // indirect pgregory.net/rapid v1.1.0 // indirect pluginrpc.com/pluginrpc v0.5.0 // indirect diff --git a/go.sum b/go.sum index c40f4e9e..17d9f7f8 100644 --- a/go.sum +++ b/go.sum @@ -323,8 +323,8 @@ github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7z github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= @@ -374,6 +374,8 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= +github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= @@ -513,8 +515,6 @@ github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NM github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= -github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= @@ -526,8 +526,6 @@ github.com/hashicorp/go-metrics v0.5.4 h1:8mmPiIJkTPPEbAiV97IxdAGNdRdaWwVap1BU6e github.com/hashicorp/go-metrics v0.5.4/go.mod h1:CG5yz4NZ/AI/aQt9Ucm/vdBnbh7fvmv4lxZ350i+QQI= github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-plugin v1.6.3 h1:xgHB+ZUSYeuJi96WtxEjzi23uh7YQpznjGh0U0UUrwg= github.com/hashicorp/go-plugin v1.6.3/go.mod h1:MRobyh+Wc/nYy1V4KAXUiYfzxoYhs7V1mlH1Z7iY2h0= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= @@ -545,8 +543,6 @@ github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iP github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= @@ -572,8 +568,8 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/ipfs/boxo v0.27.4 h1:6nC8lY5GnR6whAbW88hFz6L13wZUj2vr5BRe3iTvYBI= -github.com/ipfs/boxo v0.27.4/go.mod h1:qEIRrGNr0bitDedTCzyzBHxzNWqYmyuHgK8LG9Q83EM= +github.com/ipfs/boxo v0.30.0 h1:7afsoxPGGqfoH7Dum/wOTGUB9M5fb8HyKPMlLfBvIEQ= +github.com/ipfs/boxo v0.30.0/go.mod h1:BPqgGGyHB9rZZcPSzah2Dc9C+5Or3U1aQe7EH1H7370= github.com/ipfs/go-block-format v0.2.0 h1:ZqrkxBA2ICbDRbK8KJs/u0O3dlp6gmAuuXUJNiW1Ycs= github.com/ipfs/go-block-format v0.2.0/go.mod h1:+jpL11nFx5A/SPpsoBn6Bzkra/zaArfSmsknbPMYgzM= github.com/ipfs/go-cid v0.5.0 h1:goEKKhaGm0ul11IHA7I6p1GmKz8kEYniqFopaB5Otwg= @@ -586,13 +582,10 @@ github.com/ipfs/go-ds-badger4 v0.1.8 h1:frNczf5CjCVm62RJ5mW5tD/oLQY/9IKAUpKviRV9 github.com/ipfs/go-ds-badger4 v0.1.8/go.mod h1:FdqSLA5TMsyqooENB/Hf4xzYE/iH0z/ErLD6ogtfMrA= github.com/ipfs/go-ipfs-util v0.0.3 h1:2RFdGez6bu2ZlZdI+rWfIdbQb1KudQp3VGwPtdNCmE0= github.com/ipfs/go-ipfs-util v0.0.3/go.mod h1:LHzG1a0Ig4G+iZ26UUOMjHd+lfM84LZCrn17xAKWBvs= -github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8= -github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo= -github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g= github.com/ipfs/go-log/v2 v2.6.0 h1:2Nu1KKQQ2ayonKp4MPo6pXCjqw1ULc9iohRqWV5EYqg= github.com/ipfs/go-log/v2 v2.6.0/go.mod h1:p+Efr3qaY5YXpx9TX7MoLCSEZX5boSWj9wh86P5HJa8= -github.com/ipfs/go-test v0.0.4 h1:DKT66T6GBB6PsDFLoO56QZPrOmzJkqU1FZH5C9ySkew= -github.com/ipfs/go-test v0.0.4/go.mod h1:qhIM1EluEfElKKM6fnWxGn822/z9knUGM1+I/OAQNKI= +github.com/ipfs/go-test v0.2.1 h1:/D/a8xZ2JzkYqcVcV/7HYlCnc7bv/pKHQiX5TdClkPE= +github.com/ipfs/go-test v0.2.1/go.mod h1:dzu+KB9cmWjuJnXFDYJwC25T3j1GcN57byN+ixmK39M= github.com/ipld/go-ipld-prime v0.21.0 h1:n4JmcpOlPDIxBcY037SVfpd1G+Sj1nKZah0m6QH9C2E= github.com/ipld/go-ipld-prime v0.21.0/go.mod h1:3RLqy//ERg/y5oShXXdx5YIp50cFGOanyMctpPjsvxQ= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= @@ -669,16 +662,16 @@ github.com/libp2p/go-libp2p v0.41.1 h1:8ecNQVT5ev/jqALTvisSJeVNvXYJyK4NhQx1nNRXQ github.com/libp2p/go-libp2p v0.41.1/go.mod h1:DcGTovJzQl/I7HMrby5ZRjeD0kQkGiy+9w6aEkSZpRI= github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94= github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8= -github.com/libp2p/go-libp2p-kad-dht v0.29.1 h1:RyD1RnnkXOh1gwBCrMQ6ZVfTJECY5yDOY6qxt9VNqE4= -github.com/libp2p/go-libp2p-kad-dht v0.29.1/go.mod h1:tZEFTKWCsY0xngypKyAIwNDNZOBiikSUIgd/BjTF5Ms= -github.com/libp2p/go-libp2p-kbucket v0.6.5 h1:Fsl1YvZcMwqrR4DYrTO02yo9PGYs2HBQIT3lGXFMTxg= -github.com/libp2p/go-libp2p-kbucket v0.6.5/go.mod h1:U6WOd0BvnSp03IQSrjgM54tg7zh1UUNsXLJqAQzClTA= +github.com/libp2p/go-libp2p-kad-dht v0.33.1 h1:hKFhHMf7WH69LDjaxsJUWOU6qZm71uO47M/a5ijkiP0= +github.com/libp2p/go-libp2p-kad-dht v0.33.1/go.mod h1:CdmNk4VeGJa9EXM9SLNyNVySEvduKvb+5rSC/H4pLAo= +github.com/libp2p/go-libp2p-kbucket v0.7.0 h1:vYDvRjkyJPeWunQXqcW2Z6E93Ywx7fX0jgzb/dGOKCs= +github.com/libp2p/go-libp2p-kbucket v0.7.0/go.mod h1:blOINGIj1yiPYlVEX0Rj9QwEkmVnz3EP8LK1dRKBC6g= github.com/libp2p/go-libp2p-pubsub v0.14.1 h1:XK/rPKZKhPvRrtsjvfwrOZPnQQbGLmaEg7u6qnJfn8U= github.com/libp2p/go-libp2p-pubsub v0.14.1/go.mod h1:MKPU5vMI8RRFyTP0HfdsF9cLmL1nHAeJm44AxJGJx44= github.com/libp2p/go-libp2p-record v0.3.1 h1:cly48Xi5GjNw5Wq+7gmjfBiG9HCzQVkiZOUZ8kUl+Fg= github.com/libp2p/go-libp2p-record v0.3.1/go.mod h1:T8itUkLcWQLCYMqtX7Th6r7SexyUJpIyPgks757td/E= -github.com/libp2p/go-libp2p-routing-helpers v0.7.4 h1:6LqS1Bzn5CfDJ4tzvP9uwh42IB7TJLNFJA6dEeGBv84= -github.com/libp2p/go-libp2p-routing-helpers v0.7.4/go.mod h1:we5WDj9tbolBXOuF1hGOkR+r7Uh1408tQbAKaT5n1LE= +github.com/libp2p/go-libp2p-routing-helpers v0.7.5 h1:HdwZj9NKovMx0vqq6YNPTh6aaNzey5zHD7HeLJtq6fI= +github.com/libp2p/go-libp2p-routing-helpers v0.7.5/go.mod h1:3YaxrwP0OBPDD7my3D0KxfR89FlcX/IEbxDEDfAmj98= github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg= github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0= @@ -720,8 +713,8 @@ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.64 h1:wuZgD9wwCE6XMT05UU/mlSko71eRSXEAm2EbjQXLKnQ= -github.com/miekg/dns v1.1.64/go.mod h1:Dzw9769uoKVaLuODMDZz9M6ynFU6Em65csPuoi8G0ck= +github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE= +github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms= github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc= @@ -864,8 +857,6 @@ github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= -github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= @@ -880,8 +871,8 @@ github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144T github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= -github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 h1:Dx7Ovyv/SFnMFw3fD4oEoeorXc6saIiQ23LrGLth0Gw= github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= @@ -963,8 +954,8 @@ github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1: github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= -github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= @@ -983,8 +974,8 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.16.0 h1:xh6oHhKwnOJKMYiYBDWmkHqQPyiY40sny36Cmx2bbsM= -github.com/prometheus/procfs v0.16.0/go.mod h1:8veyXUu3nGP7oaCxhX6yeaM5u4stL2FeMXnCqhDthZg= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= github.com/quic-go/quic-go v0.51.0 h1:K8exxe9zXxeRKxaXxi/GpUqYiTrtdiWP8bo1KFya6Wc= @@ -1002,14 +993,14 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/rollkit/rollkit v0.14.2-0.20250630141726-fa03f78d8121 h1:CDTRFiNOkYoYwH7kp8vBlA508wmqYvIex7uheD1HonM= -github.com/rollkit/rollkit v0.14.2-0.20250630141726-fa03f78d8121/go.mod h1:voFC1cTCp9osZRb7DWeLLWLAtRI0DWiWoXdpaS5DkeA= -github.com/rollkit/rollkit/core v0.0.0-20250630141726-fa03f78d8121 h1:hZd7CBZXDadofPmmYcnRJF++EgA0UFKcM9B/aukPrJA= -github.com/rollkit/rollkit/core v0.0.0-20250630141726-fa03f78d8121/go.mod h1:0RhbqC8Is970KRhr6zPUQOZkmKt6/WqPRDQWfd2P7P0= -github.com/rollkit/rollkit/da v0.0.0-20250630141726-fa03f78d8121 h1:XDUT6BWc6j/5FV8Gv2iq3AzaPFIFb8pSLabetiAx3KA= -github.com/rollkit/rollkit/da v0.0.0-20250630141726-fa03f78d8121/go.mod h1:oiubohaox9z/Zl5lopu5nXySOIoMtFPJUdrBrlzHIDs= -github.com/rollkit/rollkit/sequencers/single v0.0.0-20250630141726-fa03f78d8121 h1:cPzpb7Ne2lohk8QmvsBHrQ4JRBXo0RmzgYske6SwtDQ= -github.com/rollkit/rollkit/sequencers/single v0.0.0-20250630141726-fa03f78d8121/go.mod h1:xu+rEtcXynLSwB7u/jnNsRiBwmxCLhP3cjH+tEBOAvc= +github.com/rollkit/rollkit v0.14.2-0.20250703132945-e191259d4cac h1:sL+Qcxbjm3Gqaj7usmChWJqbBTZ7S4jYqq2JmS9kBK4= +github.com/rollkit/rollkit v0.14.2-0.20250703132945-e191259d4cac/go.mod h1:RXYW7SkD5sGpA6RpgNrRtZo9UnoOMNKYkffS37Utn+Y= +github.com/rollkit/rollkit/core v0.0.0-20250703132945-e191259d4cac h1:z0FkzJCSL4QfNKcCP5LcPcEBhZxVh1spF8DwrYBM4ac= +github.com/rollkit/rollkit/core v0.0.0-20250703132945-e191259d4cac/go.mod h1:0RhbqC8Is970KRhr6zPUQOZkmKt6/WqPRDQWfd2P7P0= +github.com/rollkit/rollkit/da v0.0.0-20250703132945-e191259d4cac h1:javMdMHVDTqzWhU7eky06dqf2ZZGOLxmYg+Ac28O7QE= +github.com/rollkit/rollkit/da v0.0.0-20250703132945-e191259d4cac/go.mod h1:oiubohaox9z/Zl5lopu5nXySOIoMtFPJUdrBrlzHIDs= +github.com/rollkit/rollkit/sequencers/single v0.0.0-20250703132945-e191259d4cac h1:+hf9Mxo28MRlW4avG+eNzJE28ejuCma5wyTXYMStiq0= +github.com/rollkit/rollkit/sequencers/single v0.0.0-20250703132945-e191259d4cac/go.mod h1:BXo3oPT5TDgrHNuG7HntzR+jCDYctYpAtkQx0BlyARg= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= @@ -1021,10 +1012,8 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= -github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= -github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= -github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= +github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU= github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U= @@ -1077,8 +1066,8 @@ github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIK github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= -github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= +github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= @@ -1088,8 +1077,8 @@ github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= -github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= +github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= +github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= @@ -1113,7 +1102,6 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= @@ -1202,7 +1190,6 @@ go.opentelemetry.io/proto/otlp v1.6.0/go.mod h1:cicgGehlFuNdgZkcALOCh3VE6K/u2tAj go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= @@ -1217,14 +1204,12 @@ go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= @@ -1468,8 +1453,8 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -gonum.org/v1/gonum v0.15.1 h1:FNy7N6OUZVUaWG9pTiD+jlhdQ3lMP+/LcTpJ6+a8sQ0= -gonum.org/v1/gonum v0.15.1/go.mod h1:eZTZuRFrzu5pcyjN5wJhcIhnUdNijYxX1T2IcrOGY0o= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= @@ -1492,8 +1477,8 @@ google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= -google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= +google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk= +google.golang.org/genproto v0.0.0-20241118233622-e639e219e697/go.mod h1:JJrvXBWRZaFMxBufik1a4RpFw4HhgVtBBWQeQgUj2cc= google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2 h1:vPV0tzlsK6EzEDHNNH5sa7Hs9bd7iXR7B1tSiPepkV0= google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:pKLAc5OolXC3ViWGI62vvC0n10CpwAtRcTNCFwTKBEw= google.golang.org/genproto/googleapis/rpc v0.0.0-20250528174236-200df99c418a h1:v2PbRU4K3llS09c7zodFpNePeamkAwG3mPrAery9VeE= @@ -1548,8 +1533,6 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= @@ -1576,8 +1559,8 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -lukechampine.com/blake3 v1.4.0 h1:xDbKOZCVbnZsfzM6mHSYcGRHZ3YrLDzqz8XnV4uaD5w= -lukechampine.com/blake3 v1.4.0/go.mod h1:MQJNQCTnR+kwOP/JEZSxj3MaQjp80FOFSNMMHXcSeX0= +lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg= +lukechampine.com/blake3 v1.4.1/go.mod h1:QFosUxmjB8mnrWFSNwKmvxHpfY72bmD2tQ0kBMM3kwo= nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k= nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= diff --git a/hack/.gitignore b/hack/.gitignore new file mode 100644 index 00000000..934e3e0e --- /dev/null +++ b/hack/.gitignore @@ -0,0 +1,3 @@ +downloads/ +testnet/ +config/ diff --git a/hack/README.md b/hack/README.md new file mode 100644 index 00000000..7a15364b --- /dev/null +++ b/hack/README.md @@ -0,0 +1,21 @@ +# Hack + +Local test scripts + +## Requirements + +* `gaiad` + `hermes` app are available. Use `download.sh` script to fetch +* Rollkit example app `gmd` is built with network integration and available in the path +* `local-da` is built and located in `../../rollkit/build/local-da` + +## Run local setup + +```shell +./init-gaia.sh # setup and start gaia app +./run_node.sh # setup and start gmd example app + local da + +go run ./attester --chain-id=rollkitnet-1 --home=$(pwd)/testnet/gm --mnemonic="abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art" --verbose + +./ibc-connection-hermes.sh # relayer +./ics20-token-transfer.sh # token transfer +``` diff --git a/hack/attester/.gitignore b/hack/attester/.gitignore new file mode 100644 index 00000000..b26c9b08 --- /dev/null +++ b/hack/attester/.gitignore @@ -0,0 +1 @@ +/attester diff --git a/hack/attester/README.md b/hack/attester/README.md new file mode 100644 index 00000000..88b97b5a --- /dev/null +++ b/hack/attester/README.md @@ -0,0 +1,3 @@ +# Attest everything attester + +little helper that pulls new blocks from the rpc node and submits (fake) attestations after each epoch diff --git a/hack/attester/main.go b/hack/attester/main.go new file mode 100644 index 00000000..452e9faf --- /dev/null +++ b/hack/attester/main.go @@ -0,0 +1,544 @@ +package main + +import ( + "bytes" + "context" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "os" + "os/signal" + "path/filepath" + "strconv" + "syscall" + "time" + + pvm "github.com/cometbft/cometbft/privval" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/crypto/hd" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + "github.com/cosmos/cosmos-sdk/std" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" + authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/cosmos/gogoproto/proto" + "github.com/spf13/cobra" + + networktypes "github.com/rollkit/go-execution-abci/modules/network/types" +) + +const ( + flagChainID = "chain-id" + flagNode = "node" + flagAPIAddr = "api-addr" + flagHome = "home" + flagVerbose = "verbose" + flagMnemonic = "mnemonic" +) + +func main() { + rootCmd := &cobra.Command{ + Use: "attester_ws", + Short: "Attester client for Rollkit using websocket", + Long: `Attester client for Rollkit that joins the attester set and attests to blocks at the end of each epoch.`, + DisableFlagParsing: false, + SuggestionsMinimumDistance: 2, + RunE: runAttester, + } + + // Add flags + rootCmd.Flags().String(flagChainID, "", "Chain ID of the blockchain") + rootCmd.Flags().String(flagNode, "tcp://localhost:26657", "RPC node address") + rootCmd.Flags().String(flagAPIAddr, "http://localhost:1317", "API node address") + rootCmd.Flags().String(flagHome, "", "Directory for config and data") + rootCmd.Flags().Bool(flagVerbose, false, "Enable verbose output") + rootCmd.Flags().String(flagMnemonic, "", "Mnemonic for the private key") + + _ = rootCmd.MarkFlagRequired(flagChainID) + _ = rootCmd.MarkFlagRequired(flagMnemonic) + + // Execute + if err := rootCmd.Execute(); err != nil { + fmt.Println(err) + os.Exit(1) + } +} + +func runAttester(cmd *cobra.Command, args []string) error { + chainID, err := cmd.Flags().GetString(flagChainID) + if err != nil { + return err + } + + node, err := cmd.Flags().GetString(flagNode) + if err != nil { + return err + } + apiAddr, err := cmd.Flags().GetString(flagAPIAddr) + if err != nil { + return err + } + + home, err := cmd.Flags().GetString(flagHome) + if err != nil { + return err + } + + verbose, err := cmd.Flags().GetBool(flagVerbose) + if err != nil { + return err + } + + mnemonic, err := cmd.Flags().GetString(flagMnemonic) + if err != nil { + return err + } + + // Create context with cancellation + ctx, cancel := context.WithCancel(cmd.Context()) + defer cancel() + + config := sdk.GetConfig() + config.SetBech32PrefixForAccount("gm", "gmpub") + config.SetBech32PrefixForValidator("gmvaloper", "gmvaloperpub") + config.Seal() + + // Create the sender key from the mnemonic + senderKey, err := createPrivateKeyFromMnemonic(mnemonic) + if err != nil { + return fmt.Errorf("failed to create private key from mnemonic: %w", err) + } + + // Get the account address from the private key + pubKey := senderKey.PubKey() + valAddr := sdk.ValAddress(pubKey.Address()) + + if verbose { + addr := sdk.AccAddress(pubKey.Address()) + fmt.Printf("Sender Account address: %s\n", addr.String()) + fmt.Printf("Sender Validator address: %s\n", valAddr.String()) + } + + pv := pvm.LoadOrGenFilePV(filepath.Join(home, "config", "priv_validator_key.json"), filepath.Join(home, "data", "priv_validator_state.json")) + + // Handle OS signals for graceful shutdown + sigCh := make(chan os.Signal, 1) + signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) + go func() { + <-sigCh + fmt.Println("Received signal, shutting down...") + cancel() + }() + + // Step 1: Join attester set + fmt.Println("Joining attester set...") + if err := joinAttesterSet(ctx, chainID, node, verbose, valAddr, senderKey, pv); err != nil { + return fmt.Errorf("join attester set: %w", err) + } + + // Step 2: Query network parameters to get epoch length + fmt.Println("Querying network parameters...") + epochLength, err := getEpochLength(apiAddr) + if err != nil { + return fmt.Errorf("get epoch length: %w", err) + } + fmt.Printf("Epoch length: %d blocks\n", epochLength) + + // Get validator address from private key + valAccAddr := sdk.AccAddress(senderKey.PubKey().Address()) + + // Step 3 & 4: Watch new block events via websocket and attest at the end of each epoch + fmt.Println("Starting to watch for new blocks...") + if err := pullBlocksAndAttest(ctx, chainID, node, home, epochLength, valAccAddr.Bytes(), verbose, senderKey, pv); err != nil { + return fmt.Errorf("error watching blocks: %w", err) + } + + return nil +} + +// joinAttesterSet creates and submits a MsgJoinAttesterSet transaction +func joinAttesterSet(ctx context.Context, chainID, node string, verbose bool, valAddr sdk.ValAddress, privKey *secp256k1.PrivKey, pv *pvm.FilePV) error { + if verbose { + fmt.Printf("Using validator address: %s\n", valAddr.String()) + } + + // Create the message + msg := networktypes.NewMsgJoinAttesterSet(valAddr.String()) + + // Broadcast the transaction + txHash, err := broadcastTx(ctx, chainID, node, msg, privKey, verbose) + if err != nil { + return fmt.Errorf("broadcast join attester set tx: %w", err) + } + + fmt.Printf("Successfully joined attester set. Tx hash: %s\n", txHash) + return nil +} + +// getEpochLength queries the network parameters to get the epoch length +func getEpochLength(apiAddr string) (uint64, error) { + // Create a simple HTTP client to query the node + httpClient := &http.Client{ + Timeout: 10 * time.Second, + } + + // Get epoch parameters + paramsResp, err := httpClient.Get(fmt.Sprintf("%s/rollkit/network/v1/params", apiAddr)) + if err != nil { + return 0, fmt.Errorf("error getting params: %w", err) + } + defer paramsResp.Body.Close() //nolint:errcheck // test code + + var paramsResult struct { + Params struct { + EpochLength string `json:"epoch_length"` + } `json:"params"` + } + var buf bytes.Buffer + + if err := json.NewDecoder(io.TeeReader(paramsResp.Body, &buf)).Decode(¶msResult); err != nil { + return 0, fmt.Errorf("error decoding params: %w: got %s", err, buf.String()) + } + + epochLength, err := strconv.ParseUint(paramsResult.Params.EpochLength, 10, 64) + if err != nil { + return 0, fmt.Errorf("epoch length: %w", err) + } + if epochLength == 0 { + return 0, fmt.Errorf("epoch length is 0") + } + + return epochLength, nil +} + +// pullBlocksAndAttest polls for new blocks via HTTP and attests at the end of each epoch +func pullBlocksAndAttest( + ctx context.Context, + chainID, node, home string, + epochLength uint64, + valAddr sdk.ValAddress, + verbose bool, + senderKey *secp256k1.PrivKey, + pv *pvm.FilePV, +) error { + // Parse node URL + parsed, err := url.Parse(node) + if err != nil { + return fmt.Errorf("parse node URL: %w", err) + } + + httpClient := &http.Client{ + Timeout: 10 * time.Second, + } + + var lastAttested int64 = 0 + + // Poll for new blocks + for { + select { + case <-ctx.Done(): + return nil + default: + // Query latest block + resp, err := httpClient.Get(fmt.Sprintf("http://%s/block", parsed.Host)) + if err != nil { + fmt.Printf("Error querying block: %v\n", err) + time.Sleep(time.Second / 10) + continue + } + + var blockResponse struct { + Result struct { + Block struct { + Header struct { + Height string `json:"height"` + AppHash string `json:"app_hash"` + } `json:"header"` + } `json:"block"` + } `json:"result"` + } + + var buf bytes.Buffer + if err := json.NewDecoder(io.TeeReader(resp.Body, &buf)).Decode(&blockResponse); err != nil { + fmt.Printf("Error parsing response: %v: %s\n", err, buf.String()) + _ = resp.Body.Close() + time.Sleep(time.Second / 10) + continue + } + _ = resp.Body.Close() + + // Extract block height + height, err := strconv.ParseInt(blockResponse.Result.Block.Header.Height, 10, 64) + if err != nil { + fmt.Printf("Error parsing height: %v\n", err) + time.Sleep(time.Second / 10) + continue + } + + fmt.Printf("Current block: %d\n", height) + + // Check if this is the end of an epoch and we haven't attested to it yet + if height > 1 && height%int64(epochLength) == 0 && height > lastAttested { + fmt.Printf("End of epoch at height %d, submitting attestation\n", height) + + // Submit attestation with the block's app hash + appHash, err := hex.DecodeString(blockResponse.Result.Block.Header.AppHash) + if err != nil { + return fmt.Errorf("decoding app hash: %w", err) + } + err = submitAttestation(ctx, chainID, node, home, height, appHash, valAddr, verbose, senderKey, pv) + if err != nil { + return fmt.Errorf("submitting attestation: %w", err) + } + + lastAttested = height + } + + // Wait before next poll + time.Sleep(time.Second / 10) + } + } +} + +// formatCommandArgs formats command arguments for verbose output +func formatCommandArgs(args []string) string { + var result string + for i, arg := range args { + if i > 0 { + result += " " + } + // Add quotes if the argument contains spaces + if containsSpace(arg) { + result += "\"" + arg + "\"" + } else { + result += arg + } + } + return result +} + +// containsSpace checks if a string contains any space character +func containsSpace(s string) bool { + for _, c := range s { + if c == ' ' || c == '\t' || c == '\n' || c == '\r' { + return true + } + } + return false +} + +// broadcastTx executes a command to broadcast a transaction using the Cosmos SDK +func broadcastTx(ctx context.Context, chainID, nodeAddr string, msg proto.Message, privKey *secp256k1.PrivKey, verbose bool) (string, error) { + // Get validator address from private key + valAddr := sdk.ValAddress(privKey.PubKey().Address()) + + if verbose { + fmt.Printf("Broadcasting transaction for validator: %s\n", valAddr.String()) + } + + // Initialize the transaction config with the proper codec and sign modes + interfaceRegistry := codectypes.NewInterfaceRegistry() + authtypes.RegisterInterfaces(interfaceRegistry) + std.RegisterInterfaces(interfaceRegistry) + protoCodec := codec.NewProtoCodec(interfaceRegistry) + txConfig := authtx.NewTxConfig(protoCodec, authtx.DefaultSignModes) + + // Create proper transaction + txBuilder := txConfig.NewTxBuilder() + err := txBuilder.SetMsgs(msg) + if err != nil { + return "", fmt.Errorf("setting messages: %w", err) + } + + txBuilder.SetGasLimit(200000) + txBuilder.SetFeeAmount(sdk.NewCoins()) + txBuilder.SetMemo("") + // Get account info from node + clientCtx, err := createClientContext(nodeAddr, chainID, txConfig) + if err != nil { + return "", fmt.Errorf("creating client context: %w", err) + } + clientCtx = clientCtx.WithInterfaceRegistry(interfaceRegistry).WithCodec(protoCodec) + addr := sdk.AccAddress(privKey.PubKey().Address()) + accountRetriever := authtypes.AccountRetriever{} + account, err := accountRetriever.GetAccount(clientCtx, addr) + if err != nil { + return "", fmt.Errorf("getting account: %w", err) + } + fmt.Printf("+++ chainid: %s, GetAccountNumber: %d\n", chainID, account.GetAccountNumber()) + // Sign transaction using account sequence + accSeq := account.GetSequence() + signerData := authsigning.SignerData{ + Address: addr.String(), + ChainID: chainID, + AccountNumber: account.GetAccountNumber(), + Sequence: accSeq, + PubKey: privKey.PubKey(), + } + + // For SIGN_MODE_DIRECT, we need to set a nil signature first + // to generate the correct sign bytes + sigData := signing.SingleSignatureData{ + SignMode: signing.SignMode_SIGN_MODE_DIRECT, + Signature: nil, + } + sig := signing.SignatureV2{ + PubKey: privKey.PubKey(), + Data: &sigData, + Sequence: accSeq, + } + + err = txBuilder.SetSignatures(sig) + if err != nil { + return "", fmt.Errorf("setting nil signatures: %w", err) + } + + // Now get the bytes to sign and create the real signature + signBytes, err := authsigning.GetSignBytesAdapter( + ctx, + txConfig.SignModeHandler(), + signing.SignMode_SIGN_MODE_DIRECT, + signerData, + txBuilder.GetTx(), + ) + if err != nil { + return "", fmt.Errorf("getting sign bytes: %w", err) + } + + // Sign those bytes + signature, err := privKey.Sign(signBytes) + if err != nil { + return "", fmt.Errorf("signing bytes: %w", err) + } + + // Construct the SignatureV2 struct with the actual signature + sigData = signing.SingleSignatureData{ + SignMode: signing.SignMode_SIGN_MODE_DIRECT, + Signature: signature, + } + sig = signing.SignatureV2{ + PubKey: privKey.PubKey(), + Data: &sigData, + Sequence: accSeq, + } + + err = txBuilder.SetSignatures(sig) + if err != nil { + return "", fmt.Errorf("setting signatures: %w", err) + } + + txBytes, err := txConfig.TxEncoder()(txBuilder.GetTx()) + if err != nil { + return "", fmt.Errorf("encoding transaction: %w", err) + } + + // Set broadcast mode to sync + clientCtx = clientCtx.WithBroadcastMode("sync") + + // Broadcast the transaction using the cosmos-sdk library + resp, err := clientCtx.BroadcastTx(txBytes) + if err != nil { + return "", fmt.Errorf("broadcasting transaction: %w", err) + } + + // Check if the transaction was successful + if resp.Code != 0 { + return "", fmt.Errorf("transaction failed with code %d: %s", resp.Code, resp.RawLog) + } + + if verbose { + fmt.Printf("Transaction successful with hash: %s\n", resp.TxHash) + } + + return resp.TxHash, nil +} + +// createClientContext creates a client.Context with the necessary fields for broadcasting transactions +func createClientContext(nodeAddr, chainID string, txConfig client.TxConfig) (client.Context, error) { + // Create a CometRPC client + rpcClient, err := client.NewClientFromNode(nodeAddr) + if err != nil { + return client.Context{}, fmt.Errorf("creating RPC client: %w", err) + } + + // Create a client.Context with the necessary fields + clientCtx := client.Context{ + Client: rpcClient, + ChainID: chainID, + TxConfig: txConfig, + BroadcastMode: "sync", + Output: os.Stdout, + AccountRetriever: authtypes.AccountRetriever{}, + } + + return clientCtx, nil +} + +// createPrivateKeyFromMnemonic derives a private key from a mnemonic using the standard +// BIP44 HD path (m/44'/118'/0'/0/0) +func createPrivateKeyFromMnemonic(mnemonic string) (*secp256k1.PrivKey, error) { + // Create master key from mnemonic + derivedPriv, err := hd.Secp256k1.Derive()( + mnemonic, + "", + hd.CreateHDPath(118, 0, 0).String(), // Cosmos HD path + ) + if err != nil { + return nil, fmt.Errorf("failed to derive private key: %w", err) + } + return &secp256k1.PrivKey{Key: derivedPriv}, nil +} + +// submitAttestation creates and submits an attestation for a block using direct RPC +func submitAttestation( + ctx context.Context, + chainID, node, home string, + height int64, + appHash []byte, + valAddr sdk.ValAddress, + verbose bool, + senderKey *secp256k1.PrivKey, + pv *pvm.FilePV, +) error { + // Create the vote + vote := &cmtproto.Vote{ + Type: cmtproto.PrecommitType, + ValidatorAddress: pv.GetAddress(), + Height: height, + Round: 0, + BlockID: cmtproto.BlockID{Hash: appHash, PartSetHeader: cmtproto.PartSetHeader{Total: 1, Hash: appHash}}, + Timestamp: time.Now(), + } + var err error + err = pv.SignVote(chainID, vote) + if err != nil { + return fmt.Errorf("sign vote: %w", err) + } + + voteBytes, err := proto.Marshal(vote) + if err != nil { + return fmt.Errorf("marshal vote: %w", err) + } + + msg := networktypes.NewMsgAttest( + valAddr.String(), + height, + voteBytes, + ) + + txHash, err := broadcastTx(ctx, chainID, node, msg, senderKey, verbose) + if err != nil { + return fmt.Errorf("broadcast attest tx: %w", err) + } + + fmt.Printf("Attestation submitted with hash: %s\n", txHash) + return nil +} diff --git a/hack/download.sh b/hack/download.sh new file mode 100755 index 00000000..5f30eaa9 --- /dev/null +++ b/hack/download.sh @@ -0,0 +1,101 @@ +#!/bin/bash + + +HERMES_VERSION=${1:-"v1.13.1"} +GAIAD_VERSION=${2:-"v24.0.0"} +COSMOS_RELAYER_VERSION=${3:-"v2.6.0"} + +ARCH=$(uname -m) +OS=$(uname -s) + +case "$ARCH" in + "arm64") + ARCH_LABEL="aarch64" + ;; + "x86_64") + ARCH_LABEL="x86_64" + ;; + *) + echo "Unsupported architecture: $ARCH" + exit 1 + ;; +esac + +case "$OS" in + "Darwin") + OS_LABEL="apple-darwin" + ;; + "Linux") + OS_LABEL="unknown-linux-gnu" + ;; + *) + echo "Unsupported operating system: $OS" + exit 1 + ;; +esac + +HERMES_URL="https://github.com/informalsystems/hermes/releases/download/$HERMES_VERSION/hermes-$HERMES_VERSION-${ARCH_LABEL}-${OS_LABEL}.tar.gz" +GAIAD_URL="https://github.com/cosmos/gaia/releases/download/$GAIAD_VERSION/gaiad-$GAIAD_VERSION-darwin-$ARCH" + +# For Cosmos Relayer, we need to map the architecture differently +COSMOS_RELAYER_ARCH="amd64" +if [ "$ARCH" == "arm64" ]; then + COSMOS_RELAYER_ARCH="arm64" +fi + +COSMOS_RELAYER_OS="linux" +if [ "$OS" == "Darwin" ]; then + COSMOS_RELAYER_OS="darwin" +fi + +COSMOS_RELAYER_URL="https://github.com/cosmos/relayer/releases/download/$COSMOS_RELAYER_VERSION/Cosmos.Relayer_${COSMOS_RELAYER_VERSION#v}_${COSMOS_RELAYER_OS}_${COSMOS_RELAYER_ARCH}.tar.gz" + +if [ "$OS" == "Linux" ]; then + GAIAD_URL="https://github.com/cosmos/gaia/releases/download/$GAIAD_VERSION/gaiad-$GAIAD_VERSION-linux-amd64" +fi + +# Define output directories +DOWNLOAD_DIR="./downloads" +mkdir -p "$DOWNLOAD_DIR" + +# Function to download a file +download_file() { + local url=$1 + local output_path=$2 + + echo "Downloading: $url" + curl -L -o "$output_path" "$url" + if [ $? -ne 0 ]; then + echo "Failed to download $url" + exit 1 + fi +} + +# Download hermes +HERMES_ARCHIVE="$DOWNLOAD_DIR/hermes.tar.gz" +download_file "$HERMES_URL" "$HERMES_ARCHIVE" + +# Extract hermes if tar.gz +if [[ "$HERMES_ARCHIVE" == *.tar.gz ]]; then + echo "Extracting: $HERMES_ARCHIVE" + tar -xzvf "$HERMES_ARCHIVE" -C "$DOWNLOAD_DIR" +elif [[ "$HERMES_ARCHIVE" == *.zip ]]; then + echo "Extracting: $HERMES_ARCHIVE" + unzip "$HERMES_ARCHIVE" -d "$DOWNLOAD_DIR" +fi + +GAIAD_BINARY="$DOWNLOAD_DIR/gaiad" +download_file "$GAIAD_URL" "$GAIAD_BINARY" + +# Make gaiad binary executable +chmod +x "$GAIAD_BINARY" + +# Download Cosmos Relayer +COSMOS_RELAYER_ARCHIVE="$DOWNLOAD_DIR/cosmos-relayer.tar.gz" +download_file "$COSMOS_RELAYER_URL" "$COSMOS_RELAYER_ARCHIVE" + +# Extract Cosmos Relayer +echo "Extracting: $COSMOS_RELAYER_ARCHIVE" +tar -xzvf "$COSMOS_RELAYER_ARCHIVE" --strip-components=1 -C "$DOWNLOAD_DIR" + +echo "Hermes, Gaiad, and Cosmos Relayer downloaded successfully to $DOWNLOAD_DIR" diff --git a/hack/ibc-connection-hermes.sh b/hack/ibc-connection-hermes.sh new file mode 100755 index 00000000..5625e8aa --- /dev/null +++ b/hack/ibc-connection-hermes.sh @@ -0,0 +1,131 @@ +#!/bin/bash +set -e + +# Configuration variables +GAIA_CHAIN_ID="localnet-1" +WORDLED_CHAIN_ID="rollkitnet-1" + +GAIA_RPC="http://localhost:26654" +WORDLED_RPC="http://localhost:26657" + +GAIA_GRPC="http://localhost:9091" +WORDLED_GRPC="http://localhost:9090" + +GAIA_DENOM="stake" +WORDLED_DENOM="stake" + +RELAYER_WALLET="relayer" # Name of relayer account + +# Hardcoded mnemonic (must match initialization scripts) +RELAYER_MNEMONIC="reject camp lock magic dragon degree loop ignore quantum verify invest primary object afraid crane unveil parrot jelly rubber risk mirror globe torch category" + + +# Logging functions +log_info() { + echo "[INFO] $1" +} + +log_success() { + echo "[SUCCESS] $1" +} + +log_error() { + echo "[ERROR] $1" >&2 +} + +CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +HERMES_BIN="${HERMES_BIN:-$CURRENT_DIR/downloads/hermes}" + +# Verify Hermes is installed +if [ ! -f "$HERMES_BIN" ]; then + log_error "Hermes not found at $HERMES_BIN" + exit 1 +fi +log_success "Hermes installed" + +CONFIG_DIR="${CURRENT_DIR}/testnet/hermes" +rm -rf "$CONFIG_DIR" +mkdir -p "$CONFIG_DIR" + +# Generate configuration file (config.toml) +CONFIG_FILE="$CONFIG_DIR/config.toml" +log_info "Generating Hermes configuration at $CONFIG_FILE..." + +cat < "$CONFIG_FILE" +[global] +log_level = "trace" + +[mode] + [mode.clients] + enabled = true + refresh = true + misbehaviour = true + [mode.connections] + enabled = true + [mode.channels] + enabled = true + [mode.packets] + enabled = true + +[[chains]] +id = "$GAIA_CHAIN_ID" +rpc_addr = "$GAIA_RPC" +grpc_addr = "$GAIA_GRPC" +event_source = { mode = "pull", interval = "1s", max_retries = 4 } +store_prefix = "ibc" +account_prefix = "cosmos" +key_name = "$RELAYER_WALLET" +gas_price = { price = 3.5, denom = "stake" } +gas_multiplier = 1.4 +rpc_timeout = "10s" +trusting_period = "503h" +clock_drift = "10s" +key_store_folder = "$CONFIG_DIR/$GAIA_CHAIN_ID-keys" + +[[chains]] +id = "$WORDLED_CHAIN_ID" +rpc_addr = "$WORDLED_RPC" +grpc_addr = "$WORDLED_GRPC" +store_prefix = "ibc" +event_source = { mode = "pull", interval = "1s", max_retries = 4 } +account_prefix = "gm" +key_name = "$RELAYER_WALLET" +gas_price = { price = 0.025, denom = "stake" } +gas_multiplier = 1.4 +rpc_timeout = "10s" +trusting_period = "503h" +clock_drift = "10s" +key_store_folder = "$CONFIG_DIR/$WORDLED_CHAIN_ID-keys" +EOF + +log_success "Configuration file generated" + +# Import keys to relayer (Hermes) using mnemonic +TMP_MNEMONIC=$(mktemp) +echo "$RELAYER_MNEMONIC" > "$TMP_MNEMONIC" + +log_info "Importing key for $GAIA_CHAIN_ID..." +"$HERMES_BIN" --config "$CONFIG_FILE" keys add --chain "$GAIA_CHAIN_ID" --mnemonic-file "$TMP_MNEMONIC" +log_success "Key imported for $GAIA_CHAIN_ID" + +log_info "Importing key for $WORDLED_CHAIN_ID..." +"$HERMES_BIN" --config "$CONFIG_FILE" keys add --chain "$WORDLED_CHAIN_ID" --mnemonic-file "$TMP_MNEMONIC" +log_success "Key imported for $WORDLED_CHAIN_ID" + +rm "$TMP_MNEMONIC" + +# Show configured addresses (optional) +log_info "Showing configured addresses:" +"$HERMES_BIN" --config "$CONFIG_FILE" keys list --chain "$GAIA_CHAIN_ID" +"$HERMES_BIN" --config "$CONFIG_FILE" keys list --chain "$WORDLED_CHAIN_ID" + +# Create IBC channel between chains +log_info "Creating IBC channel between $GAIA_CHAIN_ID and $WORDLED_CHAIN_ID..." + "$HERMES_BIN" --config "$CONFIG_FILE" create channel --a-chain "$GAIA_CHAIN_ID" --a-port transfer \ + --b-chain "$WORDLED_CHAIN_ID" --b-port transfer \ + --new-client-connection --yes +log_success "IBC channel created" + +# Start Hermes +log_info "Starting Hermes..." +"$HERMES_BIN" --config "$CONFIG_FILE" start \ No newline at end of file diff --git a/hack/ibc-connection-rly.sh b/hack/ibc-connection-rly.sh new file mode 100755 index 00000000..910a46b2 --- /dev/null +++ b/hack/ibc-connection-rly.sh @@ -0,0 +1,133 @@ +#!/bin/bash +set -e + +# Configuration variables +GAIA_CHAIN_ID="localnet-1" +WORDLED_CHAIN_ID="rollkitnet-1" + +GAIA_RPC="http://localhost:26654" +WORDLED_RPC="http://localhost:26657" + +GAIA_GRPC="http://localhost:9091" +WORDLED_GRPC="http://localhost:9090" + +GAIA_DENOM="stake" +WORDLED_DENOM="stake" + +RELAYER_WALLET="relayer" # Name of relayer account + +# Hardcoded mnemonic (must match initialization scripts) +RELAYER_MNEMONIC="reject camp lock magic dragon degree loop ignore quantum verify invest primary object afraid crane unveil parrot jelly rubber risk mirror globe torch category" + +# Logging functions +log_info() { + echo "[INFO] $1" +} + +log_success() { + echo "[SUCCESS] $1" +} + +log_error() { + echo "[ERROR] $1" >&2 +} + +CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +RELAYER_BIN="${RELAYER_BIN:-$CURRENT_DIR/downloads/rly}" + + +# Verify Go IBC Relayer is installed +if ! command -v $RELAYER_BIN &> /dev/null; then + log_error "Go IBC Relayer not found. Please install it with: go install github.com/cosmos/relayer/v2@latest" + exit 1 +fi +log_success "Go IBC Relayer installed" + +CONFIG_DIR="${CURRENT_DIR}/testnet/go-relayer" +rm -rf "$CONFIG_DIR" +mkdir -p "$CONFIG_DIR" + +# Initialize the relayer configuration +log_info "Initializing Go IBC Relayer configuration..." +$RELAYER_BIN config init --home "$CONFIG_DIR" +log_success "Relayer configuration initialized" + +# Add chains to the relayer configuration +log_info "Adding $GAIA_CHAIN_ID chain to the relayer..." +cat < gaia-config.json +{ + "type": "cosmos", + "value": { + "key": "$RELAYER_WALLET", + "chain-id": "$GAIA_CHAIN_ID", + "rpc-addr": "$GAIA_RPC", + "grpc-addr": "$GAIA_GRPC", + "account-prefix": "cosmos", + "keyring-backend": "test", + "gas-adjustment": 2.0, + "gas-prices": "0.1$GAIA_DENOM", + "debug": true, + "timeout": "10s", + "output-format": "json", + "sign-mode": "direct", + "trusting-period": "504h" + } +} +EOF +$RELAYER_BIN chains add $GAIA_CHAIN_ID -f gaia-config.json --home "$CONFIG_DIR" +log_success "$GAIA_CHAIN_ID chain added" + +log_info "Adding $WORDLED_CHAIN_ID chain to the relayer..." +cat < wordled-config.json +{ + "type": "cosmos", + "value": { + "key": "$RELAYER_WALLET", + "chain-id": "$WORDLED_CHAIN_ID", + "rpc-addr": "$WORDLED_RPC", + "grpc-addr": "$WORDLED_GRPC", + "account-prefix": "gm", + "keyring-backend": "test", + "gas-adjustment": 2.0, + "gas-prices": "0.1$WORDLED_DENOM", + "debug": true, + "timeout": "10s", + "output-format": "json", + "sign-mode": "direct", + "trusting-period": "504h" + } +} +EOF +$RELAYER_BIN chains add $WORDLED_CHAIN_ID -f wordled-config.json --home "$CONFIG_DIR" +log_success "$WORDLED_CHAIN_ID chain added" + +# Import keys to relayer using mnemonic +log_info "Importing key for $GAIA_CHAIN_ID..." +$RELAYER_BIN keys restore $GAIA_CHAIN_ID $RELAYER_WALLET "$RELAYER_MNEMONIC" --home "$CONFIG_DIR" +log_success "Key imported for $GAIA_CHAIN_ID" + +log_info "Importing key for $WORDLED_CHAIN_ID..." +$RELAYER_BIN keys restore $WORDLED_CHAIN_ID $RELAYER_WALLET "$RELAYER_MNEMONIC" --home "$CONFIG_DIR" +log_success "Key imported for $WORDLED_CHAIN_ID" +# Clean up config files +rm gaia-config.json wordled-config.json + +# Show configured addresses (optional) +log_info "Showing configured addresses:" +$RELAYER_BIN keys list $GAIA_CHAIN_ID --home "$CONFIG_DIR" +$RELAYER_BIN keys list $WORDLED_CHAIN_ID --home "$CONFIG_DIR" + +# Create a path between the chains +PATH_NAME="gaia-rollkit" +log_info "Creating path $PATH_NAME between $GAIA_CHAIN_ID and $WORDLED_CHAIN_ID..." +$RELAYER_BIN paths new $GAIA_CHAIN_ID $WORDLED_CHAIN_ID $PATH_NAME --home "$CONFIG_DIR" +log_success "Path created" + +# Create IBC channel between chains +log_info "Creating IBC connection and channel between $GAIA_CHAIN_ID and $WORDLED_CHAIN_ID..." +$RELAYER_BIN tx link $PATH_NAME --home "$CONFIG_DIR" --src-port transfer --dst-port transfer --order unordered --log-level=DEBUG --debug +log_success "IBC connection and channel created" + +# Start the relayer +log_info "Starting Go IBC Relayer..." +$RELAYER_BIN start $PATH_NAME --home "$CONFIG_DIR" diff --git a/hack/ics20-token-transfer.sh b/hack/ics20-token-transfer.sh new file mode 100755 index 00000000..b08b0ca7 --- /dev/null +++ b/hack/ics20-token-transfer.sh @@ -0,0 +1,166 @@ +#!/bin/bash +set -e + +# Configuration variables (matching ibc-connection-hermes.sh) +GAIA_CHAIN_ID="localnet-1" +ROLLKIT_CHAIN_ID="rollkitnet-1" + +GAIA_RPC="http://localhost:26654" +ROLLKIT_RPC="http://localhost:26657" + +GAIA_DENOM="stake" +ROLLKIT_DENOM="stake" + +GAIA_KEY_NAME="bob" +ROLLKIT_KEY_NAME="carl" + +# IBC channel information +CHANNEL_ID="" +TRANSFER_PORT="transfer" + +# Logging functions +log_info() { + echo "[INFO] $1" +} + +log_success() { + echo "[SUCCESS] $1" +} + +log_error() { + echo "[ERROR] $1" >&2 +} + +CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +GAIAD_BIN="${GAIAD_BIN:-$CURRENT_DIR/downloads/gaiad}" +GMD_BIN="gmd" +ROLLKIT_HOME="${1:-"${CURRENT_DIR}/testnet/gm"}" + +# Verify binaries are installed +if [ ! -f "$GAIAD_BIN" ]; then + log_error "Gaiad not found at $GAIAD_BIN" + exit 1 +fi +log_success "Gaiad installed" + +if ! command -v $GMD_BIN &> /dev/null; then + log_error "gmd binary not found in PATH" + exit 1 +fi +log_success "gmd installed" + +# Get validator addresses +log_info "Getting user addresses..." +USER_ADDRESS_GAIA=$($GAIAD_BIN keys show $GAIA_KEY_NAME -a --keyring-backend test --home "$CURRENT_DIR/testnet/gaia") +USER_ADDRESS_ROLLKIT=$($GMD_BIN keys show $ROLLKIT_KEY_NAME -a --keyring-backend test --home "$ROLLKIT_HOME") + +log_info "Gaia user address: $USER_ADDRESS_GAIA" +log_info "Rollkit user address: $USER_ADDRESS_ROLLKIT" + +# Check initial balances +log_info "Checking initial balances..." +log_info "Gaia $GAIA_KEY_NAME balance:" +$GAIAD_BIN q bank balances $USER_ADDRESS_GAIA --node $GAIA_RPC --home "$CURRENT_DIR/testnet/gaia" + +log_info "Rollkit $ROLLKIT_KEY_NAME balance:" +$GMD_BIN q bank balances $USER_ADDRESS_ROLLKIT --node $ROLLKIT_RPC --home "$ROLLKIT_HOME" + +# Get channel ID +log_info "Getting IBC channel ID..." +CHANNEL_INFO=$($GAIAD_BIN q ibc channel channels --node $GAIA_RPC --home "$CURRENT_DIR/testnet/gaia" -o json) +CHANNEL_ID=$(echo $CHANNEL_INFO | jq -r '.channels[0].channel_id') + +if [ -z "$CHANNEL_ID" ] || [ "$CHANNEL_ID" == "null" ]; then + log_error "Failed to get channel ID. Make sure IBC connection is established." + exit 1 +fi + +log_info "Using IBC channel: $CHANNEL_ID" + +# Transfer Rollkit tokens to Gaia +ROLLKIT_TRANSFER_AMOUNT="101" + +log_info "Transferring ${ROLLKIT_TRANSFER_AMOUNT}${ROLLKIT_DENOM} from Rollkit to Gaia..." +TX_HASH=$($GMD_BIN tx ibc-transfer transfer $TRANSFER_PORT $CHANNEL_ID $USER_ADDRESS_GAIA ${ROLLKIT_TRANSFER_AMOUNT}${ROLLKIT_DENOM} \ + --from ${ROLLKIT_KEY_NAME} \ + --chain-id $ROLLKIT_CHAIN_ID \ + --node $ROLLKIT_RPC \ + --keyring-backend test \ + --home "$ROLLKIT_HOME" \ + --gas auto \ + --gas-adjustment 1.4 \ + --gas-prices 1stake \ + --yes -o json | jq -r '.txhash') + +log_info "Querying gmd tx... $TX_HASH" +for i in {1..10}; do + if ! tx_result=$($GMD_BIN q tx --type=hash "$TX_HASH" -o json --home "$ROLLKIT_HOME" 2>/dev/null); then + sleep 1 + continue + fi +done +if [ "$(echo "$tx_result" | jq -r '.code')" != "0" ]; then + log_error "Transaction failed : $tx_result" + exit 1 +fi + + +# Check balances after Rollkit to Gaia transfer +log_info "Checking balances after Rollkit to Gaia transfer..." +log_info "Gaia user balance (should show IBC tokens):" +$GAIAD_BIN q bank balances $USER_ADDRESS_GAIA --node $GAIA_RPC --home "$CURRENT_DIR/testnet/gaia" + +log_info "Rollkit user balance:" +$GMD_BIN q bank balances $USER_ADDRESS_ROLLKIT --node $ROLLKIT_RPC --home "$ROLLKIT_HOME" + +# Transfer tokens from Gaia to Rollkit +TRANSFER_AMOUNT="102" +log_info "Transferring $TRANSFER_AMOUNT$GAIA_DENOM from Gaia to Rollkit..." +TX_HASH=$($GAIAD_BIN tx ibc-transfer transfer $TRANSFER_PORT $CHANNEL_ID $USER_ADDRESS_ROLLKIT ${TRANSFER_AMOUNT}${GAIA_DENOM} \ + --from ${GAIA_KEY_NAME} \ + --chain-id $GAIA_CHAIN_ID \ + --node $GAIA_RPC \ + --keyring-backend test \ + --home "$CURRENT_DIR/testnet/gaia" \ + --gas auto \ + --gas-adjustment 1.4 \ + --gas-prices 1stake \ + --yes -o json | jq -r '.txhash') + +log_info "Querying gaia tx... $TX_HASH" +for i in {1..20}; do + if ! tx_result=$($GAIAD_BIN q tx --type=hash "$TX_HASH" -o json --node $GAIA_RPC 2>/dev/null); then + echo "$tx_result" + sleep 1 + continue + fi +done +if [ "$(echo "$tx_result" | jq -r '.code')" != "0" ]; then + log_error "Transaction failed : $tx_result" + exit 1 +fi + +# Check balances after transfer to Rollkit +log_info "Checking balances after transfer to Rollkit..." +log_info "Gaia user balance:" +$GAIAD_BIN q bank balances $USER_ADDRESS_GAIA --node $GAIA_RPC --home "$CURRENT_DIR/testnet/gaia" + +log_info "Rollkit user balance (should show IBC tokens):" +ibc_tokens_visible=false + +for i in {1..${20}}; do + $GMD_BIN q bank balances $USER_ADDRESS_ROLLKIT --node $ROLLKIT_RPC --home "$ROLLKIT_HOME" + if $GMD_BIN q bank balances $USER_ADDRESS_ROLLKIT --node $ROLLKIT_RPC --home "$ROLLKIT_HOME" | grep -q "ibc"; then + log_success "IBC tokens are now visible in Rollkit balance" + ibc_tokens_visible=true + break + fi + sleep 1 +done + +if [ "$ibc_tokens_visible" = false ]; then + log_error "IBC tokens did not become visible within time frame" + exit 1 +fi + +log_success "ICS20 token transfer test completed!" diff --git a/hack/init-gaia.sh b/hack/init-gaia.sh new file mode 100755 index 00000000..e6fcc5ae --- /dev/null +++ b/hack/init-gaia.sh @@ -0,0 +1,98 @@ +#!/bin/bash + +# Function for formatted logging +log() { + local color=$1 + local emoji=$2 + local message=$3 + shift 3 + printf "\e[${color}m${emoji} [$(date '+%T')] ${message}\e[0m\n" "$@" +} + +# Hardcoded mnemonic for validator account (igual que en Wordled) +VALIDATOR_MNEMONIC="abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art" +BOB_MNEMONIC="rack argue disorder flame appear broom smile effort one rubber buffalo suspect tool devote zebra between inhale trigger brief possible parrot nation expose place" +RELAYER_MNEMONIC="reject camp lock magic dragon degree loop ignore quantum verify invest primary object afraid crane unveil parrot jelly rubber risk mirror globe torch category" + +CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +GAIA_HOME=${1:-"${CURRENT_DIR}/testnet/gaia"} +GAIAD_BIN=${2:-"${CURRENT_DIR}/downloads/gaiad"} + +# Kill existing Gaia processes +log "31" "💀" "Checking for existing Gaia processes..." +GAIA_PID=$(pgrep gaiad) +if [ -n "$GAIA_PID" ]; then + log "31" "🔪" "Killing existing Gaia process (PID: $GAIA_PID)..." + kill -9 "$GAIA_PID" +else + log "32" "👌" "No existing Gaia process found." +fi + +# Clean previous configurations +log "32" "🔥" "Cleaning previous Gaia configurations..." +rm -rf "$GAIA_HOME" + +# 1. Initialize Gaia chain +log "36" "🆕" "Initializing Gaia chain..." +"$GAIAD_BIN" init my-node --chain-id localnet-1 --home "$GAIA_HOME" + +# 2. Create/Recover validator account usando la mnemónica fija +log "35" "👤" "Generating validator account from mnemonic..." +echo "$VALIDATOR_MNEMONIC" | "$GAIAD_BIN" keys add validator \ + --keyring-backend test \ + --home "$GAIA_HOME" \ + --recover > /dev/null 2>&1 +echo "$BOB_MNEMONIC" | "$GAIAD_BIN" keys add bob \ + --keyring-backend test \ + --home "$GAIA_HOME" \ + --recover > /dev/null 2>&1 +echo "$RELAYER_MNEMONIC" | "$GAIAD_BIN" keys add relayer \ + --keyring-backend test \ + --home "$GAIA_HOME" \ + --recover > /dev/null 2>&1 + +# 3. Add account to genesis +log "34" "📝" "Adding account to genesis..." +"$GAIAD_BIN" genesis add-genesis-account validator 10000000000000000stake --keyring-backend test --home "$GAIA_HOME" +"$GAIAD_BIN" genesis add-genesis-account bob 10000000000000000stake --keyring-backend test --home "$GAIA_HOME" +"$GAIAD_BIN" genesis add-genesis-account relayer 10000000000000000stake --keyring-backend test --home "$GAIA_HOME" + +# 4. Generate gentx +log "33" "📜" "Creating validator transaction..." +"$GAIAD_BIN" genesis gentx validator 1000000000stake \ + --chain-id localnet-1 \ + --keyring-backend test \ + --home "$GAIA_HOME" + +# 5. Collect gentxs +log "32" "📦" "Collecting genesis transactions..." +"$GAIAD_BIN" genesis collect-gentxs --home "$GAIA_HOME" + +# 6. Configure minimum gas prices +log "36" "⛽" "Setting minimum gas prices..." +sed -i.bak -E 's#minimum-gas-prices = ""#minimum-gas-prices = "0stake"#g' "$GAIA_HOME/config/app.toml" + +# 7. Modify consensus timeouts +log "35" "⏱️" "Adjusting consensus timeouts..." +sed -i.bak -E 's/timeout_commit = "5s"/timeout_commit = "1s"/g' "$GAIA_HOME/config/config.toml" +sed -i.bak -E 's/timeout_propose = "3s"/timeout_propose = "1s"/g' "$GAIA_HOME/config/config.toml" + +# 8. Start Gaia chain +log "34" "🚀" "Starting Gaia node..." +"$GAIAD_BIN" start --home "$GAIA_HOME" --minimum-gas-prices "0stake" --rpc.laddr tcp://0.0.0.0:26654 --rpc.pprof_laddr localhost:6061 --p2p.laddr tcp://0.0.0.0:26653 --grpc.address 0.0.0.0:9091 | tee "$GAIA_HOME/gaia.log" & +GAIA_PID=$! + +# Wait for initialization +log "33" "⏳" "Waiting for Gaia initialization (port 26654)..." +while ! nc -z localhost 26654; do + sleep 1 +done +log "32" "✅" "Gaia chain running successfully!" + +# Show recent logs +log "36" "📄" "Last lines of Gaia log:" +tail -n 5 "$GAIA_HOME/gaia.log" + +# Keep script alive +log "35" "👀" "Monitoring Gaia chain activity..." +wait $GAIA_PID \ No newline at end of file diff --git a/hack/run_gmd.sh b/hack/run_gmd.sh new file mode 100755 index 00000000..de0d7a1f --- /dev/null +++ b/hack/run_gmd.sh @@ -0,0 +1,147 @@ +#!/bin/bash +set -x + +# Function for cleanup on script interruption +cleanup() { + log "31" "🛑" "Cleaning up processes..." + if [ -n "$DA_PID" ]; then + kill -9 "$DA_PID" 2>/dev/null || true + fi + if [ -n "$ROLLKIT_PID" ]; then + kill -9 "$ROLLKIT_PID" 2>/dev/null || true + fi + exit 0 +} + +trap cleanup INT TERM + +# Function for formatted logging +log() { + local color=$1 + local emoji=$2 + local message=$3 + shift 3 + printf "\e[${color}m${emoji} [$(date '+%T')] ${message}\e[0m\n" "$@" +} + + +# Define paths +CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROLLKIT_BIN="gmd" +ROLLKIT_HOME="${1:-"${CURRENT_DIR}/testnet/gm"}" +LOCAL_DA_PATH="${2:-"../../rollkit/build/local-da"}" +CHAIN_ID="${3:-"rollkitnet-1"}" + +# Clean previous configurations +log "32" "🔥" "Cleaning previous rollkit configurations..." +rm -rf "$ROLLKIT_HOME" + +"$ROLLKIT_BIN" init my-rollkit-node --chain-id "$CHAIN_ID" --home "$ROLLKIT_HOME" + +# Hardcoded mnemonic for validator account (igual que en Wordled) +VALIDATOR_MNEMONIC="abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art" +#ATTESTER_MNEMONIC="tennis sponsor brick almost coyote soup rib wisdom warm bean onion tray devote pretty crime grid rough boil wear december travel inch work note" +RELAYER_MNEMONIC="reject camp lock magic dragon degree loop ignore quantum verify invest primary object afraid crane unveil parrot jelly rubber risk mirror globe torch category" +USER_MNEMONIC="sport head real antique sad expect ignore feature claim manual heavy mouse coil rebuild police flag robust picture milk symptom suffer chuckle worry virus" + + +echo "$VALIDATOR_MNEMONIC" | "$ROLLKIT_BIN" keys add validator \ + --keyring-backend test \ + --home "$ROLLKIT_HOME" \ + --recover > /dev/null 2>&1 + +#echo "$ATTESTER_MNEMONIC" | "$ROLLKIT_BIN" keys add attester \ +# --keyring-backend test \ +# --home "$ROLLKIT_HOME" \ +# --recover > /dev/null 2>&1 + +echo "$RELAYER_MNEMONIC" | "$ROLLKIT_BIN" keys add relayer \ + --keyring-backend test \ + --home "$ROLLKIT_HOME" \ + --recover > /dev/null 2>&1 + +echo "$USER_MNEMONIC" | "$ROLLKIT_BIN" keys add carl \ + --keyring-backend test \ + --home "$ROLLKIT_HOME" \ + --recover > /dev/null 2>&1 + +"$ROLLKIT_BIN" genesis add-genesis-account validator "10000000000000000stake" --keyring-backend test --home "$ROLLKIT_HOME" +#"$ROLLKIT_BIN" genesis add-genesis-account attester "10000000000000000stake" --keyring-backend test --home "$ROLLKIT_HOME" +"$ROLLKIT_BIN" genesis add-genesis-account relayer "10000000000000000stake" --keyring-backend test --home "$ROLLKIT_HOME" +"$ROLLKIT_BIN" genesis add-genesis-account carl "10000000000000000stake" --keyring-backend test --home "$ROLLKIT_HOME" + + +log "33" "📜" "Creating validator transaction..." +"$ROLLKIT_BIN" genesis gentx validator 1000000000stake \ + --chain-id "$CHAIN_ID" \ + --keyring-backend test \ + --home "$ROLLKIT_HOME" + +# 5. Collect gentxs +log "32" "📦" "Collecting genesis transactions..." +"$ROLLKIT_BIN" genesis collect-gentxs --home "$ROLLKIT_HOME" + +# Set validator in consensus block +log "32" "🔄" "Setting validator in consensus block..." +# Extract validator address and pubkey from validator key file, then modify genesis file to set the validator +jq -r '.address as $addr | .pub_key | { + address: $addr, + pub_key: { type: "tendermint/PubKeyEd25519", value: .value }, + power: "1000", + name: "Rollkit Sequencer" +}' "$ROLLKIT_HOME/config/priv_validator_key.json" | \ +jq --slurpfile genesis "$ROLLKIT_HOME/config/genesis.json" \ + '. as $validator | ($genesis[0] | .consensus.validators += [$validator])' > \ + "$ROLLKIT_HOME/config/tmp_genesis.json" && \ +mv "$ROLLKIT_HOME/config/tmp_genesis.json" "$ROLLKIT_HOME/config/genesis.json" + +# 6. Configure minimum gas prices +log "33" "⛽" "Setting minimum gas prices..." +sed -i.bak -E 's#minimum-gas-prices = ""#minimum-gas-prices = "0stake"#g' "$ROLLKIT_HOME/config/app.toml" + +# 7. Modify consensus timeouts +log "34" "⏱️" "Adjusting consensus timeouts..." +sed -i.bak -E 's/timeout_commit = "5s"/timeout_commit = "1s"/g' "$ROLLKIT_HOME/config/config.toml" +sed -i.bak -E 's/timeout_propose = "3s"/timeout_propose = "1s"/g' "$ROLLKIT_HOME/config/config.toml" + +# enable api +sed -i.bak 's#enable = false#enable = true#g' "$ROLLKIT_HOME/config/app.toml" + + + +# Build and start local DA +echo "Building and starting local DA..." +# --- Kill previous local-da process if running --- +echo "Checking for existing local-da process on port 7980..." +# Find PID using lsof on the DA port (adjust if your DA uses a different port) +DA_PORT=7980 +EXISTING_PID=$(lsof -ti tcp:${DA_PORT}) + +if [ -n "$EXISTING_PID" ]; then + echo "Found existing processes on port $DA_PORT with PIDs: $EXISTING_PID. Killing..." + # Kill the process(es) + kill -9 $EXISTING_PID + # Allow a moment for the OS to release the port + sleep 2 +else + echo "No existing process found on port $DA_PORT." +fi +# --- End Kill previous local-da process --- + + +if [ ! -f "$LOCAL_DA_PATH" ]; then + echo "Error: local-da binary not found at $LOCAL_DA_PATH" + exit 1 +else + echo "Starting local DA in background..." + $LOCAL_DA_PATH & + DA_PID=$! + echo "Local DA started with PID $DA_PID" + sleep 2 # Give DA a moment to start +fi + +log "35" "🚀" "Starting ROLLKIT node..." +"$ROLLKIT_BIN" start --home "$ROLLKIT_HOME" --rollkit.node.aggregator --minimum-gas-prices "0stake" --rollkit.node.lazy_block_interval=150ms --rollkit.node.block_time=100ms --rollkit.da.block_time=500ms --pruning=nothing --rollkit.network.soft-confirmation --log_level=debug & +ROLLKIT_PID=$! +log "36" "✅" "ROLLKIT chain running successfully!" +wait $ROLLKIT_PID diff --git a/pkg/adapter/adapter.go b/pkg/adapter/adapter.go index 6f73ef29..6c6644c4 100644 --- a/pkg/adapter/adapter.go +++ b/pkg/adapter/adapter.go @@ -277,10 +277,10 @@ func (a *Adapter) InitChain(ctx context.Context, genesisTime time.Time, initialH nValSet := cmttypes.NewValidatorSet(vals) - if len(nValSet.Validators) != 1 { - err := fmt.Errorf("expected exactly one validator") - return nil, 0, err - } + //if n := len(nValSet.Validators); n != 1 { + // err := fmt.Errorf("expected exactly one validator but got %d", n) + // return nil, 0, err + //} s.Validators = cmttypes.NewValidatorSet(nValSet.Validators) s.NextValidators = cmttypes.NewValidatorSet(nValSet.Validators).CopyIncrementProposerPriority(1) diff --git a/server/start.go b/server/start.go index 457b54a0..a72aa926 100644 --- a/server/start.go +++ b/server/start.go @@ -681,6 +681,9 @@ func loadRollkitMigrationGenesis(rootDir string) (*rollkitMigrationGenesis, erro // This is used for normal startup scenarios where a full cometbft genesis document // is available and contains all the necessary information. func createRollkitGenesisFromCometBFT(cmtGenDoc *cmttypes.GenesisDoc) *genesis.Genesis { + if len(cmtGenDoc.Validators) == 0 { + panic("no .validators in genesis") + } rollkitGenesis := genesis.NewGenesis( cmtGenDoc.ChainID, uint64(cmtGenDoc.InitialHeight), From 9dd1557d80d51bb119a60dd9db413badbf6fec05 Mon Sep 17 00:00:00 2001 From: Alex Peters Date: Fri, 4 Jul 2025 14:06:23 +0200 Subject: [PATCH 06/17] Commit --- pkg/rpc/core/blocks.go | 70 ++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/pkg/rpc/core/blocks.go b/pkg/rpc/core/blocks.go index 0933b38f..16c6540f 100644 --- a/pkg/rpc/core/blocks.go +++ b/pkg/rpc/core/blocks.go @@ -224,9 +224,10 @@ func Commit(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultCommit, erro return nil, fmt.Errorf("failed to check soft confirmation status: %w", err) } - if !isSoftConfirmed { - return nil, fmt.Errorf("commit for height %d does not exist (block not soft confirmed)", height) - } + //if !isSoftConfirmed { + // return nil, fmt.Errorf("commit for height %d does not exist (block not soft confirmed)", height) + //} + _ = isSoftConfirmed header, rollkitData, err := env.Adapter.RollkitStore.GetBlockData(ctx.Context(), height) if err != nil { @@ -246,6 +247,7 @@ func Commit(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultCommit, erro // Update the commit's BlockID to match the final ABCI block hash block.LastCommit.BlockID.Hash = block.Header.Hash() + block.LastCommit.BlockID.PartSetHeader.Hash = block.LastCommit.BlockID.Hash return &ctypes.ResultCommit{ SignedHeader: cmttypes.SignedHeader{ @@ -439,48 +441,48 @@ func checkSoftConfirmation(ctx context.Context, height uint64) (bool, *networkty // buildCommitFromAttestations constructs a commit with real signatures from attestations func buildCommitFromAttestations(ctx context.Context, height uint64, attestationData *networktypes.QueryAttestationBitmapResponse) (*cmttypes.Commit, error) { - // Get the attestation bitmap - bitmap := attestationData.Bitmap.Bitmap - if bitmap == nil { - return nil, fmt.Errorf("no attestation bitmap found for height %d", height) - } - // Get validators from genesis (since we know there's exactly one validator) genesisValidators := env.Adapter.AppGenesis.Consensus.Validators if len(genesisValidators) == 0 { return nil, fmt.Errorf("no validators found in genesis") } - votes := make([]cmttypes.CommitSig, len(genesisValidators)) - // Iterate only through the actual validators (not all bits in bitmap) - for i, genesisValidator := range genesisValidators { - // Check if this validator voted (bit is set in bitmap) - if i < len(bitmap)*8 && (bitmap[i/8]&(1<<(i%8))) != 0 { - // This validator voted, get their signature - vote := cmttypes.CommitSig{ - BlockIDFlag: cmttypes.BlockIDFlagCommit, - ValidatorAddress: genesisValidator.Address, // Use real validator address - Timestamp: time.Now(), // Should be actual timestamp from attestation - Signature: nil, // We'll get this from the query below - } - - // Try to get the real signature using the validator's address - validatorAddr := genesisValidator.Address.String() - signature, err := getValidatorSignatureFromQuery(ctx, int64(height), validatorAddr) - if err == nil { - vote.Signature = signature - } + if attestationData != nil { + // Get the attestation bitmap + bitmap := attestationData.Bitmap.Bitmap + if bitmap == nil { + return nil, fmt.Errorf("no attestation bitmap found for height %d", height) + } - votes[i] = vote - } else { - // Validator didn't vote, add absent vote - votes[i] = cmttypes.CommitSig{ - BlockIDFlag: cmttypes.BlockIDFlagAbsent, + // Iterate only through the actual validators (not all bits in bitmap) + for i, genesisValidator := range genesisValidators { + // Check if this validator voted (bit is set in bitmap) + if i < len(bitmap)*8 && (bitmap[i/8]&(1<<(i%8))) != 0 { + // This validator voted, get their signature + vote := cmttypes.CommitSig{ + BlockIDFlag: cmttypes.BlockIDFlagCommit, + ValidatorAddress: genesisValidator.Address, // Use real validator address + Timestamp: time.Now(), // Should be actual timestamp from attestation + Signature: nil, // We'll get this from the query below + } + + // Try to get the real signature using the validator's address + validatorAddr := genesisValidator.Address.String() + signature, err := getValidatorSignatureFromQuery(ctx, int64(height), validatorAddr) + if err == nil { + vote.Signature = signature + } + + votes[i] = vote + } else { + // Validator didn't vote, add absent vote + votes[i] = cmttypes.CommitSig{ + BlockIDFlag: cmttypes.BlockIDFlagAbsent, + } } } } - commit := &cmttypes.Commit{ Height: int64(height), Round: 0, // Default round From e94c3904fa042a660a3bdbd669035c6343267367 Mon Sep 17 00:00:00 2001 From: Alex Peters Date: Fri, 4 Jul 2025 17:51:39 +0200 Subject: [PATCH 07/17] Alwyas fill commit --- pkg/rpc/core/blocks.go | 55 +++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/pkg/rpc/core/blocks.go b/pkg/rpc/core/blocks.go index 16c6540f..71c57b2e 100644 --- a/pkg/rpc/core/blocks.go +++ b/pkg/rpc/core/blocks.go @@ -448,41 +448,40 @@ func buildCommitFromAttestations(ctx context.Context, height uint64, attestation } votes := make([]cmttypes.CommitSig, len(genesisValidators)) + var bitmap []byte if attestationData != nil { - // Get the attestation bitmap - bitmap := attestationData.Bitmap.Bitmap - if bitmap == nil { + if bitmap = attestationData.Bitmap.Bitmap; bitmap == nil { return nil, fmt.Errorf("no attestation bitmap found for height %d", height) } + } + // Iterate only through the actual validators (not all bits in bitmap) + for i, genesisValidator := range genesisValidators { + // Check if this validator voted (bit is set in bitmap) + if attestationData != nil && i < len(bitmap)*8 && (bitmap[i/8]&(1<<(i%8))) != 0 { + // This validator voted, get their signature + vote := cmttypes.CommitSig{ + BlockIDFlag: cmttypes.BlockIDFlagCommit, + ValidatorAddress: genesisValidator.Address, // Use real validator address + Timestamp: time.Now(), // Should be actual timestamp from attestation + Signature: nil, // We'll get this from the query below + } - // Iterate only through the actual validators (not all bits in bitmap) - for i, genesisValidator := range genesisValidators { - // Check if this validator voted (bit is set in bitmap) - if i < len(bitmap)*8 && (bitmap[i/8]&(1<<(i%8))) != 0 { - // This validator voted, get their signature - vote := cmttypes.CommitSig{ - BlockIDFlag: cmttypes.BlockIDFlagCommit, - ValidatorAddress: genesisValidator.Address, // Use real validator address - Timestamp: time.Now(), // Should be actual timestamp from attestation - Signature: nil, // We'll get this from the query below - } - - // Try to get the real signature using the validator's address - validatorAddr := genesisValidator.Address.String() - signature, err := getValidatorSignatureFromQuery(ctx, int64(height), validatorAddr) - if err == nil { - vote.Signature = signature - } - - votes[i] = vote - } else { - // Validator didn't vote, add absent vote - votes[i] = cmttypes.CommitSig{ - BlockIDFlag: cmttypes.BlockIDFlagAbsent, - } + // Try to get the real signature using the validator's address + validatorAddr := genesisValidator.Address.String() + signature, err := getValidatorSignatureFromQuery(ctx, int64(height), validatorAddr) + if err == nil { + vote.Signature = signature + } + + votes[i] = vote + } else { + // Validator didn't vote, add absent vote + votes[i] = cmttypes.CommitSig{ + BlockIDFlag: cmttypes.BlockIDFlagAbsent, } } } + commit := &cmttypes.Commit{ Height: int64(height), Round: 0, // Default round From 552305bdd0ae1e57d8b46d2c0b8590606bee6cb8 Mon Sep 17 00:00:00 2001 From: Randy Grok Date: Mon, 7 Jul 2025 17:18:09 +0200 Subject: [PATCH 08/17] use signatures --- pkg/rpc/core/blocks.go | 58 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/pkg/rpc/core/blocks.go b/pkg/rpc/core/blocks.go index 71c57b2e..00ce0310 100644 --- a/pkg/rpc/core/blocks.go +++ b/pkg/rpc/core/blocks.go @@ -134,9 +134,32 @@ func Block(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlock, error) return nil, err } - lastCommit, err := getLastCommit(ctx.Context(), heightValue) - if err != nil { - return nil, fmt.Errorf("failed to get last commit for block %d: %w", heightValue, err) + // Try to get attestation-based commit for the previous block first, fall back to getLastCommit if not available + var lastCommit *cmttypes.Commit + if heightValue > 0 { + previousHeight := heightValue - 1 + isSoftConfirmed, softConfirmationData, err := checkSoftConfirmation(ctx.Context(), previousHeight) + if err == nil && isSoftConfirmed && softConfirmationData != nil { + // Build commit with real signatures from attestations for the previous block + lastCommit, err = buildCommitFromAttestations(ctx.Context(), previousHeight, softConfirmationData) + if err != nil { + // Fall back to regular lastCommit if attestation-based commit fails + env.Logger.Debug("failed to build commit from attestations, falling back to getLastCommit", "height", previousHeight, "error", err) + lastCommit, err = getLastCommit(ctx.Context(), heightValue) + if err != nil { + return nil, fmt.Errorf("failed to get last commit for block %d: %w", heightValue, err) + } + } + } else { + // Use regular lastCommit + lastCommit, err = getLastCommit(ctx.Context(), heightValue) + if err != nil { + return nil, fmt.Errorf("failed to get last commit for block %d: %w", heightValue, err) + } + } + } else { + // For genesis block (height 0), there's no previous block, so no lastCommit + lastCommit = nil } block, err := cometcompat.ToABCIBlock(header, data, lastCommit) @@ -187,9 +210,32 @@ func BlockByHash(ctx *rpctypes.Context, hash []byte) (*ctypes.ResultBlock, error return nil, err } - lastCommit, err := getLastCommit(ctx.Context(), header.Height()) - if err != nil { - return nil, fmt.Errorf("failed to get last commit for block %d: %w", header.Height(), err) + // Try to get attestation-based commit for the previous block first, fall back to getLastCommit if not available + var lastCommit *cmttypes.Commit + if header.Height() > 0 { + previousHeight := header.Height() - 1 + isSoftConfirmed, softConfirmationData, err := checkSoftConfirmation(ctx.Context(), previousHeight) + if err == nil && isSoftConfirmed && softConfirmationData != nil { + // Build commit with real signatures from attestations for the previous block + lastCommit, err = buildCommitFromAttestations(ctx.Context(), previousHeight, softConfirmationData) + if err != nil { + // Fall back to regular lastCommit if attestation-based commit fails + env.Logger.Debug("failed to build commit from attestations, falling back to getLastCommit", "height", previousHeight, "error", err) + lastCommit, err = getLastCommit(ctx.Context(), header.Height()) + if err != nil { + return nil, fmt.Errorf("failed to get last commit for block %d: %w", header.Height(), err) + } + } + } else { + // Use regular lastCommit + lastCommit, err = getLastCommit(ctx.Context(), header.Height()) + if err != nil { + return nil, fmt.Errorf("failed to get last commit for block %d: %w", header.Height(), err) + } + } + } else { + // For genesis block (height 0), there's no previous block, so no lastCommit + lastCommit = nil } block, err := cometcompat.ToABCIBlock(header, data, lastCommit) From 691590323ea8beef5b875c335af7a038da18f212 Mon Sep 17 00:00:00 2001 From: Alex Peters Date: Tue, 24 Jun 2025 17:46:21 +0200 Subject: [PATCH 09/17] Start better attestation --- modules/network/depinject.go | 2 + modules/network/keeper/msg_server.go | 185 ++++++++++++------ modules/network/keeper/msg_server_test.go | 217 +++++++++++++++++++++- modules/network/types/expected_keepers.go | 6 + 4 files changed, 345 insertions(+), 65 deletions(-) diff --git a/modules/network/depinject.go b/modules/network/depinject.go index 01b57e6e..b0c99cd4 100644 --- a/modules/network/depinject.go +++ b/modules/network/depinject.go @@ -35,6 +35,7 @@ type ModuleInputs struct { StakingKeeper types.StakingKeeper AccountKeeper types.AccountKeeper BankKeeper types.BankKeeper + BlockSource types.BlockSource } type ModuleOutputs struct { @@ -57,6 +58,7 @@ func ProvideModule(in ModuleInputs) ModuleOutputs { in.StakingKeeper, in.AccountKeeper, in.BankKeeper, + in.BlockSource, authority.String(), ) m := NewAppModule( diff --git a/modules/network/keeper/msg_server.go b/modules/network/keeper/msg_server.go index 48176c75..554b77ee 100644 --- a/modules/network/keeper/msg_server.go +++ b/modules/network/keeper/msg_server.go @@ -1,12 +1,15 @@ package keeper import ( + "bytes" "context" - "errors" + "cosmossdk.io/collections" "fmt" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + cmttypes "github.com/cometbft/cometbft/types" + "github.com/cosmos/gogoproto/proto" - "cosmossdk.io/collections" - sdkerr "cosmossdk.io/errors" + "cosmossdk.io/errors" "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -30,33 +33,53 @@ var _ types.MsgServer = msgServer{} func (k msgServer) Attest(goCtx context.Context, msg *types.MsgAttest) (*types.MsgAttestResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - if k.GetParams(ctx).SignMode == types.SignMode_SIGN_MODE_CHECKPOINT && - !k.IsCheckpointHeight(ctx, msg.Height) { - return nil, sdkerr.Wrapf(sdkerrors.ErrInvalidRequest, "height %d is not a checkpoint", msg.Height) - } - has, err := k.IsInAttesterSet(ctx, msg.Validator) - if err != nil { - return nil, sdkerr.Wrapf(err, "in attester set") + if err := k.validateAttestation(ctx, msg); err != nil { + return nil, err } - if !has { - return nil, sdkerr.Wrapf(sdkerrors.ErrUnauthorized, "validator %s not in attester set", msg.Validator) + votedEpoch := k.GetEpochForHeight(ctx, msg.Height) + if k.GetCurrentEpoch(ctx) != votedEpoch+1 { // can vote only for last epoch + return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "validator %s not voted for epoch %d", msg.Validator, votedEpoch) } - index, found := k.GetValidatorIndex(ctx, msg.Validator) + valIndexPos, found := k.GetValidatorIndex(ctx, msg.Validator) if !found { - return nil, sdkerr.Wrapf(sdkerrors.ErrNotFound, "validator index not found for %s", msg.Validator) + return nil, errors.Wrapf(sdkerrors.ErrNotFound, "validator index not found for %s", msg.Validator) } - // todo (Alex): we need to set a limit to not have validators attest old blocks. Also make sure that this relates with - // the retention period for pruning - bitmap, err := k.GetAttestationBitmap(ctx, msg.Height) - if err != nil && !errors.Is(err, collections.ErrNotFound) { - return nil, sdkerr.Wrap(err, "get attestation bitmap") + if err := k.updateAttestationBitmap(ctx, msg, valIndexPos); err != nil { + return nil, errors.Wrap(err, "update attestation bitmap") } - if bitmap == nil { + + vote, err := k.verifyVote(ctx, msg) + if err != nil { + return nil, err + } + + if err := k.SetSignature(ctx, msg.Height, msg.Validator, vote.Signature); err != nil { + return nil, errors.Wrap(err, "store signature") + } + + if err := k.updateEpochBitmap(ctx, votedEpoch, valIndexPos); err != nil { + return nil, err + } + + // Emit event + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.TypeMsgAttest, + sdk.NewAttribute("validator", msg.Validator), + sdk.NewAttribute("height", math.NewInt(msg.Height).String()), + ), + ) + return &types.MsgAttestResponse{}, nil +} + +func (k msgServer) updateEpochBitmap(ctx sdk.Context, votedEpoch uint64, index uint16) error { + epochBitmap := k.GetEpochBitmap(ctx, votedEpoch) + if epochBitmap == nil { validators, err := k.stakingKeeper.GetLastValidators(ctx) if err != nil { - return nil, err + return err } numValidators := 0 for _, v := range validators { @@ -64,32 +87,43 @@ func (k msgServer) Attest(goCtx context.Context, msg *types.MsgAttest) (*types.M numValidators++ } } - bitmap = k.bitmapHelper.NewBitmap(numValidators) + epochBitmap = k.bitmapHelper.NewBitmap(numValidators) } - - if k.bitmapHelper.IsSet(bitmap, int(index)) { - return nil, sdkerr.Wrapf(sdkerrors.ErrInvalidRequest, "validator %s already attested for height %d", msg.Validator, msg.Height) + k.bitmapHelper.SetBit(epochBitmap, int(index)) + if err := k.SetEpochBitmap(ctx, votedEpoch, epochBitmap); err != nil { + return errors.Wrap(err, "set epoch bitmap") } + return nil +} - // TODO: Verify the vote signature here once we implement vote parsing +// validateAttestation validates the attestation request +func (k msgServer) validateAttestation(ctx sdk.Context, msg *types.MsgAttest) error { + if k.GetParams(ctx).SignMode == types.SignMode_SIGN_MODE_CHECKPOINT && + !k.IsCheckpointHeight(ctx, msg.Height) { + return errors.Wrapf(sdkerrors.ErrInvalidRequest, "height %d is not a checkpoint", msg.Height) + } - // Set the bit - k.bitmapHelper.SetBit(bitmap, int(index)) - if err := k.SetAttestationBitmap(ctx, msg.Height, bitmap); err != nil { - return nil, sdkerr.Wrap(err, "set attestation bitmap") + has, err := k.IsInAttesterSet(ctx, msg.Validator) + if err != nil { + return errors.Wrapf(err, "in attester set") + } + if !has { + return errors.Wrapf(sdkerrors.ErrUnauthorized, "validator %s not in attester set", msg.Validator) } + return nil +} - // Store signature using the new collection method - if err := k.SetSignature(ctx, msg.Height, msg.Validator, msg.Vote); err != nil { - return nil, sdkerr.Wrap(err, "store signature") +// updateAttestationBitmap handles bitmap operations for attestation +func (k msgServer) updateAttestationBitmap(ctx sdk.Context, msg *types.MsgAttest, index uint16) error { + bitmap, err := k.GetAttestationBitmap(ctx, msg.Height) + if err != nil && !errors.IsOf(err, collections.ErrNotFound) { + return err } - epoch := k.GetCurrentEpoch(ctx) - epochBitmap := k.GetEpochBitmap(ctx, epoch) - if epochBitmap == nil { + if bitmap == nil { validators, err := k.stakingKeeper.GetLastValidators(ctx) if err != nil { - return nil, err + return err } numValidators := 0 for _, v := range validators { @@ -97,23 +131,56 @@ func (k msgServer) Attest(goCtx context.Context, msg *types.MsgAttest) (*types.M numValidators++ } } - epochBitmap = k.bitmapHelper.NewBitmap(numValidators) + bitmap = k.bitmapHelper.NewBitmap(numValidators) } - k.bitmapHelper.SetBit(epochBitmap, int(index)) - if err := k.SetEpochBitmap(ctx, epoch, epochBitmap); err != nil { - return nil, sdkerr.Wrap(err, "set epoch bitmap") + + if k.bitmapHelper.IsSet(bitmap, int(index)) { + return errors.Wrapf(sdkerrors.ErrInvalidRequest, "validator %s already attested for height %d", msg.Validator, msg.Height) } - // Emit event - ctx.EventManager().EmitEvent( - sdk.NewEvent( - types.TypeMsgAttest, - sdk.NewAttribute("validator", msg.Validator), - sdk.NewAttribute("height", math.NewInt(msg.Height).String()), - ), - ) + k.bitmapHelper.SetBit(bitmap, int(index)) - return &types.MsgAttestResponse{}, nil + if err := k.SetAttestationBitmap(ctx, msg.Height, bitmap); err != nil { + return errors.Wrap(err, "set attestation bitmap") + } + return nil +} + +// verifyVote verifies the vote signature and block hash +func (k msgServer) verifyVote(ctx sdk.Context, msg *types.MsgAttest) (*cmtproto.Vote, error) { + var vote cmtproto.Vote + if err := proto.Unmarshal(msg.Vote, &vote); err != nil { + return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "unmarshal vote: %s", err) + } + + header, _, err := k.blockSource.GetBlockData(ctx, uint64(msg.Height)) + if err != nil { + return nil, errors.Wrapf(err, "block data for height %d", msg.Height) + } + if header == nil { + return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "block header not found for height %d", msg.Height) + } + + if !bytes.Equal(vote.BlockID.Hash, header.Header.AppHash) { + return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "vote block ID hash does not match app hash for height %d", msg.Height) + } + + validator, err := k.stakingKeeper.GetValidator(ctx, vote.ValidatorAddress) + if err != nil { + return nil, errors.Wrapf(err, "get validator") + } + + pubKey, err := validator.ConsPubKey() + if err != nil { + return nil, errors.Wrapf(err, "pubkey") + } + + voteSignBytes := cmttypes.VoteSignBytes(header.Header.ChainID(), &vote) + if !pubKey.VerifySignature(voteSignBytes, vote.Signature) { + return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid vote signature") + } + + return &vote, nil } // JoinAttesterSet handles MsgJoinAttesterSet @@ -122,7 +189,7 @@ func (k msgServer) JoinAttesterSet(goCtx context.Context, msg *types.MsgJoinAtte valAddr, err := sdk.ValAddressFromBech32(msg.Validator) if err != nil { - return nil, sdkerr.Wrapf(sdkerrors.ErrInvalidAddress, "invalid validator address: %s", err) + return nil, errors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid validator address: %s", err) } validator, err := k.stakingKeeper.GetValidator(ctx, valAddr) @@ -131,19 +198,19 @@ func (k msgServer) JoinAttesterSet(goCtx context.Context, msg *types.MsgJoinAtte } if !validator.IsBonded() { - return nil, sdkerr.Wrapf(sdkerrors.ErrInvalidRequest, "validator must be bonded to join attester set") + return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "validator must be bonded to join attester set") } has, err := k.IsInAttesterSet(ctx, msg.Validator) if err != nil { - return nil, sdkerr.Wrapf(err, "in attester set") + return nil, errors.Wrapf(err, "in attester set") } if has { - return nil, sdkerr.Wrapf(sdkerrors.ErrInvalidRequest, "validator already in attester set") + return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "validator already in attester set") } // TODO (Alex): the valset should be updated at the end of an epoch only if err := k.SetAttesterSetMember(ctx, msg.Validator); err != nil { - return nil, sdkerr.Wrap(err, "set attester set member") + return nil, errors.Wrap(err, "set attester set member") } ctx.EventManager().EmitEvent( @@ -162,15 +229,15 @@ func (k msgServer) LeaveAttesterSet(goCtx context.Context, msg *types.MsgLeaveAt has, err := k.IsInAttesterSet(ctx, msg.Validator) if err != nil { - return nil, sdkerr.Wrapf(err, "in attester set") + return nil, errors.Wrapf(err, "in attester set") } if !has { - return nil, sdkerr.Wrapf(sdkerrors.ErrInvalidRequest, "validator not in attester set") + return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "validator not in attester set") } // TODO (Alex): the valset should be updated at the end of an epoch only if err := k.RemoveAttesterSetMember(ctx, msg.Validator); err != nil { - return nil, sdkerr.Wrap(err, "remove attester set member") + return nil, errors.Wrap(err, "remove attester set member") } ctx.EventManager().EmitEvent( @@ -188,7 +255,7 @@ func (k msgServer) UpdateParams(goCtx context.Context, msg *types.MsgUpdateParam ctx := sdk.UnwrapSDKContext(goCtx) if k.GetAuthority() != msg.Authority { - return nil, sdkerr.Wrapf(govtypes.ErrInvalidSigner, "invalid authority; expected %s, got %s", k.GetAuthority(), msg.Authority) + return nil, errors.Wrapf(govtypes.ErrInvalidSigner, "invalid authority; expected %s, got %s", k.GetAuthority(), msg.Authority) } if err := msg.Params.Validate(); err != nil { diff --git a/modules/network/keeper/msg_server_test.go b/modules/network/keeper/msg_server_test.go index 7f8a992f..9cbe1ef2 100644 --- a/modules/network/keeper/msg_server_test.go +++ b/modules/network/keeper/msg_server_test.go @@ -2,16 +2,27 @@ package keeper import ( "context" + "crypto/sha256" + cmttypes "github.com/cometbft/cometbft/types" "maps" "slices" "strings" "testing" "time" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + + "github.com/cosmos/gogoproto/proto" + ds "github.com/ipfs/go-datastore" + kt "github.com/ipfs/go-datastore/keytransform" + rollnode "github.com/rollkit/rollkit/node" + rstore "github.com/rollkit/rollkit/pkg/store" + "cosmossdk.io/log" "cosmossdk.io/math" storetypes "cosmossdk.io/store/types" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" "github.com/cosmos/cosmos-sdk/runtime" "github.com/cosmos/cosmos-sdk/testutil/integration" sdk "github.com/cosmos/cosmos-sdk/types" @@ -19,6 +30,7 @@ import ( moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + rollkittypes "github.com/rollkit/rollkit/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -26,7 +38,7 @@ import ( ) func TestJoinAttesterSet(t *testing.T) { - myValAddr := sdk.ValAddress("validator4") + myValAddr := sdk.ValAddress("validator") type testCase struct { setup func(t *testing.T, ctx sdk.Context, keeper *Keeper, sk *MockStakingKeeper) @@ -114,7 +126,7 @@ func TestJoinAttesterSet(t *testing.T) { logger := log.NewTestLogger(t) cms := integration.CreateMultiStore(keys, logger) authority := authtypes.NewModuleAddress("gov") - keeper := NewKeeper(cdc, runtime.NewKVStoreService(keys[types.StoreKey]), sk, nil, nil, authority.String()) + keeper := NewKeeper(cdc, runtime.NewKVStoreService(keys[types.StoreKey]), sk, nil, nil, nil, authority.String()) server := msgServer{Keeper: keeper} ctx := sdk.NewContext(cms, cmtproto.Header{ChainID: "test-chain", Time: time.Now().UTC(), Height: 10}, false, logger). WithContext(t.Context()) @@ -141,23 +153,192 @@ func TestJoinAttesterSet(t *testing.T) { } } +func TestAttest(t *testing.T) { + var ( + myHash = sha256.Sum256([]byte("app_hash")) + myAppHash = myHash[:] + chainID = "testing" + voteSigner = ed25519.GenPrivKey() + validVote = &cmtproto.Vote{ + Type: cmtproto.PrecommitType, + Height: 10, + Round: 0, + BlockID: cmtproto.BlockID{Hash: myAppHash, PartSetHeader: cmtproto.PartSetHeader{Total: 1, Hash: myAppHash}}, + Timestamp: time.Now(), + ValidatorAddress: voteSigner.PubKey().Address(), + ValidatorIndex: 0, + } + // Create the vote signature using VoteSignBytes + ) + validVote.Signature = must(voteSigner.Sign(cmttypes.VoteSignBytes(chainID, validVote))) + validVoteBz := must(proto.Marshal(validVote)) + + testCases := map[string]struct { + setupMock func(t *testing.T, ctx sdk.Context, keeper *Keeper, sk *MockStakingKeeper, bs rstore.Store) + msg *types.MsgAttest + expErr error + }{ + "valid attestation": { + setupMock: func(t *testing.T, ctx sdk.Context, keeper *Keeper, sk *MockStakingKeeper, bs rstore.Store) { + // Use the validator address from the vote + valAddrStr := sdk.ValAddress(voteSigner.PubKey().Address()).String() + validator, err := stakingtypes.NewValidator(valAddrStr, voteSigner.PubKey(), stakingtypes.Description{}) + require.NoError(t, err) + require.NoError(t, sk.SetValidator(ctx, validator)) + require.NoError(t, keeper.SetAttesterSetMember(ctx, valAddrStr)) + require.NoError(t, keeper.SetValidatorIndex(ctx, valAddrStr, 0, 100)) + sk.SetValidatorPubKey(valAddrStr, voteSigner.PubKey()) + + // Set up params for checkpoint height + params := types.DefaultParams() + params.EpochLength = 10 + require.NoError(t, keeper.SetParams(ctx, params)) + + header := rollkittypes.Header{ + BaseHeader: rollkittypes.BaseHeader{ + Height: 10, + Time: uint64(time.Now().UnixNano()), + ChainID: "testing", + }, + Version: rollkittypes.Version{Block: 1, App: 1}, + ProposerAddress: voteSigner.PubKey().Address(), + AppHash: myAppHash, + DataHash: []byte("data_hash"), + ConsensusHash: []byte("consensus_hash"), + ValidatorHash: []byte("validator_hash"), + } + signedHeader := &rollkittypes.SignedHeader{ + Header: header, + Signature: myAppHash, + //Signer: rollkittypes.Signer{PubKey: voteSigner.PubKey()}, + } + data := &rollkittypes.Data{ + Txs: rollkittypes.Txs{}, + } + var signature rollkittypes.Signature + require.NoError(t, bs.SaveBlockData(ctx, signedHeader, data, &signature)) + }, + msg: &types.MsgAttest{ + Validator: sdk.ValAddress(voteSigner.PubKey().Address()).String(), + Height: 10, + Vote: validVoteBz, + }, + }, + //"validator not in attester set": { + // setupMock: func(t *testing.T, ctx sdk.Context, keeper *Keeper, sk *MockStakingKeeper, bs rstore.Store) { + // // Set up validator but don't add to attester set + // valAddr := sdk.ValAddress("validator1") + // validator := stakingtypes.Validator{ + // OperatorAddress: valAddr.String(), + // Status: stakingtypes.Bonded, + // } + // require.NoError(t, sk.SetValidator(ctx, validator)) + // + // // Set up params for checkpoint height + // params := types.DefaultParams() + // params.EpochLength = 10 // Make height 10 a checkpoint + // require.NoError(t, keeper.SetParams(ctx, params)) + // }, + // msg: &types.MsgAttest{ + // Validator: sdk.ValAddress("validator1").String(), + // Height: 10, + // Vote: []byte("mock_vote"), + // }, + // expErr: sdkerrors.ErrUnauthorized, + //}, + //"not a checkpoint height": { + // setupMock: func(t *testing.T, ctx sdk.Context, keeper *Keeper, sk *MockStakingKeeper, bs rstore.Store) { + // // Set up validator and add to attester set + // valAddr := sdk.ValAddress("validator1") + // validator := stakingtypes.Validator{ + // OperatorAddress: valAddr.String(), + // Status: stakingtypes.Bonded, + // } + // require.NoError(t, sk.SetValidator(ctx, validator)) + // require.NoError(t, keeper.SetAttesterSetMember(ctx, valAddr.String())) + // + // // Set up params for checkpoint height + // params := types.DefaultParams() + // params.EpochLength = 10 // Make height 10 a checkpoint + // require.NoError(t, keeper.SetParams(ctx, params)) + // }, + // msg: &types.MsgAttest{ + // Validator: sdk.ValAddress("validator1").String(), + // Height: 11, // Not a checkpoint height + // Vote: []byte("mock_vote"), + // }, + // expErr: sdkerrors.ErrInvalidRequest, + //}, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + // Create mocks with test doubles + sk := NewMockStakingKeeper() + + rollkitPrefixStore := kt.Wrap(ds.NewMapDatastore(), &kt.PrefixTransform{ + Prefix: ds.NewKey(rollnode.RollkitPrefix), + }) + bs := rstore.New(rollkitPrefixStore) + + // Set up codec and store + cdc := moduletestutil.MakeTestEncodingConfig().Codec + keys := storetypes.NewKVStoreKeys(types.StoreKey) + + // Set up context and keeper + logger := log.NewTestLogger(t) + cms := integration.CreateMultiStore(keys, logger) + authority := authtypes.NewModuleAddress("gov") + keeper := NewKeeper(cdc, runtime.NewKVStoreService(keys[types.StoreKey]), &sk, nil, nil, bs, authority.String()) + server := NewMsgServerImpl(keeper) + + ctx := sdk.NewContext(cms, cmtproto.Header{ChainID: "test-chain", Time: time.Now().UTC(), Height: 20}, false, logger). + WithContext(t.Context()) + + // Run setup + tc.setupMock(t, ctx, &keeper, &sk, bs) + + // when + rsp, err := server.Attest(ctx, tc.msg) + + // then + if tc.expErr != nil { + require.Error(t, err) + require.ErrorIs(t, err, tc.expErr) + require.Nil(t, rsp) + return + } + + require.NoError(t, err) + require.NotNil(t, rsp) + + // In a successful case, we would check that the signature was stored + // and the attestation bitmap was updated + // But since we're mocking the verifyVote method, we can't check these + // in a meaningful way + }) + } +} + var _ types.StakingKeeper = &MockStakingKeeper{} type MockStakingKeeper struct { activeSet map[string]stakingtypes.Validator + pubKeys map[string]cryptotypes.PubKey } func NewMockStakingKeeper() MockStakingKeeper { return MockStakingKeeper{ activeSet: make(map[string]stakingtypes.Validator), + pubKeys: make(map[string]cryptotypes.PubKey), } } func (m *MockStakingKeeper) SetValidator(ctx context.Context, validator stakingtypes.Validator) error { m.activeSet[validator.GetOperator()] = validator return nil - } + func (m MockStakingKeeper) GetAllValidators(ctx context.Context) (validators []stakingtypes.Validator, err error) { return slices.SortedFunc(maps.Values(m.activeSet), func(v1 stakingtypes.Validator, v2 stakingtypes.Validator) int { return strings.Compare(v1.OperatorAddress, v2.OperatorAddress) @@ -165,11 +346,24 @@ func (m MockStakingKeeper) GetAllValidators(ctx context.Context) (validators []s } func (m MockStakingKeeper) GetValidator(ctx context.Context, addr sdk.ValAddress) (validator stakingtypes.Validator, err error) { + // First try to find the validator by address validator, found := m.activeSet[addr.String()] - if !found { - return validator, sdkerrors.ErrNotFound + if found { + return validator, nil + } + + // If not found by address, try to find by public key address + addrStr := addr.String() + for valAddrStr, pubKey := range m.pubKeys { + if pubKey.Address().String() == addrStr { + validator, found = m.activeSet[valAddrStr] + if found { + return validator, nil + } + } } - return validator, nil + + return validator, sdkerrors.ErrNotFound } func (m MockStakingKeeper) GetLastValidators(ctx context.Context) (validators []stakingtypes.Validator, err error) { @@ -184,3 +378,14 @@ func (m MockStakingKeeper) GetLastValidators(ctx context.Context) (validators [] func (m MockStakingKeeper) GetLastTotalPower(ctx context.Context) (math.Int, error) { return math.NewInt(int64(len(m.activeSet))), nil } + +func (m *MockStakingKeeper) SetValidatorPubKey(valAddrStr string, key cryptotypes.PubKey) { + m.pubKeys[valAddrStr] = key +} + +func must[T any](r T, err error) T { + if err != nil { + panic(err) + } + return r +} diff --git a/modules/network/types/expected_keepers.go b/modules/network/types/expected_keepers.go index 8c11e6de..b9def7f3 100644 --- a/modules/network/types/expected_keepers.go +++ b/modules/network/types/expected_keepers.go @@ -2,6 +2,7 @@ package types import ( "context" + tyrollkittypes "github.com/rollkit/rollkit/types" "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" @@ -25,3 +26,8 @@ type AccountKeeper interface { type BankKeeper interface { SpendableCoins(ctx context.Context, addr sdk.AccAddress) sdk.Coins } + +// BlockSource is the block store +type BlockSource interface { + GetBlockData(ctx context.Context, height uint64) (*tyrollkittypes.SignedHeader, *tyrollkittypes.Data, error) +} From e16c6b2bd3d34775b3c1c5a11a3bbe409c77dd48 Mon Sep 17 00:00:00 2001 From: Alex Peters Date: Wed, 25 Jun 2025 15:07:45 +0200 Subject: [PATCH 10/17] Test attestation happy path --- modules/network/keeper/msg_server_test.go | 151 +++++++++++++--------- 1 file changed, 88 insertions(+), 63 deletions(-) diff --git a/modules/network/keeper/msg_server_test.go b/modules/network/keeper/msg_server_test.go index 9cbe1ef2..de31053f 100644 --- a/modules/network/keeper/msg_server_test.go +++ b/modules/network/keeper/msg_server_test.go @@ -3,13 +3,15 @@ package keeper import ( "context" "crypto/sha256" - cmttypes "github.com/cometbft/cometbft/types" "maps" "slices" "strings" "testing" "time" + cmttypes "github.com/cometbft/cometbft/types" + "github.com/libp2p/go-libp2p/core/crypto" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" "github.com/cosmos/gogoproto/proto" @@ -18,6 +20,7 @@ import ( rollnode "github.com/rollkit/rollkit/node" rstore "github.com/rollkit/rollkit/pkg/store" + "cosmossdk.io/collections" "cosmossdk.io/log" "cosmossdk.io/math" storetypes "cosmossdk.io/store/types" @@ -155,34 +158,23 @@ func TestJoinAttesterSet(t *testing.T) { func TestAttest(t *testing.T) { var ( - myHash = sha256.Sum256([]byte("app_hash")) - myAppHash = myHash[:] - chainID = "testing" - voteSigner = ed25519.GenPrivKey() - validVote = &cmtproto.Vote{ - Type: cmtproto.PrecommitType, - Height: 10, - Round: 0, - BlockID: cmtproto.BlockID{Hash: myAppHash, PartSetHeader: cmtproto.PartSetHeader{Total: 1, Hash: myAppHash}}, - Timestamp: time.Now(), - ValidatorAddress: voteSigner.PubKey().Address(), - ValidatorIndex: 0, - } - // Create the vote signature using VoteSignBytes + myHash = sha256.Sum256([]byte("app_hash")) + myAppHash = myHash[:] + voteSigner = ed25519.GenPrivKey() + valAddrStr = sdk.ValAddress(voteSigner.PubKey().Address()).String() + validVote = VoteFixture(myAppHash, voteSigner) + validVoteBz = must(proto.Marshal(validVote)) ) - validVote.Signature = must(voteSigner.Sign(cmttypes.VoteSignBytes(chainID, validVote))) - validVoteBz := must(proto.Marshal(validVote)) - testCases := map[string]struct { - setupMock func(t *testing.T, ctx sdk.Context, keeper *Keeper, sk *MockStakingKeeper, bs rstore.Store) - msg *types.MsgAttest - expErr error + specs := map[string]struct { + setup func(t *testing.T, ctx sdk.Context, keeper *Keeper, sk *MockStakingKeeper, bs rstore.Store) + msg *types.MsgAttest + expErr error }{ "valid attestation": { - setupMock: func(t *testing.T, ctx sdk.Context, keeper *Keeper, sk *MockStakingKeeper, bs rstore.Store) { - // Use the validator address from the vote - valAddrStr := sdk.ValAddress(voteSigner.PubKey().Address()).String() + setup: func(t *testing.T, ctx sdk.Context, keeper *Keeper, sk *MockStakingKeeper, bs rstore.Store) { validator, err := stakingtypes.NewValidator(valAddrStr, voteSigner.PubKey(), stakingtypes.Description{}) + validator.Status = stakingtypes.Bonded require.NoError(t, err) require.NoError(t, sk.SetValidator(ctx, validator)) require.NoError(t, keeper.SetAttesterSetMember(ctx, valAddrStr)) @@ -194,32 +186,13 @@ func TestAttest(t *testing.T) { params.EpochLength = 10 require.NoError(t, keeper.SetParams(ctx, params)) - header := rollkittypes.Header{ - BaseHeader: rollkittypes.BaseHeader{ - Height: 10, - Time: uint64(time.Now().UnixNano()), - ChainID: "testing", - }, - Version: rollkittypes.Version{Block: 1, App: 1}, - ProposerAddress: voteSigner.PubKey().Address(), - AppHash: myAppHash, - DataHash: []byte("data_hash"), - ConsensusHash: []byte("consensus_hash"), - ValidatorHash: []byte("validator_hash"), - } - signedHeader := &rollkittypes.SignedHeader{ - Header: header, - Signature: myAppHash, - //Signer: rollkittypes.Signer{PubKey: voteSigner.PubKey()}, - } - data := &rollkittypes.Data{ - Txs: rollkittypes.Txs{}, - } + signedHeader := HeaderFixture(voteSigner, myAppHash) + data := &rollkittypes.Data{Txs: rollkittypes.Txs{}} var signature rollkittypes.Signature require.NoError(t, bs.SaveBlockData(ctx, signedHeader, data, &signature)) }, msg: &types.MsgAttest{ - Validator: sdk.ValAddress(voteSigner.PubKey().Address()).String(), + Validator: valAddrStr, Height: 10, Vote: validVoteBz, }, @@ -271,16 +244,12 @@ func TestAttest(t *testing.T) { //}, } - for name, tc := range testCases { + for name, spec := range specs { t.Run(name, func(t *testing.T) { - // Create mocks with test doubles - sk := NewMockStakingKeeper() - rollkitPrefixStore := kt.Wrap(ds.NewMapDatastore(), &kt.PrefixTransform{ Prefix: ds.NewKey(rollnode.RollkitPrefix), }) bs := rstore.New(rollkitPrefixStore) - // Set up codec and store cdc := moduletestutil.MakeTestEncodingConfig().Codec keys := storetypes.NewKVStoreKeys(types.StoreKey) @@ -289,6 +258,7 @@ func TestAttest(t *testing.T) { logger := log.NewTestLogger(t) cms := integration.CreateMultiStore(keys, logger) authority := authtypes.NewModuleAddress("gov") + sk := NewMockStakingKeeper() keeper := NewKeeper(cdc, runtime.NewKVStoreService(keys[types.StoreKey]), &sk, nil, nil, bs, authority.String()) server := NewMsgServerImpl(keeper) @@ -296,30 +266,85 @@ func TestAttest(t *testing.T) { WithContext(t.Context()) // Run setup - tc.setupMock(t, ctx, &keeper, &sk, bs) + spec.setup(t, ctx, &keeper, &sk, bs) // when - rsp, err := server.Attest(ctx, tc.msg) + gotRsp, gotErr := server.Attest(ctx, spec.msg) // then - if tc.expErr != nil { - require.Error(t, err) - require.ErrorIs(t, err, tc.expErr) - require.Nil(t, rsp) + if spec.expErr != nil { + require.Error(t, gotErr) + require.ErrorIs(t, gotErr, spec.expErr) + // and ensure the signature is not stored + _, err := keeper.GetSignature(ctx, spec.msg.Height, valAddrStr) + assert.ErrorIs(t, err, collections.ErrNotFound) return } - require.NoError(t, err) - require.NotNil(t, rsp) + require.NoError(t, gotErr) + require.NotNil(t, gotRsp) + + // and attestation marked + bitmap, gotErr := keeper.GetAttestationBitmap(ctx, spec.msg.Height) + require.NoError(t, gotErr) + require.NotEmpty(t, bitmap) + require.Equal(t, byte(1), bitmap[0]) - // In a successful case, we would check that the signature was stored - // and the attestation bitmap was updated - // But since we're mocking the verifyVote method, we can't check these - // in a meaningful way + // and the signature was stored properly + gotSig, err := keeper.GetSignature(ctx, spec.msg.Height, valAddrStr) + require.NoError(t, err) + var vote cmtproto.Vote + require.NoError(t, proto.Unmarshal(spec.msg.Vote, &vote)) + assert.Equal(t, vote.Signature, gotSig) }) } } +func HeaderFixture(signer *ed25519.PrivKey, myAppHash []byte, mutators ...func(*rollkittypes.SignedHeader)) *rollkittypes.SignedHeader { + header := rollkittypes.Header{ + BaseHeader: rollkittypes.BaseHeader{ + Height: 10, + Time: uint64(time.Now().UnixNano()), + ChainID: "testing", + }, + Version: rollkittypes.Version{Block: 1, App: 1}, + ProposerAddress: signer.PubKey().Address(), + AppHash: myAppHash, + DataHash: []byte("data_hash"), + ConsensusHash: []byte("consensus_hash"), + ValidatorHash: []byte("validator_hash"), + } + signedHeader := &rollkittypes.SignedHeader{ + Header: header, + Signature: myAppHash, + Signer: rollkittypes.Signer{PubKey: must(crypto.UnmarshalEd25519PublicKey(signer.PubKey().Bytes()))}, + } + for _, m := range mutators { + m(signedHeader) + } + return signedHeader +} + +func VoteFixture(myAppHash []byte, voteSigner *ed25519.PrivKey, mutators ...func(vote *cmtproto.Vote)) *cmtproto.Vote { + const chainID = "testing" + + vote := &cmtproto.Vote{ + Type: cmtproto.PrecommitType, + Height: 10, + Round: 0, + BlockID: cmtproto.BlockID{Hash: myAppHash, PartSetHeader: cmtproto.PartSetHeader{Total: 1, Hash: myAppHash}}, + Timestamp: time.Now(), + ValidatorAddress: voteSigner.PubKey().Address(), + ValidatorIndex: 0, + } + vote.Signature = must(voteSigner.Sign(cmttypes.VoteSignBytes(chainID, vote))) + + for _, m := range mutators { + m(vote) + } + return vote +} + var _ types.StakingKeeper = &MockStakingKeeper{} type MockStakingKeeper struct { From e70ca965c011d26c05e8bd4f738242769824aca2 Mon Sep 17 00:00:00 2001 From: Alex Peters Date: Thu, 26 Jun 2025 17:18:22 +0200 Subject: [PATCH 11/17] Add more conditions for successful attestation --- modules/network/keeper/fixtures_test.go | 122 ++++++ modules/network/keeper/msg_server.go | 42 +- modules/network/keeper/msg_server_test.go | 506 +++++++++++----------- modules/network/types/expected_keepers.go | 3 +- 4 files changed, 405 insertions(+), 268 deletions(-) create mode 100644 modules/network/keeper/fixtures_test.go diff --git a/modules/network/keeper/fixtures_test.go b/modules/network/keeper/fixtures_test.go new file mode 100644 index 00000000..12d3b273 --- /dev/null +++ b/modules/network/keeper/fixtures_test.go @@ -0,0 +1,122 @@ +package keeper + +import ( + "context" + "maps" + "slices" + "strings" + "time" + + "cosmossdk.io/math" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + cmttypes "github.com/cometbft/cometbft/types" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/libp2p/go-libp2p/core/crypto" + + rollkittypes "github.com/rollkit/rollkit/types" + + "github.com/rollkit/go-execution-abci/modules/network/types" +) + +func HeaderFixture(signer *ed25519.PrivKey, myAppHash []byte, mutators ...func(*rollkittypes.SignedHeader)) *rollkittypes.SignedHeader { + header := rollkittypes.Header{ + BaseHeader: rollkittypes.BaseHeader{ + Height: 10, + Time: uint64(time.Now().UnixNano()), + ChainID: "testing", + }, + Version: rollkittypes.Version{Block: 1, App: 1}, + ProposerAddress: signer.PubKey().Address(), + AppHash: myAppHash, + DataHash: []byte("data_hash"), + ConsensusHash: []byte("consensus_hash"), + ValidatorHash: []byte("validator_hash"), + } + signedHeader := &rollkittypes.SignedHeader{ + Header: header, + Signature: myAppHash, + Signer: rollkittypes.Signer{PubKey: must(crypto.UnmarshalEd25519PublicKey(signer.PubKey().Bytes()))}, + } + for _, m := range mutators { + m(signedHeader) + } + return signedHeader +} + +func VoteFixture(myAppHash []byte, voteSigner *ed25519.PrivKey, mutators ...func(vote *cmtproto.Vote)) *cmtproto.Vote { + const chainID = "testing" + + vote := &cmtproto.Vote{ + Type: cmtproto.PrecommitType, + Height: 10, + Round: 0, + BlockID: cmtproto.BlockID{Hash: myAppHash, PartSetHeader: cmtproto.PartSetHeader{Total: 1, Hash: myAppHash}}, + Timestamp: time.Now().UTC(), + ValidatorAddress: voteSigner.PubKey().Address(), + ValidatorIndex: 0, + } + vote.Signature = must(voteSigner.Sign(cmttypes.VoteSignBytes(chainID, vote))) + + for _, m := range mutators { + m(vote) + } + return vote +} + +var _ types.StakingKeeper = &MockStakingKeeper{} + +type MockStakingKeeper struct { + activeSet map[string]stakingtypes.Validator +} + +func NewMockStakingKeeper() MockStakingKeeper { + return MockStakingKeeper{ + activeSet: make(map[string]stakingtypes.Validator), + } +} + +func (m *MockStakingKeeper) SetValidator(ctx context.Context, validator stakingtypes.Validator) error { + m.activeSet[validator.GetOperator()] = validator + return nil +} +func (m MockStakingKeeper) GetAllValidators(ctx context.Context) (validators []stakingtypes.Validator, err error) { + return slices.SortedFunc(maps.Values(m.activeSet), func(v1 stakingtypes.Validator, v2 stakingtypes.Validator) int { + return strings.Compare(v1.OperatorAddress, v2.OperatorAddress) + }), nil +} +func (m MockStakingKeeper) GetValidator(ctx context.Context, addr sdk.ValAddress) (validator stakingtypes.Validator, err error) { + // First try to find the validator by address + validator, found := m.activeSet[addr.String()] + if found { + return validator, nil + } + + //// If not found by address, try to find by public key address + //addrStr := addr.String() + //for valAddrStr, pubKey := range m.pubKeys { + // if pubKey.Address().String() == addrStr { + // validator, found = m.activeSet[valAddrStr] + // if found { + // return validator, nil + // } + // } + //} + + return validator, sdkerrors.ErrNotFound +} + +func (m MockStakingKeeper) GetLastValidators(ctx context.Context) (validators []stakingtypes.Validator, err error) { + for _, validator := range m.activeSet { + if validator.IsBonded() { // Assuming IsBonded() identifies if a validator is in the last validators + validators = append(validators, validator) + } + } + return +} + +func (m MockStakingKeeper) GetLastTotalPower(ctx context.Context) (math.Int, error) { + return math.NewInt(int64(len(m.activeSet))), nil +} diff --git a/modules/network/keeper/msg_server.go b/modules/network/keeper/msg_server.go index 554b77ee..0428ce7b 100644 --- a/modules/network/keeper/msg_server.go +++ b/modules/network/keeper/msg_server.go @@ -3,17 +3,18 @@ package keeper import ( "bytes" "context" - "cosmossdk.io/collections" "fmt" - cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" - cmttypes "github.com/cometbft/cometbft/types" - "github.com/cosmos/gogoproto/proto" + "time" + "cosmossdk.io/collections" "cosmossdk.io/errors" "cosmossdk.io/math" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + cmttypes "github.com/cometbft/cometbft/types" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + "github.com/cosmos/gogoproto/proto" "github.com/rollkit/go-execution-abci/modules/network/types" ) @@ -36,9 +37,9 @@ func (k msgServer) Attest(goCtx context.Context, msg *types.MsgAttest) (*types.M if err := k.validateAttestation(ctx, msg); err != nil { return nil, err } - votedEpoch := k.GetEpochForHeight(ctx, msg.Height) - if k.GetCurrentEpoch(ctx) != votedEpoch+1 { // can vote only for last epoch - return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "validator %s not voted for epoch %d", msg.Validator, votedEpoch) + // can vote only for the last epoch + if delta := ctx.BlockHeight() - msg.Height; delta < 0 || delta > int64(k.GetParams(ctx).EpochLength) { + return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "exceeded voting window: %d blocks", delta) } valIndexPos, found := k.GetValidatorIndex(ctx, msg.Validator) @@ -59,7 +60,7 @@ func (k msgServer) Attest(goCtx context.Context, msg *types.MsgAttest) (*types.M return nil, errors.Wrap(err, "store signature") } - if err := k.updateEpochBitmap(ctx, votedEpoch, valIndexPos); err != nil { + if err := k.updateEpochBitmap(ctx, k.GetEpochForHeight(ctx, msg.Height), valIndexPos); err != nil { return nil, err } @@ -152,30 +153,39 @@ func (k msgServer) verifyVote(ctx sdk.Context, msg *types.MsgAttest) (*cmtproto. if err := proto.Unmarshal(msg.Vote, &vote); err != nil { return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "unmarshal vote: %s", err) } - - header, _, err := k.blockSource.GetBlockData(ctx, uint64(msg.Height)) + if msg.Height != vote.Height { + return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "vote height does not match attestation height") + } + header, _, err := k.blockSource.GetBlockData(ctx, uint64(vote.Height)) if err != nil { - return nil, errors.Wrapf(err, "block data for height %d", msg.Height) + return nil, errors.Wrapf(err, "block data for height %d", vote.Height) } if header == nil { - return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "block header not found for height %d", msg.Height) + return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "block header not found for height %d", vote.Height) } - if !bytes.Equal(vote.BlockID.Hash, header.Header.AppHash) { - return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "vote block ID hash does not match app hash for height %d", msg.Height) + if !bytes.Equal(vote.BlockID.Hash, header.AppHash) { // todo (Alex): is this the correct hash? + return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "vote block ID hash does not match app hash for height %d", vote.Height) + } + + maxClockDrift := 5 * time.Second // todo (Alex): make this a parameter? + if drift := vote.Timestamp.Sub(header.Time()); drift < 0*time.Nanosecond || drift > maxClockDrift { + return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "vote timestamp drift exceeds limit: %s", drift) } validator, err := k.stakingKeeper.GetValidator(ctx, vote.ValidatorAddress) if err != nil { return nil, errors.Wrapf(err, "get validator") } - pubKey, err := validator.ConsPubKey() if err != nil { return nil, errors.Wrapf(err, "pubkey") } + if !bytes.Equal(pubKey.Address(), vote.ValidatorAddress) { + return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "pubkey address does not match validator address") + } - voteSignBytes := cmttypes.VoteSignBytes(header.Header.ChainID(), &vote) + voteSignBytes := cmttypes.VoteSignBytes(header.ChainID(), &vote) if !pubKey.VerifySignature(voteSignBytes, vote.Signature) { return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid vote signature") } diff --git a/modules/network/keeper/msg_server_test.go b/modules/network/keeper/msg_server_test.go index de31053f..e78488cb 100644 --- a/modules/network/keeper/msg_server_test.go +++ b/modules/network/keeper/msg_server_test.go @@ -1,30 +1,15 @@ package keeper import ( - "context" "crypto/sha256" - "maps" - "slices" - "strings" "testing" "time" - cmttypes "github.com/cometbft/cometbft/types" - "github.com/libp2p/go-libp2p/core/crypto" - - cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - - "github.com/cosmos/gogoproto/proto" - ds "github.com/ipfs/go-datastore" - kt "github.com/ipfs/go-datastore/keytransform" - rollnode "github.com/rollkit/rollkit/node" - rstore "github.com/rollkit/rollkit/pkg/store" - "cosmossdk.io/collections" "cosmossdk.io/log" - "cosmossdk.io/math" storetypes "cosmossdk.io/store/types" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + cmttypes "github.com/cometbft/cometbft/types" "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" "github.com/cosmos/cosmos-sdk/runtime" "github.com/cosmos/cosmos-sdk/testutil/integration" @@ -33,10 +18,16 @@ import ( moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - rollkittypes "github.com/rollkit/rollkit/types" + "github.com/cosmos/gogoproto/proto" + ds "github.com/ipfs/go-datastore" + kt "github.com/ipfs/go-datastore/keytransform" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + rollnode "github.com/rollkit/rollkit/node" + rstore "github.com/rollkit/rollkit/pkg/store" + rollkittypes "github.com/rollkit/rollkit/types" + "github.com/rollkit/go-execution-abci/modules/network/types" ) @@ -44,7 +35,7 @@ func TestJoinAttesterSet(t *testing.T) { myValAddr := sdk.ValAddress("validator") type testCase struct { - setup func(t *testing.T, ctx sdk.Context, keeper *Keeper, sk *MockStakingKeeper) + setup func(t *testing.T, env testEnv) msg *types.MsgJoinAttesterSet expErr error expSet bool @@ -52,104 +43,76 @@ func TestJoinAttesterSet(t *testing.T) { tests := map[string]testCase{ "valid": { - setup: func(t *testing.T, ctx sdk.Context, keeper *Keeper, sk *MockStakingKeeper) { + setup: func(t *testing.T, env testEnv) { validator := stakingtypes.Validator{ OperatorAddress: myValAddr.String(), Status: stakingtypes.Bonded, } - err := sk.SetValidator(ctx, validator) + err := env.SK.SetValidator(env.Ctx, validator) require.NoError(t, err, "failed to set validator") }, msg: &types.MsgJoinAttesterSet{Validator: myValAddr.String()}, expSet: true, }, "invalid_addr": { - setup: func(t *testing.T, ctx sdk.Context, keeper *Keeper, sk *MockStakingKeeper) {}, + setup: func(t *testing.T, env testEnv) {}, msg: &types.MsgJoinAttesterSet{Validator: "invalidAddr"}, expErr: sdkerrors.ErrInvalidAddress, }, "val not exists": { - setup: func(t *testing.T, ctx sdk.Context, keeper *Keeper, sk *MockStakingKeeper) {}, + setup: func(t *testing.T, env testEnv) {}, msg: &types.MsgJoinAttesterSet{Validator: myValAddr.String()}, expErr: sdkerrors.ErrNotFound, }, "val not bonded": { - setup: func(t *testing.T, ctx sdk.Context, keeper *Keeper, sk *MockStakingKeeper) { + setup: func(t *testing.T, env testEnv) { validator := stakingtypes.Validator{ OperatorAddress: myValAddr.String(), Status: stakingtypes.Unbonded, // Validator is not bonded } - err := sk.SetValidator(ctx, validator) + err := env.SK.SetValidator(env.Ctx, validator) require.NoError(t, err, "failed to set validator") }, msg: &types.MsgJoinAttesterSet{Validator: myValAddr.String()}, expErr: sdkerrors.ErrInvalidRequest, }, "already set": { - setup: func(t *testing.T, ctx sdk.Context, keeper *Keeper, sk *MockStakingKeeper) { + setup: func(t *testing.T, env testEnv) { validator := stakingtypes.Validator{ OperatorAddress: myValAddr.String(), Status: stakingtypes.Bonded, } - require.NoError(t, sk.SetValidator(ctx, validator)) - require.NoError(t, keeper.SetAttesterSetMember(ctx, myValAddr.String())) + require.NoError(t, env.SK.SetValidator(env.Ctx, validator)) + require.NoError(t, env.Keeper.SetAttesterSetMember(env.Ctx, myValAddr.String())) }, msg: &types.MsgJoinAttesterSet{Validator: myValAddr.String()}, expErr: sdkerrors.ErrInvalidRequest, expSet: true, }, - //{ - // name: "failed to set attester set member", - // setup: func(t *testing.T, ctx sdk.Context, keeper *Keeper, sk *MockStakingKeeper) { - // validatorAddr := sdk.ValAddress([]byte("validator5")) - // validator := stakingtypes.Validator{ - // OperatorAddress: validatorAddr.String(), - // Status: stakingtypes.Bonded, - // } - // err := sk.SetValidator(ctx, validator) - // require.NoError(t, err, "failed to set validator") - // keeper.forceError = true - // }, - // msg: &types.MsgJoinAttesterSet{ - // Validator: "validator5", - // }, - // expErr: sdkerrors.ErrInternal, - // expectResponse: false, - //}, } - for name, spec := range tests { t.Run(name, func(t *testing.T) { - sk := NewMockStakingKeeper() + // Setup test environment + env := setupTestEnv(t, 10) - cdc := moduletestutil.MakeTestEncodingConfig().Codec - - keys := storetypes.NewKVStoreKeys(types.StoreKey) - - logger := log.NewTestLogger(t) - cms := integration.CreateMultiStore(keys, logger) - authority := authtypes.NewModuleAddress("gov") - keeper := NewKeeper(cdc, runtime.NewKVStoreService(keys[types.StoreKey]), sk, nil, nil, nil, authority.String()) - server := msgServer{Keeper: keeper} - ctx := sdk.NewContext(cms, cmtproto.Header{ChainID: "test-chain", Time: time.Now().UTC(), Height: 10}, false, logger). - WithContext(t.Context()) - - spec.setup(t, ctx, &keeper, &sk) + // Apply test-specific setup + spec.setup(t, env) // when - rsp, err := server.JoinAttesterSet(ctx, spec.msg) + rsp, err := env.Server.JoinAttesterSet(env.Ctx, spec.msg) + // then if spec.expErr != nil { require.ErrorIs(t, err, spec.expErr) require.Nil(t, rsp) - exists, gotErr := keeper.AttesterSet.Has(ctx, spec.msg.Validator) + exists, gotErr := env.Keeper.AttesterSet.Has(env.Ctx, spec.msg.Validator) require.NoError(t, gotErr) assert.Equal(t, exists, spec.expSet) return } require.NoError(t, err) require.NotNil(t, rsp) - exists, gotErr := keeper.AttesterSet.Has(ctx, spec.msg.Validator) + exists, gotErr := env.Keeper.AttesterSet.Has(env.Ctx, spec.msg.Validator) require.NoError(t, gotErr) assert.True(t, exists) }) @@ -157,6 +120,7 @@ func TestJoinAttesterSet(t *testing.T) { } func TestAttest(t *testing.T) { + const epochLength = 10 var ( myHash = sha256.Sum256([]byte("app_hash")) myAppHash = myHash[:] @@ -166,117 +130,126 @@ func TestAttest(t *testing.T) { validVoteBz = must(proto.Marshal(validVote)) ) + // Setup test environment with block store + env := setupTestEnv(t, 2*epochLength) + + // Set up validator + validator, err := stakingtypes.NewValidator(valAddrStr, voteSigner.PubKey(), stakingtypes.Description{}) + require.NoError(t, err) + validator.Status = stakingtypes.Bonded + require.NoError(t, env.SK.SetValidator(env.Ctx, validator)) + + // Save block data + signedHeader := HeaderFixture(voteSigner, myAppHash) + data := &rollkittypes.Data{Txs: rollkittypes.Txs{}} + var signature rollkittypes.Signature + require.NoError(t, env.BlockStore.SaveBlockData(env.Ctx, signedHeader, data, &signature)) + + parentCtx := env.Ctx + specs := map[string]struct { - setup func(t *testing.T, ctx sdk.Context, keeper *Keeper, sk *MockStakingKeeper, bs rstore.Store) - msg *types.MsgAttest + setup func(t *testing.T, env testEnv) testEnv + msg func(t *testing.T) *types.MsgAttest expErr error }{ "valid attestation": { - setup: func(t *testing.T, ctx sdk.Context, keeper *Keeper, sk *MockStakingKeeper, bs rstore.Store) { - validator, err := stakingtypes.NewValidator(valAddrStr, voteSigner.PubKey(), stakingtypes.Description{}) - validator.Status = stakingtypes.Bonded - require.NoError(t, err) - require.NoError(t, sk.SetValidator(ctx, validator)) - require.NoError(t, keeper.SetAttesterSetMember(ctx, valAddrStr)) - require.NoError(t, keeper.SetValidatorIndex(ctx, valAddrStr, 0, 100)) - sk.SetValidatorPubKey(valAddrStr, voteSigner.PubKey()) - - // Set up params for checkpoint height - params := types.DefaultParams() - params.EpochLength = 10 - require.NoError(t, keeper.SetParams(ctx, params)) - - signedHeader := HeaderFixture(voteSigner, myAppHash) - data := &rollkittypes.Data{Txs: rollkittypes.Txs{}} - var signature rollkittypes.Signature - require.NoError(t, bs.SaveBlockData(ctx, signedHeader, data, &signature)) + setup: func(t *testing.T, env testEnv) testEnv { + require.NoError(t, env.Keeper.SetAttesterSetMember(env.Ctx, valAddrStr)) + require.NoError(t, env.Keeper.SetValidatorIndex(env.Ctx, valAddrStr, 0, 100)) + return env }, - msg: &types.MsgAttest{ - Validator: valAddrStr, - Height: 10, - Vote: validVoteBz, + msg: func(t *testing.T) *types.MsgAttest { + return &types.MsgAttest{Validator: valAddrStr, Height: epochLength, Vote: validVoteBz} + }, + }, + "invalid vote content": { + setup: func(t *testing.T, env testEnv) testEnv { + require.NoError(t, env.Keeper.SetAttesterSetMember(env.Ctx, valAddrStr)) + require.NoError(t, env.Keeper.SetValidatorIndex(env.Ctx, valAddrStr, 0, 100)) + return env }, + msg: func(t *testing.T) *types.MsgAttest { + return &types.MsgAttest{Validator: valAddrStr, Height: epochLength, Vote: []byte("not a valid proto vote")} + }, + expErr: sdkerrors.ErrInvalidRequest, + }, + "validator not in attester set": { + setup: func(t *testing.T, env testEnv) testEnv { + require.NoError(t, env.Keeper.SetValidatorIndex(env.Ctx, valAddrStr, 0, 100)) + return env + }, + msg: func(t *testing.T) *types.MsgAttest { + return &types.MsgAttest{Validator: valAddrStr, Height: epochLength, Vote: validVoteBz} + }, + expErr: sdkerrors.ErrUnauthorized, + }, + "invalid signature": { + setup: func(t *testing.T, env testEnv) testEnv { + require.NoError(t, env.Keeper.SetAttesterSetMember(env.Ctx, valAddrStr)) + require.NoError(t, env.Keeper.SetValidatorIndex(env.Ctx, valAddrStr, 0, 100)) + return env + }, + msg: func(t *testing.T) *types.MsgAttest { + invalidVote := VoteFixture(myAppHash, voteSigner, func(vote *cmtproto.Vote) { + vote.Signature = []byte("invalid signature") + }) + return &types.MsgAttest{Validator: valAddrStr, Height: epochLength, Vote: must(proto.Marshal(invalidVote))} + }, + expErr: sdkerrors.ErrInvalidRequest, + }, + "not a checkpoint height": { + setup: func(t *testing.T, env testEnv) testEnv { + require.NoError(t, env.Keeper.SetAttesterSetMember(env.Ctx, valAddrStr)) + require.NoError(t, env.Keeper.SetValidatorIndex(env.Ctx, valAddrStr, 0, 100)) + return env + }, + msg: func(t *testing.T) *types.MsgAttest { + return &types.MsgAttest{Validator: valAddrStr, Height: epochLength + 1, Vote: validVoteBz} + }, + expErr: sdkerrors.ErrInvalidRequest, + }, + "vote window expired": { + setup: func(t *testing.T, env testEnv) testEnv { + require.NoError(t, env.Keeper.SetAttesterSetMember(env.Ctx, valAddrStr)) + require.NoError(t, env.Keeper.SetValidatorIndex(env.Ctx, valAddrStr, 0, 100)) + env.Ctx = env.Ctx.WithBlockHeight(2*epochLength + 1) + return env + }, + msg: func(t *testing.T) *types.MsgAttest { + return &types.MsgAttest{Validator: valAddrStr, Height: epochLength, Vote: validVoteBz} + }, + expErr: sdkerrors.ErrInvalidRequest, + }, + "voting for a future epoch": { + setup: func(t *testing.T, env testEnv) testEnv { + require.NoError(t, env.Keeper.SetAttesterSetMember(env.Ctx, valAddrStr)) + require.NoError(t, env.Keeper.SetValidatorIndex(env.Ctx, valAddrStr, 0, 100)) + env.Ctx = env.Ctx.WithBlockHeight(2 * epochLength) + return env + }, + msg: func(t *testing.T) *types.MsgAttest { + return &types.MsgAttest{Validator: valAddrStr, Height: 3 * epochLength, Vote: validVoteBz} + }, + expErr: sdkerrors.ErrInvalidRequest, }, - //"validator not in attester set": { - // setupMock: func(t *testing.T, ctx sdk.Context, keeper *Keeper, sk *MockStakingKeeper, bs rstore.Store) { - // // Set up validator but don't add to attester set - // valAddr := sdk.ValAddress("validator1") - // validator := stakingtypes.Validator{ - // OperatorAddress: valAddr.String(), - // Status: stakingtypes.Bonded, - // } - // require.NoError(t, sk.SetValidator(ctx, validator)) - // - // // Set up params for checkpoint height - // params := types.DefaultParams() - // params.EpochLength = 10 // Make height 10 a checkpoint - // require.NoError(t, keeper.SetParams(ctx, params)) - // }, - // msg: &types.MsgAttest{ - // Validator: sdk.ValAddress("validator1").String(), - // Height: 10, - // Vote: []byte("mock_vote"), - // }, - // expErr: sdkerrors.ErrUnauthorized, - //}, - //"not a checkpoint height": { - // setupMock: func(t *testing.T, ctx sdk.Context, keeper *Keeper, sk *MockStakingKeeper, bs rstore.Store) { - // // Set up validator and add to attester set - // valAddr := sdk.ValAddress("validator1") - // validator := stakingtypes.Validator{ - // OperatorAddress: valAddr.String(), - // Status: stakingtypes.Bonded, - // } - // require.NoError(t, sk.SetValidator(ctx, validator)) - // require.NoError(t, keeper.SetAttesterSetMember(ctx, valAddr.String())) - // - // // Set up params for checkpoint height - // params := types.DefaultParams() - // params.EpochLength = 10 // Make height 10 a checkpoint - // require.NoError(t, keeper.SetParams(ctx, params)) - // }, - // msg: &types.MsgAttest{ - // Validator: sdk.ValAddress("validator1").String(), - // Height: 11, // Not a checkpoint height - // Vote: []byte("mock_vote"), - // }, - // expErr: sdkerrors.ErrInvalidRequest, - //}, } - for name, spec := range specs { t.Run(name, func(t *testing.T) { - rollkitPrefixStore := kt.Wrap(ds.NewMapDatastore(), &kt.PrefixTransform{ - Prefix: ds.NewKey(rollnode.RollkitPrefix), - }) - bs := rstore.New(rollkitPrefixStore) - // Set up codec and store - cdc := moduletestutil.MakeTestEncodingConfig().Codec - keys := storetypes.NewKVStoreKeys(types.StoreKey) - - // Set up context and keeper - logger := log.NewTestLogger(t) - cms := integration.CreateMultiStore(keys, logger) - authority := authtypes.NewModuleAddress("gov") - sk := NewMockStakingKeeper() - keeper := NewKeeper(cdc, runtime.NewKVStoreService(keys[types.StoreKey]), &sk, nil, nil, bs, authority.String()) - server := NewMsgServerImpl(keeper) - - ctx := sdk.NewContext(cms, cmtproto.Header{ChainID: "test-chain", Time: time.Now().UTC(), Height: 20}, false, logger). - WithContext(t.Context()) - - // Run setup - spec.setup(t, ctx, &keeper, &sk, bs) + // Create a new environment for each test case with a cached context + testEnv := env + testEnv.Ctx, _ = parentCtx.CacheContext() + testEnv = spec.setup(t, testEnv) // when - gotRsp, gotErr := server.Attest(ctx, spec.msg) + srcMsg := spec.msg(t) + gotRsp, gotErr := testEnv.Server.Attest(testEnv.Ctx, srcMsg) // then if spec.expErr != nil { require.Error(t, gotErr) require.ErrorIs(t, gotErr, spec.expErr) // and ensure the signature is not stored - _, err := keeper.GetSignature(ctx, spec.msg.Height, valAddrStr) + _, err := testEnv.Keeper.GetSignature(testEnv.Ctx, srcMsg.Height, valAddrStr) assert.ErrorIs(t, err, collections.ErrNotFound) return } @@ -285,127 +258,158 @@ func TestAttest(t *testing.T) { require.NotNil(t, gotRsp) // and attestation marked - bitmap, gotErr := keeper.GetAttestationBitmap(ctx, spec.msg.Height) + bitmap, gotErr := testEnv.Keeper.GetAttestationBitmap(testEnv.Ctx, srcMsg.Height) require.NoError(t, gotErr) require.NotEmpty(t, bitmap) require.Equal(t, byte(1), bitmap[0]) // and the signature was stored properly - gotSig, err := keeper.GetSignature(ctx, spec.msg.Height, valAddrStr) + gotSig, err := testEnv.Keeper.GetSignature(testEnv.Ctx, srcMsg.Height, valAddrStr) require.NoError(t, err) var vote cmtproto.Vote - require.NoError(t, proto.Unmarshal(spec.msg.Vote, &vote)) + require.NoError(t, proto.Unmarshal(srcMsg.Vote, &vote)) assert.Equal(t, vote.Signature, gotSig) }) } } -func HeaderFixture(signer *ed25519.PrivKey, myAppHash []byte, mutators ...func(*rollkittypes.SignedHeader)) *rollkittypes.SignedHeader { - header := rollkittypes.Header{ - BaseHeader: rollkittypes.BaseHeader{ - Height: 10, - Time: uint64(time.Now().UnixNano()), - ChainID: "testing", - }, - Version: rollkittypes.Version{Block: 1, App: 1}, - ProposerAddress: signer.PubKey().Address(), - AppHash: myAppHash, - DataHash: []byte("data_hash"), - ConsensusHash: []byte("consensus_hash"), - ValidatorHash: []byte("validator_hash"), - } - signedHeader := &rollkittypes.SignedHeader{ - Header: header, - Signature: myAppHash, - Signer: rollkittypes.Signer{PubKey: must(crypto.UnmarshalEd25519PublicKey(signer.PubKey().Bytes()))}, - } - for _, m := range mutators { - m(signedHeader) - } - return signedHeader -} +func TestVerifyVote(t *testing.T) { + var ( + myHash = sha256.Sum256([]byte("app_hash")) + myAppHash = myHash[:] + validatorPrivKey = ed25519.GenPrivKey() + valAddrStr = sdk.ValAddress(validatorPrivKey.PubKey().Address()).String() + ) -func VoteFixture(myAppHash []byte, voteSigner *ed25519.PrivKey, mutators ...func(vote *cmtproto.Vote)) *cmtproto.Vote { - const chainID = "testing" - - vote := &cmtproto.Vote{ - Type: cmtproto.PrecommitType, - Height: 10, - Round: 0, - BlockID: cmtproto.BlockID{Hash: myAppHash, PartSetHeader: cmtproto.PartSetHeader{Total: 1, Hash: myAppHash}}, - Timestamp: time.Now(), - ValidatorAddress: voteSigner.PubKey().Address(), - ValidatorIndex: 0, - } - vote.Signature = must(voteSigner.Sign(cmttypes.VoteSignBytes(chainID, vote))) + // Setup test environment with block store + env := setupTestEnv(t, 10) - for _, m := range mutators { - m(vote) - } - return vote -} + // Set up validator + validator, err := stakingtypes.NewValidator(valAddrStr, validatorPrivKey.PubKey(), stakingtypes.Description{}) + require.NoError(t, err) + validator.Status = stakingtypes.Bonded + require.NoError(t, env.SK.SetValidator(env.Ctx, validator)) -var _ types.StakingKeeper = &MockStakingKeeper{} + // Save block data + header := HeaderFixture(validatorPrivKey, myAppHash) + var signature rollkittypes.Signature + require.NoError(t, env.BlockStore.SaveBlockData(env.Ctx, header, &rollkittypes.Data{}, &signature)) -type MockStakingKeeper struct { - activeSet map[string]stakingtypes.Validator - pubKeys map[string]cryptotypes.PubKey -} + parentCtx := env.Ctx -func NewMockStakingKeeper() MockStakingKeeper { - return MockStakingKeeper{ - activeSet: make(map[string]stakingtypes.Validator), - pubKeys: make(map[string]cryptotypes.PubKey), + testCases := map[string]struct { + voteFn func(t *testing.T) *cmtproto.Vote + expErr error + }{ + "valid vote": { + voteFn: func(t *testing.T) *cmtproto.Vote { + return VoteFixture(myAppHash, validatorPrivKey) + }, + }, + "timestamp drift": { + voteFn: func(t *testing.T) *cmtproto.Vote { + return VoteFixture(myAppHash, validatorPrivKey, func(vote *cmtproto.Vote) { + vote.Timestamp = time.Now().Add(6 * time.Second) + vote.Signature = must(validatorPrivKey.Sign(cmttypes.VoteSignBytes("testing", vote))) + }) + }, + expErr: sdkerrors.ErrInvalidRequest, + }, + "block data not found": { + voteFn: func(t *testing.T) *cmtproto.Vote { + return VoteFixture(myAppHash, validatorPrivKey, func(vote *cmtproto.Vote) { + vote.Height++ + vote.Signature = must(validatorPrivKey.Sign(cmttypes.VoteSignBytes("testing", vote))) + }) + }, + expErr: sdkerrors.ErrInvalidRequest, + }, + "vote and block hash mismatch": { + voteFn: func(t *testing.T) *cmtproto.Vote { + hash := sha256.Sum256([]byte("wrong_hash")) + return VoteFixture(hash[:], validatorPrivKey) + }, + expErr: sdkerrors.ErrInvalidRequest, + }, + "validator not found": { + voteFn: func(t *testing.T) *cmtproto.Vote { + return VoteFixture(myAppHash, ed25519.GenPrivKey()) + }, + expErr: sdkerrors.ErrNotFound, + }, + "invalid vote signature": { + voteFn: func(t *testing.T) *cmtproto.Vote { + return VoteFixture(myAppHash, validatorPrivKey, func(vote *cmtproto.Vote) { + vote.Signature = []byte("invalid signature") + }) + }, + expErr: sdkerrors.ErrInvalidRequest, + }, } -} - -func (m *MockStakingKeeper) SetValidator(ctx context.Context, validator stakingtypes.Validator) error { - m.activeSet[validator.GetOperator()] = validator - return nil -} - -func (m MockStakingKeeper) GetAllValidators(ctx context.Context) (validators []stakingtypes.Validator, err error) { - return slices.SortedFunc(maps.Values(m.activeSet), func(v1 stakingtypes.Validator, v2 stakingtypes.Validator) int { - return strings.Compare(v1.OperatorAddress, v2.OperatorAddress) - }), nil -} + for name, spec := range testCases { + t.Run(name, func(t *testing.T) { + ctx, _ := parentCtx.CacheContext() -func (m MockStakingKeeper) GetValidator(ctx context.Context, addr sdk.ValAddress) (validator stakingtypes.Validator, err error) { - // First try to find the validator by address - validator, found := m.activeSet[addr.String()] - if found { - return validator, nil - } + // when + vote, err := env.Server.verifyVote(ctx, &types.MsgAttest{Height: 10, Vote: must(proto.Marshal(spec.voteFn(t)))}) - // If not found by address, try to find by public key address - addrStr := addr.String() - for valAddrStr, pubKey := range m.pubKeys { - if pubKey.Address().String() == addrStr { - validator, found = m.activeSet[valAddrStr] - if found { - return validator, nil + // then + if spec.expErr != nil { + require.Error(t, err) + require.ErrorIs(t, err, spec.expErr) + require.Nil(t, vote) + return } - } - } - - return validator, sdkerrors.ErrNotFound -} - -func (m MockStakingKeeper) GetLastValidators(ctx context.Context) (validators []stakingtypes.Validator, err error) { - for _, validator := range m.activeSet { - if validator.IsBonded() { // Assuming IsBonded() identifies if a validator is in the last validators - validators = append(validators, validator) - } + require.NoError(t, err) + require.NotNil(t, vote) + }) } - return } -func (m MockStakingKeeper) GetLastTotalPower(ctx context.Context) (math.Int, error) { - return math.NewInt(int64(len(m.activeSet))), nil +// testEnv contains all the common components needed for testing +type testEnv struct { + Ctx sdk.Context + Keeper Keeper + Server msgServer + SK MockStakingKeeper + BlockStore rstore.Store } -func (m *MockStakingKeeper) SetValidatorPubKey(valAddrStr string, key cryptotypes.PubKey) { - m.pubKeys[valAddrStr] = key +func setupTestEnv(t *testing.T, height int64) testEnv { + t.Helper() + // Set up codec and store + cdc := moduletestutil.MakeTestEncodingConfig().Codec + keys := storetypes.NewKVStoreKeys(types.StoreKey) + cms := integration.CreateMultiStore(keys, log.NewTestLogger(t)) + + sk := NewMockStakingKeeper() + rollkitPrefixStore := kt.Wrap(ds.NewMapDatastore(), &kt.PrefixTransform{ + Prefix: ds.NewKey(rollnode.RollkitPrefix), + }) + bs := rstore.New(rollkitPrefixStore) + + authority := authtypes.NewModuleAddress("gov") + keeper := NewKeeper(cdc, runtime.NewKVStoreService(keys[types.StoreKey]), &sk, nil, nil, bs, authority.String()) + + ctx := sdk.NewContext(cms, cmtproto.Header{ + ChainID: "testing", + Time: time.Now().UTC(), + Height: height, + }, false, log.NewTestLogger(t)).WithContext(t.Context()) + + server := msgServer{Keeper: keeper} + + params := types.DefaultParams() + params.EpochLength = 10 // test default + require.NoError(t, keeper.SetParams(ctx, params)) + + return testEnv{ + Ctx: ctx, + Keeper: keeper, + Server: server, + SK: sk, + BlockStore: bs, + } } func must[T any](r T, err error) T { diff --git a/modules/network/types/expected_keepers.go b/modules/network/types/expected_keepers.go index b9def7f3..a356b9aa 100644 --- a/modules/network/types/expected_keepers.go +++ b/modules/network/types/expected_keepers.go @@ -2,11 +2,12 @@ package types import ( "context" - tyrollkittypes "github.com/rollkit/rollkit/types" "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + + tyrollkittypes "github.com/rollkit/rollkit/types" ) // StakingKeeper defines the expected staking keeper interface From 866e6251d4ca7b188580becdec7824c1d07ce2c5 Mon Sep 17 00:00:00 2001 From: Alex Peters Date: Mon, 30 Jun 2025 10:49:10 +0200 Subject: [PATCH 12/17] Fix merge issues --- modules/network/keeper/fixtures_test.go | 6 +-- modules/network/keeper/keeper.go | 3 ++ modules/network/keeper/msg_server.go | 2 +- modules/network/keeper/msg_server_test.go | 56 +++++++++++------------ 4 files changed, 35 insertions(+), 32 deletions(-) diff --git a/modules/network/keeper/fixtures_test.go b/modules/network/keeper/fixtures_test.go index 12d3b273..19598829 100644 --- a/modules/network/keeper/fixtures_test.go +++ b/modules/network/keeper/fixtures_test.go @@ -21,7 +21,7 @@ import ( "github.com/rollkit/go-execution-abci/modules/network/types" ) -func HeaderFixture(signer *ed25519.PrivKey, myAppHash []byte, mutators ...func(*rollkittypes.SignedHeader)) *rollkittypes.SignedHeader { +func HeaderFixture(signer *ed25519.PrivKey, appHash []byte, mutators ...func(*rollkittypes.SignedHeader)) *rollkittypes.SignedHeader { header := rollkittypes.Header{ BaseHeader: rollkittypes.BaseHeader{ Height: 10, @@ -30,14 +30,14 @@ func HeaderFixture(signer *ed25519.PrivKey, myAppHash []byte, mutators ...func(* }, Version: rollkittypes.Version{Block: 1, App: 1}, ProposerAddress: signer.PubKey().Address(), - AppHash: myAppHash, + AppHash: appHash, DataHash: []byte("data_hash"), ConsensusHash: []byte("consensus_hash"), ValidatorHash: []byte("validator_hash"), } signedHeader := &rollkittypes.SignedHeader{ Header: header, - Signature: myAppHash, + Signature: appHash, Signer: rollkittypes.Signer{PubKey: must(crypto.UnmarshalEd25519PublicKey(signer.PubKey().Bytes()))}, } for _, m := range mutators { diff --git a/modules/network/keeper/keeper.go b/modules/network/keeper/keeper.go index 05d22a85..a94925c7 100644 --- a/modules/network/keeper/keeper.go +++ b/modules/network/keeper/keeper.go @@ -22,6 +22,7 @@ type Keeper struct { bankKeeper types.BankKeeper authority string bitmapHelper *BitmapHelper + blockSource types.BlockSource // Collections for state management ValidatorIndex collections.Map[string, uint16] @@ -42,6 +43,7 @@ func NewKeeper( sk types.StakingKeeper, ak types.AccountKeeper, bk types.BankKeeper, + blockSource types.BlockSource, authority string, ) Keeper { @@ -53,6 +55,7 @@ func NewKeeper( bankKeeper: bk, authority: authority, bitmapHelper: NewBitmapHelper(), + blockSource: blockSource, ValidatorIndex: collections.NewMap(sb, types.ValidatorIndexPrefix, "validator_index", collections.StringKey, collections.Uint16Value), ValidatorPower: collections.NewMap(sb, types.ValidatorPowerPrefix, "validator_power", collections.Uint16Key, collections.Uint64Value), diff --git a/modules/network/keeper/msg_server.go b/modules/network/keeper/msg_server.go index 0428ce7b..9b71baa6 100644 --- a/modules/network/keeper/msg_server.go +++ b/modules/network/keeper/msg_server.go @@ -60,7 +60,7 @@ func (k msgServer) Attest(goCtx context.Context, msg *types.MsgAttest) (*types.M return nil, errors.Wrap(err, "store signature") } - if err := k.updateEpochBitmap(ctx, k.GetEpochForHeight(ctx, msg.Height), valIndexPos); err != nil { + if err := k.updateEpochBitmap(ctx, uint64(msg.Height), valIndexPos); err != nil { return nil, err } diff --git a/modules/network/keeper/msg_server_test.go b/modules/network/keeper/msg_server_test.go index e78488cb..90f60185 100644 --- a/modules/network/keeper/msg_server_test.go +++ b/modules/network/keeper/msg_server_test.go @@ -122,12 +122,10 @@ func TestJoinAttesterSet(t *testing.T) { func TestAttest(t *testing.T) { const epochLength = 10 var ( - myHash = sha256.Sum256([]byte("app_hash")) - myAppHash = myHash[:] - voteSigner = ed25519.GenPrivKey() - valAddrStr = sdk.ValAddress(voteSigner.PubKey().Address()).String() - validVote = VoteFixture(myAppHash, voteSigner) - validVoteBz = must(proto.Marshal(validVote)) + myHash = sha256.Sum256([]byte("app_hash")) + myAppHash = myHash[:] + voteSigner = ed25519.GenPrivKey() + valAddrStr = sdk.ValAddress(voteSigner.PubKey().Address()).String() ) // Setup test environment with block store @@ -145,28 +143,32 @@ func TestAttest(t *testing.T) { var signature rollkittypes.Signature require.NoError(t, env.BlockStore.SaveBlockData(env.Ctx, signedHeader, data, &signature)) + var ( + validVote = VoteFixture(myAppHash, voteSigner) + validVoteBz = must(proto.Marshal(validVote)) + ) parentCtx := env.Ctx specs := map[string]struct { - setup func(t *testing.T, env testEnv) testEnv + setup func(t *testing.T, env testEnv) sdk.Context msg func(t *testing.T) *types.MsgAttest expErr error }{ "valid attestation": { - setup: func(t *testing.T, env testEnv) testEnv { + setup: func(t *testing.T, env testEnv) sdk.Context { require.NoError(t, env.Keeper.SetAttesterSetMember(env.Ctx, valAddrStr)) require.NoError(t, env.Keeper.SetValidatorIndex(env.Ctx, valAddrStr, 0, 100)) - return env + return env.Ctx }, msg: func(t *testing.T) *types.MsgAttest { return &types.MsgAttest{Validator: valAddrStr, Height: epochLength, Vote: validVoteBz} }, }, "invalid vote content": { - setup: func(t *testing.T, env testEnv) testEnv { + setup: func(t *testing.T, env testEnv) sdk.Context { require.NoError(t, env.Keeper.SetAttesterSetMember(env.Ctx, valAddrStr)) require.NoError(t, env.Keeper.SetValidatorIndex(env.Ctx, valAddrStr, 0, 100)) - return env + return env.Ctx }, msg: func(t *testing.T) *types.MsgAttest { return &types.MsgAttest{Validator: valAddrStr, Height: epochLength, Vote: []byte("not a valid proto vote")} @@ -174,9 +176,9 @@ func TestAttest(t *testing.T) { expErr: sdkerrors.ErrInvalidRequest, }, "validator not in attester set": { - setup: func(t *testing.T, env testEnv) testEnv { + setup: func(t *testing.T, env testEnv) sdk.Context { require.NoError(t, env.Keeper.SetValidatorIndex(env.Ctx, valAddrStr, 0, 100)) - return env + return env.Ctx }, msg: func(t *testing.T) *types.MsgAttest { return &types.MsgAttest{Validator: valAddrStr, Height: epochLength, Vote: validVoteBz} @@ -184,10 +186,10 @@ func TestAttest(t *testing.T) { expErr: sdkerrors.ErrUnauthorized, }, "invalid signature": { - setup: func(t *testing.T, env testEnv) testEnv { + setup: func(t *testing.T, env testEnv) sdk.Context { require.NoError(t, env.Keeper.SetAttesterSetMember(env.Ctx, valAddrStr)) require.NoError(t, env.Keeper.SetValidatorIndex(env.Ctx, valAddrStr, 0, 100)) - return env + return env.Ctx }, msg: func(t *testing.T) *types.MsgAttest { invalidVote := VoteFixture(myAppHash, voteSigner, func(vote *cmtproto.Vote) { @@ -198,10 +200,10 @@ func TestAttest(t *testing.T) { expErr: sdkerrors.ErrInvalidRequest, }, "not a checkpoint height": { - setup: func(t *testing.T, env testEnv) testEnv { + setup: func(t *testing.T, env testEnv) sdk.Context { require.NoError(t, env.Keeper.SetAttesterSetMember(env.Ctx, valAddrStr)) require.NoError(t, env.Keeper.SetValidatorIndex(env.Ctx, valAddrStr, 0, 100)) - return env + return env.Ctx }, msg: func(t *testing.T) *types.MsgAttest { return &types.MsgAttest{Validator: valAddrStr, Height: epochLength + 1, Vote: validVoteBz} @@ -209,11 +211,10 @@ func TestAttest(t *testing.T) { expErr: sdkerrors.ErrInvalidRequest, }, "vote window expired": { - setup: func(t *testing.T, env testEnv) testEnv { + setup: func(t *testing.T, env testEnv) sdk.Context { require.NoError(t, env.Keeper.SetAttesterSetMember(env.Ctx, valAddrStr)) require.NoError(t, env.Keeper.SetValidatorIndex(env.Ctx, valAddrStr, 0, 100)) - env.Ctx = env.Ctx.WithBlockHeight(2*epochLength + 1) - return env + return env.Ctx.WithBlockHeight(2*epochLength + 1) }, msg: func(t *testing.T) *types.MsgAttest { return &types.MsgAttest{Validator: valAddrStr, Height: epochLength, Vote: validVoteBz} @@ -221,11 +222,10 @@ func TestAttest(t *testing.T) { expErr: sdkerrors.ErrInvalidRequest, }, "voting for a future epoch": { - setup: func(t *testing.T, env testEnv) testEnv { + setup: func(t *testing.T, env testEnv) sdk.Context { require.NoError(t, env.Keeper.SetAttesterSetMember(env.Ctx, valAddrStr)) require.NoError(t, env.Keeper.SetValidatorIndex(env.Ctx, valAddrStr, 0, 100)) - env.Ctx = env.Ctx.WithBlockHeight(2 * epochLength) - return env + return env.Ctx.WithBlockHeight(2 * epochLength) }, msg: func(t *testing.T) *types.MsgAttest { return &types.MsgAttest{Validator: valAddrStr, Height: 3 * epochLength, Vote: validVoteBz} @@ -238,18 +238,18 @@ func TestAttest(t *testing.T) { // Create a new environment for each test case with a cached context testEnv := env testEnv.Ctx, _ = parentCtx.CacheContext() - testEnv = spec.setup(t, testEnv) + ctx := spec.setup(t, testEnv) // when srcMsg := spec.msg(t) - gotRsp, gotErr := testEnv.Server.Attest(testEnv.Ctx, srcMsg) + gotRsp, gotErr := testEnv.Server.Attest(ctx, srcMsg) // then if spec.expErr != nil { require.Error(t, gotErr) require.ErrorIs(t, gotErr, spec.expErr) // and ensure the signature is not stored - _, err := testEnv.Keeper.GetSignature(testEnv.Ctx, srcMsg.Height, valAddrStr) + _, err := testEnv.Keeper.GetSignature(ctx, srcMsg.Height, valAddrStr) assert.ErrorIs(t, err, collections.ErrNotFound) return } @@ -258,13 +258,13 @@ func TestAttest(t *testing.T) { require.NotNil(t, gotRsp) // and attestation marked - bitmap, gotErr := testEnv.Keeper.GetAttestationBitmap(testEnv.Ctx, srcMsg.Height) + bitmap, gotErr := testEnv.Keeper.GetAttestationBitmap(ctx, srcMsg.Height) require.NoError(t, gotErr) require.NotEmpty(t, bitmap) require.Equal(t, byte(1), bitmap[0]) // and the signature was stored properly - gotSig, err := testEnv.Keeper.GetSignature(testEnv.Ctx, srcMsg.Height, valAddrStr) + gotSig, err := testEnv.Keeper.GetSignature(ctx, srcMsg.Height, valAddrStr) require.NoError(t, err) var vote cmtproto.Vote require.NoError(t, proto.Unmarshal(srcMsg.Vote, &vote)) From 9e31851f0dde80ad56e608d1c5edce20c3596c3b Mon Sep 17 00:00:00 2001 From: Alex Peters Date: Mon, 30 Jun 2025 13:25:41 +0200 Subject: [PATCH 13/17] Configurable max clock drift and minor updates --- modules/network/keeper/msg_server.go | 20 ++- modules/network/keeper/msg_server_test.go | 31 +++- modules/network/types/params.go | 7 + modules/network/types/types.pb.go | 134 +++++++++++++----- .../proto/rollkitsdk/network/v1/types.proto | 7 + 5 files changed, 150 insertions(+), 49 deletions(-) diff --git a/modules/network/keeper/msg_server.go b/modules/network/keeper/msg_server.go index 9b71baa6..cc3bff0d 100644 --- a/modules/network/keeper/msg_server.go +++ b/modules/network/keeper/msg_server.go @@ -47,15 +47,15 @@ func (k msgServer) Attest(goCtx context.Context, msg *types.MsgAttest) (*types.M return nil, errors.Wrapf(sdkerrors.ErrNotFound, "validator index not found for %s", msg.Validator) } - if err := k.updateAttestationBitmap(ctx, msg, valIndexPos); err != nil { - return nil, errors.Wrap(err, "update attestation bitmap") - } - vote, err := k.verifyVote(ctx, msg) if err != nil { return nil, err } + if err := k.updateAttestationBitmap(ctx, msg, valIndexPos); err != nil { + return nil, errors.Wrap(err, "update attestation bitmap") + } + if err := k.SetSignature(ctx, msg.Height, msg.Validator, vote.Signature); err != nil { return nil, errors.Wrap(err, "store signature") } @@ -156,6 +156,14 @@ func (k msgServer) verifyVote(ctx sdk.Context, msg *types.MsgAttest) (*cmtproto. if msg.Height != vote.Height { return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "vote height does not match attestation height") } + if senderAddr, err := sdk.ValAddressFromBech32(msg.Validator); err != nil { + return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid validator address: %s", err) + } else if !bytes.Equal(senderAddr, vote.ValidatorAddress) { + return nil, errors.Wrapf(sdkerrors.ErrUnauthorized, "vote validator address does not match attestation validator address") + } + if len(vote.Signature) == 0 { + return nil, sdkerrors.ErrInvalidRequest.Wrap("empty signature") + } header, _, err := k.blockSource.GetBlockData(ctx, uint64(vote.Height)) if err != nil { return nil, errors.Wrapf(err, "block data for height %d", vote.Height) @@ -168,8 +176,8 @@ func (k msgServer) verifyVote(ctx sdk.Context, msg *types.MsgAttest) (*cmtproto. return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "vote block ID hash does not match app hash for height %d", vote.Height) } - maxClockDrift := 5 * time.Second // todo (Alex): make this a parameter? - if drift := vote.Timestamp.Sub(header.Time()); drift < 0*time.Nanosecond || drift > maxClockDrift { + maxClockDrift := time.Duration(k.GetParams(ctx).MaxClockDrift) + if drift := vote.Timestamp.Sub(header.Time()); drift < 0 || drift > maxClockDrift { return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "vote timestamp drift exceeds limit: %s", drift) } diff --git a/modules/network/keeper/msg_server_test.go b/modules/network/keeper/msg_server_test.go index 90f60185..e44a5ff2 100644 --- a/modules/network/keeper/msg_server_test.go +++ b/modules/network/keeper/msg_server_test.go @@ -275,10 +275,11 @@ func TestAttest(t *testing.T) { func TestVerifyVote(t *testing.T) { var ( - myHash = sha256.Sum256([]byte("app_hash")) - myAppHash = myHash[:] - validatorPrivKey = ed25519.GenPrivKey() - valAddrStr = sdk.ValAddress(validatorPrivKey.PubKey().Address()).String() + myHash = sha256.Sum256([]byte("app_hash")) + myAppHash = myHash[:] + validatorPrivKey = ed25519.GenPrivKey() + valAddrStr = sdk.ValAddress(validatorPrivKey.PubKey().Address()).String() + otherValidatorPrivKey = ed25519.GenPrivKey() ) // Setup test environment with block store @@ -299,12 +300,14 @@ func TestVerifyVote(t *testing.T) { testCases := map[string]struct { voteFn func(t *testing.T) *cmtproto.Vote + sender string expErr error }{ "valid vote": { voteFn: func(t *testing.T) *cmtproto.Vote { return VoteFixture(myAppHash, validatorPrivKey) }, + sender: valAddrStr, }, "timestamp drift": { voteFn: func(t *testing.T) *cmtproto.Vote { @@ -313,6 +316,7 @@ func TestVerifyVote(t *testing.T) { vote.Signature = must(validatorPrivKey.Sign(cmttypes.VoteSignBytes("testing", vote))) }) }, + sender: valAddrStr, expErr: sdkerrors.ErrInvalidRequest, }, "block data not found": { @@ -322,6 +326,7 @@ func TestVerifyVote(t *testing.T) { vote.Signature = must(validatorPrivKey.Sign(cmttypes.VoteSignBytes("testing", vote))) }) }, + sender: valAddrStr, expErr: sdkerrors.ErrInvalidRequest, }, "vote and block hash mismatch": { @@ -329,13 +334,15 @@ func TestVerifyVote(t *testing.T) { hash := sha256.Sum256([]byte("wrong_hash")) return VoteFixture(hash[:], validatorPrivKey) }, + sender: valAddrStr, expErr: sdkerrors.ErrInvalidRequest, }, "validator not found": { voteFn: func(t *testing.T) *cmtproto.Vote { return VoteFixture(myAppHash, ed25519.GenPrivKey()) }, - expErr: sdkerrors.ErrNotFound, + expErr: sdkerrors.ErrUnauthorized, + sender: sdk.ValAddress(otherValidatorPrivKey.PubKey().Address()).String(), }, "invalid vote signature": { voteFn: func(t *testing.T) *cmtproto.Vote { @@ -343,15 +350,27 @@ func TestVerifyVote(t *testing.T) { vote.Signature = []byte("invalid signature") }) }, + sender: valAddrStr, expErr: sdkerrors.ErrInvalidRequest, }, + "invalid sender": { + voteFn: func(t *testing.T) *cmtproto.Vote { + return VoteFixture(myAppHash, validatorPrivKey) + }, + sender: sdk.ValAddress(otherValidatorPrivKey.PubKey().Address()).String(), + expErr: sdkerrors.ErrUnauthorized, + }, } for name, spec := range testCases { t.Run(name, func(t *testing.T) { ctx, _ := parentCtx.CacheContext() // when - vote, err := env.Server.verifyVote(ctx, &types.MsgAttest{Height: 10, Vote: must(proto.Marshal(spec.voteFn(t)))}) + vote, err := env.Server.verifyVote(ctx, &types.MsgAttest{ + Height: 10, + Validator: spec.sender, + Vote: must(proto.Marshal(spec.voteFn(t))), + }) // then if spec.expErr != nil { diff --git a/modules/network/types/params.go b/modules/network/types/params.go index f7a68664..ef57cf70 100644 --- a/modules/network/types/params.go +++ b/modules/network/types/params.go @@ -2,6 +2,7 @@ package types import ( "fmt" + "time" "cosmossdk.io/math" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" @@ -32,6 +33,7 @@ func NewParams( minParticipation math.LegacyDec, pruneAfter uint64, signMode SignMode, + maxClockDrift time.Duration, ) Params { return Params{ EpochLength: epochLength, @@ -39,6 +41,7 @@ func NewParams( MinParticipation: minParticipation.String(), PruneAfter: pruneAfter, SignMode: signMode, + MaxClockDrift: maxClockDrift, } } @@ -50,6 +53,7 @@ func DefaultParams() Params { DefaultMinParticipation, DefaultPruneAfter, DefaultSignMode, + 5*time.Second, ) } @@ -81,6 +85,9 @@ func (p Params) Validate() error { if err := validateSignMode(p.SignMode); err != nil { return err } + if p.MaxClockDrift < 0 { + return fmt.Errorf("max clock drift must be positive: %s", p.MaxClockDrift) + } return nil } diff --git a/modules/network/types/types.pb.go b/modules/network/types/types.pb.go index a95a6c79..e57e2f51 100644 --- a/modules/network/types/types.pb.go +++ b/modules/network/types/types.pb.go @@ -6,18 +6,22 @@ package types import ( fmt "fmt" _ "github.com/cosmos/cosmos-proto" + _ "github.com/cosmos/cosmos-sdk/types/tx/amino" _ "github.com/cosmos/gogoproto/gogoproto" proto "github.com/cosmos/gogoproto/proto" + github_com_cosmos_gogoproto_types "github.com/cosmos/gogoproto/types" _ "google.golang.org/protobuf/types/known/durationpb" io "io" math "math" math_bits "math/bits" + time "time" ) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf +var _ = time.Kitchen // This is a compile-time assertion to ensure that this generated file // is compatible with the proto package it is being compiled against. @@ -65,6 +69,8 @@ type Params struct { PruneAfter uint64 `protobuf:"varint,4,opt,name=prune_after,json=pruneAfter,proto3" json:"prune_after,omitempty"` // sign_mode determines when validators must sign SignMode SignMode `protobuf:"varint,5,opt,name=sign_mode,json=signMode,proto3,enum=rollkitsdk.network.v1.SignMode" json:"sign_mode,omitempty"` + // max_clock_drift maximum a timestamp for a vote can drift + MaxClockDrift time.Duration `protobuf:"bytes,6,opt,name=max_clock_drift,json=maxClockDrift,proto3,stdduration" json:"max_clock_drift"` } func (m *Params) Reset() { *m = Params{} } @@ -135,6 +141,13 @@ func (m *Params) GetSignMode() SignMode { return SignMode_SIGN_MODE_UNSPECIFIED } +func (m *Params) GetMaxClockDrift() time.Duration { + if m != nil { + return m.MaxClockDrift + } + return 0 +} + // AttestationBitmap stores the bitmap and metadata for a checkpoint type AttestationBitmap struct { // height is the checkpoint height @@ -291,43 +304,47 @@ func init() { func init() { proto.RegisterFile("rollkitsdk/network/v1/types.proto", fileDescriptor_6ae6db90f9054c5e) } var fileDescriptor_6ae6db90f9054c5e = []byte{ - // 562 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x93, 0x4f, 0x6f, 0xd3, 0x30, - 0x14, 0xc0, 0x9b, 0xfd, 0xa3, 0xf5, 0xb6, 0xd2, 0x45, 0x1d, 0xca, 0x76, 0xc8, 0xba, 0x4a, 0x48, - 0x15, 0x52, 0x13, 0x6d, 0x1c, 0x90, 0x00, 0x09, 0xf5, 0xdf, 0xa0, 0x82, 0x75, 0x55, 0x3a, 0x38, - 0x70, 0x89, 0xdc, 0xc4, 0x4d, 0xad, 0x26, 0x79, 0xc1, 0x76, 0xba, 0x71, 0xe3, 0x23, 0xf0, 0x41, - 0x38, 0xee, 0x43, 0x70, 0x9c, 0x76, 0xe2, 0x88, 0xda, 0x2f, 0x82, 0xe2, 0xb8, 0x6c, 0x87, 0xdd, - 0xfc, 0x7e, 0xef, 0xe7, 0xf8, 0xbd, 0xe7, 0x18, 0x1d, 0x33, 0x08, 0xc3, 0x19, 0x15, 0xdc, 0x9f, - 0xd9, 0x31, 0x11, 0x57, 0xc0, 0x66, 0xf6, 0xfc, 0xc4, 0x16, 0xdf, 0x13, 0xc2, 0xad, 0x84, 0x81, - 0x00, 0x7d, 0xff, 0x5e, 0xb1, 0x94, 0x62, 0xcd, 0x4f, 0x0e, 0xab, 0x01, 0x04, 0x20, 0x0d, 0x3b, - 0x5b, 0xe5, 0xf2, 0xe1, 0x81, 0x07, 0x3c, 0x02, 0xee, 0xe6, 0x89, 0x3c, 0x50, 0x29, 0x33, 0x00, - 0x08, 0x42, 0x62, 0xcb, 0x68, 0x9c, 0x4e, 0x6c, 0x3f, 0x65, 0x58, 0x50, 0x88, 0xf3, 0x7c, 0xfd, - 0xc7, 0x1a, 0xda, 0x1a, 0x62, 0x86, 0x23, 0xae, 0x1f, 0xa3, 0x1d, 0x92, 0x80, 0x37, 0x75, 0x43, - 0x12, 0x07, 0x62, 0x6a, 0x68, 0x35, 0xad, 0xb1, 0xe1, 0x6c, 0x4b, 0xf6, 0x49, 0x22, 0xfd, 0x15, - 0x7a, 0xfa, 0x2d, 0x05, 0x96, 0x46, 0xee, 0x84, 0x61, 0x2f, 0xfb, 0x8c, 0xb1, 0x56, 0xd3, 0x1a, - 0xa5, 0x76, 0xf9, 0xee, 0xa6, 0x89, 0xd4, 0xc1, 0x5d, 0xe2, 0x39, 0xe5, 0x5c, 0x3b, 0x53, 0x96, - 0xfe, 0x06, 0xed, 0x45, 0x34, 0x76, 0x13, 0xcc, 0x04, 0xf5, 0x68, 0x22, 0x2b, 0x30, 0xd6, 0x1f, - 0xdd, 0x5a, 0x89, 0x68, 0x3c, 0x7c, 0xe8, 0xe9, 0x47, 0x68, 0x3b, 0x61, 0x69, 0x4c, 0x5c, 0x3c, - 0x11, 0x84, 0x19, 0x1b, 0xb2, 0x2e, 0x24, 0x51, 0x2b, 0x23, 0xfa, 0x5b, 0x54, 0xe2, 0x34, 0x88, - 0xdd, 0x08, 0x7c, 0x62, 0x6c, 0xd6, 0xb4, 0x46, 0xf9, 0xf4, 0xc8, 0x7a, 0x74, 0x80, 0xd6, 0x88, - 0x06, 0xf1, 0x39, 0xf8, 0xc4, 0x29, 0x72, 0xb5, 0xaa, 0xff, 0xd2, 0xd0, 0x5e, 0x4b, 0x08, 0xc2, - 0x85, 0x3c, 0xae, 0x4d, 0x45, 0x84, 0x13, 0xfd, 0x19, 0xda, 0x9a, 0x12, 0x1a, 0x4c, 0x85, 0x9c, - 0xc3, 0xba, 0xa3, 0xa2, 0x8c, 0x8f, 0xa5, 0x21, 0x3b, 0xdf, 0x71, 0x54, 0x94, 0x15, 0x39, 0x07, - 0x41, 0x7c, 0x37, 0x81, 0x2b, 0xc2, 0x64, 0x6f, 0x1b, 0x0e, 0x92, 0x68, 0x98, 0x91, 0x4c, 0x10, - 0x20, 0x70, 0xa8, 0x04, 0xd5, 0x85, 0x44, 0xb9, 0xf0, 0x1c, 0x95, 0x39, 0x4c, 0x84, 0xeb, 0x41, - 0x3c, 0xa1, 0x2c, 0x22, 0xbe, 0x6c, 0xa5, 0xe8, 0xec, 0x66, 0xb4, 0xb3, 0x82, 0xf5, 0x04, 0x95, - 0xbf, 0xe0, 0x90, 0xfa, 0x58, 0x00, 0xeb, 0xc7, 0x3e, 0xb9, 0xd6, 0x4f, 0xd1, 0x13, 0xec, 0xfb, - 0x8c, 0x70, 0x2e, 0x6b, 0x2d, 0xb5, 0x8d, 0xbb, 0x9b, 0x66, 0x55, 0x8d, 0xb4, 0x95, 0x67, 0x46, - 0x82, 0xd1, 0x38, 0x70, 0x56, 0xa2, 0x5e, 0x45, 0x9b, 0x34, 0xdb, 0x2c, 0xbb, 0xd8, 0x75, 0xf2, - 0x20, 0xa3, 0x0f, 0xcb, 0xcf, 0x83, 0x17, 0xef, 0x50, 0x71, 0x35, 0x36, 0xfd, 0x00, 0xed, 0x8f, - 0xfa, 0xef, 0x07, 0xee, 0xf9, 0x45, 0xb7, 0xe7, 0x7e, 0x1e, 0x8c, 0x86, 0xbd, 0x4e, 0xff, 0xac, - 0xdf, 0xeb, 0x56, 0x0a, 0xba, 0x81, 0xaa, 0xf7, 0xa9, 0xce, 0x87, 0x5e, 0xe7, 0xe3, 0xf0, 0xa2, - 0x3f, 0xb8, 0xac, 0x68, 0xed, 0xcb, 0xdf, 0x0b, 0x53, 0xbb, 0x5d, 0x98, 0xda, 0xdf, 0x85, 0xa9, - 0xfd, 0x5c, 0x9a, 0x85, 0xdb, 0xa5, 0x59, 0xf8, 0xb3, 0x34, 0x0b, 0x5f, 0x5f, 0x07, 0x54, 0x4c, - 0xd3, 0xb1, 0xe5, 0x41, 0x64, 0xab, 0x0b, 0xb3, 0x03, 0x68, 0x92, 0x6b, 0xe2, 0xa5, 0xd9, 0x65, - 0x34, 0xf1, 0xd8, 0xa3, 0x76, 0x04, 0x7e, 0x1a, 0x12, 0xfe, 0xff, 0xad, 0xc8, 0x87, 0x32, 0xde, - 0x92, 0x7f, 0xf0, 0xcb, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x77, 0x78, 0xad, 0x26, 0x4e, 0x03, - 0x00, 0x00, + // 630 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x93, 0xcd, 0x4e, 0xdb, 0x40, + 0x14, 0x85, 0x63, 0x7e, 0x52, 0x18, 0x48, 0x00, 0x2b, 0x54, 0x86, 0x85, 0x13, 0x90, 0x2a, 0x45, + 0x48, 0xb1, 0x05, 0x5d, 0x54, 0x6a, 0x2b, 0x55, 0xe4, 0x87, 0x36, 0x6a, 0x81, 0xc8, 0xa1, 0x5d, + 0x74, 0x63, 0x4d, 0xec, 0x89, 0x33, 0x8a, 0xed, 0xeb, 0x8e, 0xc7, 0x90, 0xbe, 0x45, 0x97, 0x7d, + 0x84, 0x2e, 0xba, 0xe8, 0x82, 0x87, 0x60, 0x89, 0x58, 0x75, 0xd5, 0x56, 0x64, 0xd1, 0xd7, 0xa8, + 0x66, 0x3c, 0x29, 0x2c, 0xd8, 0x44, 0x73, 0xce, 0xfd, 0x6e, 0x74, 0xe7, 0x5c, 0x0f, 0xda, 0x61, + 0x10, 0x86, 0x63, 0xca, 0x53, 0x7f, 0x6c, 0xc7, 0x84, 0x5f, 0x00, 0x1b, 0xdb, 0xe7, 0xfb, 0x36, + 0xff, 0x9c, 0x90, 0xd4, 0x4a, 0x18, 0x70, 0xd0, 0x37, 0xef, 0x10, 0x4b, 0x21, 0xd6, 0xf9, 0xfe, + 0x76, 0x25, 0x80, 0x00, 0x24, 0x61, 0x8b, 0x53, 0x0e, 0x6f, 0x6f, 0x79, 0x90, 0x46, 0x90, 0xba, + 0x79, 0x21, 0x17, 0xaa, 0x64, 0x06, 0x00, 0x41, 0x48, 0x6c, 0xa9, 0x06, 0xd9, 0xd0, 0xf6, 0x33, + 0x86, 0x39, 0x85, 0x58, 0xd5, 0x37, 0x70, 0x44, 0x63, 0xb0, 0xe5, 0x6f, 0x6e, 0xed, 0xde, 0xcc, + 0xa1, 0x62, 0x0f, 0x33, 0x1c, 0xa5, 0xfa, 0x0e, 0x5a, 0x25, 0x09, 0x78, 0x23, 0x37, 0x24, 0x71, + 0xc0, 0x47, 0x86, 0x56, 0xd3, 0xea, 0x0b, 0xce, 0x8a, 0xf4, 0xde, 0x49, 0x4b, 0x7f, 0x86, 0xd6, + 0x3e, 0x65, 0xc0, 0xb2, 0xc8, 0x1d, 0x32, 0xec, 0x89, 0x7f, 0x36, 0xe6, 0x6a, 0x5a, 0x7d, 0xb9, + 0x59, 0xbe, 0xb9, 0x6c, 0x20, 0x35, 0x4b, 0x9b, 0x78, 0x4e, 0x39, 0xc7, 0x8e, 0x14, 0xa5, 0xbf, + 0x40, 0x1b, 0x11, 0x8d, 0xdd, 0x04, 0x33, 0x4e, 0x3d, 0x9a, 0xc8, 0xa1, 0x8c, 0xf9, 0x07, 0x5b, + 0xd7, 0x23, 0x1a, 0xf7, 0xee, 0x73, 0x7a, 0x15, 0xad, 0x24, 0x2c, 0x8b, 0x89, 0x8b, 0x87, 0x9c, + 0x30, 0x63, 0x41, 0xce, 0x85, 0xa4, 0x75, 0x28, 0x1c, 0xfd, 0x25, 0x5a, 0x4e, 0x69, 0x10, 0xbb, + 0x11, 0xf8, 0xc4, 0x58, 0xac, 0x69, 0xf5, 0xf2, 0x41, 0xd5, 0x7a, 0x30, 0x53, 0xab, 0x4f, 0x83, + 0xf8, 0x18, 0x7c, 0xe2, 0x2c, 0xa5, 0xea, 0xa4, 0xf7, 0xd0, 0x5a, 0x84, 0x27, 0xae, 0x17, 0x82, + 0x37, 0x76, 0x7d, 0x46, 0x87, 0xdc, 0x28, 0xd6, 0xb4, 0xfa, 0xca, 0xc1, 0x96, 0x95, 0xe7, 0x69, + 0xcd, 0xf2, 0xb4, 0xda, 0x2a, 0xcf, 0x66, 0xe9, 0xea, 0x57, 0xb5, 0xf0, 0xf5, 0x77, 0x55, 0xfb, + 0xf6, 0xf7, 0xc7, 0x9e, 0xe6, 0x94, 0x22, 0x3c, 0x69, 0x89, 0xfe, 0xb6, 0x68, 0xdf, 0xfd, 0xae, + 0xa1, 0x8d, 0x43, 0xce, 0x49, 0xca, 0x73, 0x9a, 0xf2, 0x08, 0x27, 0xfa, 0x63, 0x54, 0x1c, 0x11, + 0x1a, 0x8c, 0xb8, 0x4c, 0x76, 0xde, 0x51, 0x4a, 0xf8, 0x03, 0x49, 0xc8, 0x2c, 0x57, 0x1d, 0xa5, + 0xc4, 0xb5, 0xcf, 0x81, 0x13, 0xdf, 0x4d, 0xe0, 0x82, 0x30, 0x99, 0xd6, 0x82, 0x83, 0xa4, 0xd5, + 0x13, 0x8e, 0x00, 0x38, 0x70, 0x1c, 0x2a, 0x40, 0xe5, 0x22, 0xad, 0x1c, 0x78, 0x82, 0xca, 0x29, + 0x0c, 0xb9, 0xeb, 0x41, 0x3c, 0xa4, 0x2c, 0x22, 0xbe, 0x0c, 0x67, 0xc9, 0x29, 0x09, 0xb7, 0x35, + 0x33, 0x77, 0x13, 0x54, 0xfe, 0x80, 0x43, 0xea, 0x63, 0x0e, 0xac, 0x1b, 0xfb, 0x64, 0xa2, 0x1f, + 0xa0, 0x47, 0xd8, 0xf7, 0x19, 0x49, 0x53, 0x39, 0xeb, 0x72, 0xd3, 0xb8, 0xb9, 0x6c, 0x54, 0xd4, + 0x92, 0x0e, 0xf3, 0x4a, 0x9f, 0x33, 0x1a, 0x07, 0xce, 0x0c, 0xd4, 0x2b, 0x68, 0x91, 0x8a, 0x66, + 0x79, 0x8b, 0x92, 0x93, 0x0b, 0xe1, 0xde, 0x1f, 0x3f, 0x17, 0x7b, 0xaf, 0xd0, 0xd2, 0x6c, 0x11, + 0xfa, 0x16, 0xda, 0xec, 0x77, 0x5f, 0x9f, 0xb8, 0xc7, 0xa7, 0xed, 0x8e, 0xfb, 0xfe, 0xa4, 0xdf, + 0xeb, 0xb4, 0xba, 0x47, 0xdd, 0x4e, 0x7b, 0xbd, 0xa0, 0x1b, 0xa8, 0x72, 0x57, 0x6a, 0xbd, 0xe9, + 0xb4, 0xde, 0xf6, 0x4e, 0xbb, 0x27, 0x67, 0xeb, 0x5a, 0xf3, 0xec, 0xea, 0xd6, 0xd4, 0xae, 0x6f, + 0x4d, 0xed, 0xcf, 0xad, 0xa9, 0x7d, 0x99, 0x9a, 0x85, 0xeb, 0xa9, 0x59, 0xf8, 0x39, 0x35, 0x0b, + 0x1f, 0x9f, 0x07, 0x94, 0x8f, 0xb2, 0x81, 0xe5, 0x41, 0x64, 0xab, 0x4f, 0xc0, 0x0e, 0xa0, 0x41, + 0x26, 0xc4, 0xcb, 0xc4, 0x32, 0x1a, 0x78, 0xe0, 0x51, 0x3b, 0x02, 0x3f, 0x0b, 0x49, 0xfa, 0xff, + 0x41, 0xca, 0xd7, 0x38, 0x28, 0xca, 0x45, 0x3f, 0xfd, 0x17, 0x00, 0x00, 0xff, 0xff, 0xa5, 0x75, + 0x6d, 0x19, 0xb3, 0x03, 0x00, 0x00, } func (m *Params) Marshal() (dAtA []byte, err error) { @@ -350,6 +367,14 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + n1, err1 := github_com_cosmos_gogoproto_types.StdDurationMarshalTo(m.MaxClockDrift, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.MaxClockDrift):]) + if err1 != nil { + return 0, err1 + } + i -= n1 + i = encodeVarintTypes(dAtA, i, uint64(n1)) + i-- + dAtA[i] = 0x32 if m.SignMode != 0 { i = encodeVarintTypes(dAtA, i, uint64(m.SignMode)) i-- @@ -511,6 +536,8 @@ func (m *Params) Size() (n int) { if m.SignMode != 0 { n += 1 + sovTypes(uint64(m.SignMode)) } + l = github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.MaxClockDrift) + n += 1 + l + sovTypes(uint64(l)) return n } @@ -714,6 +741,39 @@ func (m *Params) Unmarshal(dAtA []byte) error { break } } + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxClockDrift", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTypes + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := github_com_cosmos_gogoproto_types.StdDurationUnmarshal(&m.MaxClockDrift, dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) diff --git a/modules/proto/rollkitsdk/network/v1/types.proto b/modules/proto/rollkitsdk/network/v1/types.proto index 625d527c..d0dc7291 100644 --- a/modules/proto/rollkitsdk/network/v1/types.proto +++ b/modules/proto/rollkitsdk/network/v1/types.proto @@ -7,6 +7,8 @@ option go_package = "github.com/rollkit/go-execution-abci/modules/network/types" import "gogoproto/gogo.proto"; import "cosmos_proto/cosmos.proto"; import "google/protobuf/duration.proto"; +import "amino/amino.proto"; + // Params defines the parameters for the network module. message Params { @@ -24,6 +26,11 @@ message Params { // sign_mode determines when validators must sign SignMode sign_mode = 5; + + // max_clock_drift maximum a timestamp for a vote can drift + google.protobuf.Duration max_clock_drift = 6 + [(gogoproto.stdduration) = true, (gogoproto.nullable) = false, (amino.dont_omitempty) = true]; + } // SignMode defines when validators must sign From 6314f457be54e865633c5af49ce6c9c9084f2cea Mon Sep 17 00:00:00 2001 From: Alex Peters Date: Tue, 1 Jul 2025 14:16:10 +0200 Subject: [PATCH 14/17] broken --- modules/network/depinject.go | 2 -- modules/network/keeper/context.go | 24 +++++++++++++++++++++++ modules/network/keeper/keeper.go | 5 +---- modules/network/keeper/msg_server.go | 10 +++++++--- modules/network/keeper/msg_server_test.go | 2 +- modules/network/types/expected_keepers.go | 10 ++++++---- 6 files changed, 39 insertions(+), 14 deletions(-) create mode 100644 modules/network/keeper/context.go diff --git a/modules/network/depinject.go b/modules/network/depinject.go index b0c99cd4..01b57e6e 100644 --- a/modules/network/depinject.go +++ b/modules/network/depinject.go @@ -35,7 +35,6 @@ type ModuleInputs struct { StakingKeeper types.StakingKeeper AccountKeeper types.AccountKeeper BankKeeper types.BankKeeper - BlockSource types.BlockSource } type ModuleOutputs struct { @@ -58,7 +57,6 @@ func ProvideModule(in ModuleInputs) ModuleOutputs { in.StakingKeeper, in.AccountKeeper, in.BankKeeper, - in.BlockSource, authority.String(), ) m := NewAppModule( diff --git a/modules/network/keeper/context.go b/modules/network/keeper/context.go new file mode 100644 index 00000000..002e991f --- /dev/null +++ b/modules/network/keeper/context.go @@ -0,0 +1,24 @@ +package keeper + +import ( + "context" + "fmt" + + "github.com/rollkit/go-execution-abci/modules/network/types" +) + +type contextKey struct{} + +var headerSourceKey = contextKey{} + +func WithHeaderSource(ctx context.Context, s types.HeaderSource) context.Context { + return context.WithValue(ctx, headerSourceKey, s) +} + +func HeaderSource(ctx context.Context) (types.HeaderSource, error) { + headerSource, ok := ctx.Value(headerSourceKey).(types.HeaderSource) + if !ok { + return nil, fmt.Errorf("header source not found in context") + } + return headerSource, nil +} diff --git a/modules/network/keeper/keeper.go b/modules/network/keeper/keeper.go index a94925c7..c31a4841 100644 --- a/modules/network/keeper/keeper.go +++ b/modules/network/keeper/keeper.go @@ -22,7 +22,6 @@ type Keeper struct { bankKeeper types.BankKeeper authority string bitmapHelper *BitmapHelper - blockSource types.BlockSource // Collections for state management ValidatorIndex collections.Map[string, uint16] @@ -43,7 +42,6 @@ func NewKeeper( sk types.StakingKeeper, ak types.AccountKeeper, bk types.BankKeeper, - blockSource types.BlockSource, authority string, ) Keeper { @@ -55,7 +53,6 @@ func NewKeeper( bankKeeper: bk, authority: authority, bitmapHelper: NewBitmapHelper(), - blockSource: blockSource, ValidatorIndex: collections.NewMap(sb, types.ValidatorIndexPrefix, "validator_index", collections.StringKey, collections.Uint16Value), ValidatorPower: collections.NewMap(sb, types.ValidatorPowerPrefix, "validator_power", collections.Uint16Key, collections.Uint64Value), @@ -219,7 +216,7 @@ func (k Keeper) IsCheckpointHeight(ctx sdk.Context, height int64) bool { // CalculateVotedPower calculates the total voted power from a bitmap func (k Keeper) CalculateVotedPower(ctx sdk.Context, bitmap []byte) (uint64, error) { var votedPower uint64 - for i := 0; i < len(bitmap)*8; i++ { + for i := range len(bitmap) * 8 { if k.bitmapHelper.IsSet(bitmap, i) { power, err := k.GetValidatorPower(ctx, uint16(i)) if err != nil { diff --git a/modules/network/keeper/msg_server.go b/modules/network/keeper/msg_server.go index cc3bff0d..dd71834f 100644 --- a/modules/network/keeper/msg_server.go +++ b/modules/network/keeper/msg_server.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "fmt" - "time" "cosmossdk.io/collections" "cosmossdk.io/errors" @@ -164,7 +163,12 @@ func (k msgServer) verifyVote(ctx sdk.Context, msg *types.MsgAttest) (*cmtproto. if len(vote.Signature) == 0 { return nil, sdkerrors.ErrInvalidRequest.Wrap("empty signature") } - header, _, err := k.blockSource.GetBlockData(ctx, uint64(vote.Height)) + + headerSource, err := HeaderSource(ctx) + if err != nil { + return nil, sdkerrors.ErrInvalidType.Wrap(err.Error()) + } + header, _, err := headerSource.GetBlockData(ctx, uint64(vote.Height)) if err != nil { return nil, errors.Wrapf(err, "block data for height %d", vote.Height) } @@ -176,7 +180,7 @@ func (k msgServer) verifyVote(ctx sdk.Context, msg *types.MsgAttest) (*cmtproto. return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "vote block ID hash does not match app hash for height %d", vote.Height) } - maxClockDrift := time.Duration(k.GetParams(ctx).MaxClockDrift) + maxClockDrift := k.GetParams(ctx).MaxClockDrift if drift := vote.Timestamp.Sub(header.Time()); drift < 0 || drift > maxClockDrift { return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "vote timestamp drift exceeds limit: %s", drift) } diff --git a/modules/network/keeper/msg_server_test.go b/modules/network/keeper/msg_server_test.go index e44a5ff2..27362948 100644 --- a/modules/network/keeper/msg_server_test.go +++ b/modules/network/keeper/msg_server_test.go @@ -408,7 +408,7 @@ func setupTestEnv(t *testing.T, height int64) testEnv { bs := rstore.New(rollkitPrefixStore) authority := authtypes.NewModuleAddress("gov") - keeper := NewKeeper(cdc, runtime.NewKVStoreService(keys[types.StoreKey]), &sk, nil, nil, bs, authority.String()) + keeper := NewKeeper(cdc, runtime.NewKVStoreService(keys[types.StoreKey]), &sk, nil, nil, authority.String()) ctx := sdk.NewContext(cms, cmtproto.Header{ ChainID: "testing", diff --git a/modules/network/types/expected_keepers.go b/modules/network/types/expected_keepers.go index a356b9aa..e6341912 100644 --- a/modules/network/types/expected_keepers.go +++ b/modules/network/types/expected_keepers.go @@ -7,7 +7,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - tyrollkittypes "github.com/rollkit/rollkit/types" + rkttypes "github.com/rollkit/rollkit/types" ) // StakingKeeper defines the expected staking keeper interface @@ -28,7 +28,9 @@ type BankKeeper interface { SpendableCoins(ctx context.Context, addr sdk.AccAddress) sdk.Coins } -// BlockSource is the block store -type BlockSource interface { - GetBlockData(ctx context.Context, height uint64) (*tyrollkittypes.SignedHeader, *tyrollkittypes.Data, error) +// HeaderSource is the block store +type HeaderSource interface { + // GetBlockData is deprecated + // Deprecated: use GetHeader when available in working rollkit main + GetBlockData(ctx context.Context, height uint64) (*rkttypes.SignedHeader, *rkttypes.Data, error) } From bbd0a29284195591229be1b1092c7143108771c4 Mon Sep 17 00:00:00 2001 From: Alex Peters Date: Wed, 2 Jul 2025 12:03:17 +0200 Subject: [PATCH 15/17] Remove app hash verification --- modules/network/keeper/context.go | 24 ---- modules/network/keeper/keeper.go | 2 +- modules/network/keeper/msg_server.go | 82 ++++------- modules/network/keeper/msg_server_test.go | 18 --- modules/network/types/expected_keepers.go | 9 -- modules/network/types/params.go | 7 - modules/network/types/types.pb.go | 134 +++++------------- .../proto/rollkitsdk/network/v1/types.proto | 7 - 8 files changed, 69 insertions(+), 214 deletions(-) delete mode 100644 modules/network/keeper/context.go diff --git a/modules/network/keeper/context.go b/modules/network/keeper/context.go deleted file mode 100644 index 002e991f..00000000 --- a/modules/network/keeper/context.go +++ /dev/null @@ -1,24 +0,0 @@ -package keeper - -import ( - "context" - "fmt" - - "github.com/rollkit/go-execution-abci/modules/network/types" -) - -type contextKey struct{} - -var headerSourceKey = contextKey{} - -func WithHeaderSource(ctx context.Context, s types.HeaderSource) context.Context { - return context.WithValue(ctx, headerSourceKey, s) -} - -func HeaderSource(ctx context.Context) (types.HeaderSource, error) { - headerSource, ok := ctx.Value(headerSourceKey).(types.HeaderSource) - if !ok { - return nil, fmt.Errorf("header source not found in context") - } - return headerSource, nil -} diff --git a/modules/network/keeper/keeper.go b/modules/network/keeper/keeper.go index c31a4841..05d22a85 100644 --- a/modules/network/keeper/keeper.go +++ b/modules/network/keeper/keeper.go @@ -216,7 +216,7 @@ func (k Keeper) IsCheckpointHeight(ctx sdk.Context, height int64) bool { // CalculateVotedPower calculates the total voted power from a bitmap func (k Keeper) CalculateVotedPower(ctx sdk.Context, bitmap []byte) (uint64, error) { var votedPower uint64 - for i := range len(bitmap) * 8 { + for i := 0; i < len(bitmap)*8; i++ { if k.bitmapHelper.IsSet(bitmap, i) { power, err := k.GetValidatorPower(ctx, uint16(i)) if err != nil { diff --git a/modules/network/keeper/msg_server.go b/modules/network/keeper/msg_server.go index dd71834f..96dae215 100644 --- a/modules/network/keeper/msg_server.go +++ b/modules/network/keeper/msg_server.go @@ -6,7 +6,7 @@ import ( "fmt" "cosmossdk.io/collections" - "cosmossdk.io/errors" + sdkerr "cosmossdk.io/errors" "cosmossdk.io/math" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" cmttypes "github.com/cometbft/cometbft/types" @@ -38,12 +38,12 @@ func (k msgServer) Attest(goCtx context.Context, msg *types.MsgAttest) (*types.M } // can vote only for the last epoch if delta := ctx.BlockHeight() - msg.Height; delta < 0 || delta > int64(k.GetParams(ctx).EpochLength) { - return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "exceeded voting window: %d blocks", delta) + return nil, sdkerr.Wrapf(sdkerrors.ErrInvalidRequest, "exceeded voting window: %d blocks", delta) } valIndexPos, found := k.GetValidatorIndex(ctx, msg.Validator) if !found { - return nil, errors.Wrapf(sdkerrors.ErrNotFound, "validator index not found for %s", msg.Validator) + return nil, sdkerr.Wrapf(sdkerrors.ErrNotFound, "validator index not found for %s", msg.Validator) } vote, err := k.verifyVote(ctx, msg) @@ -52,11 +52,11 @@ func (k msgServer) Attest(goCtx context.Context, msg *types.MsgAttest) (*types.M } if err := k.updateAttestationBitmap(ctx, msg, valIndexPos); err != nil { - return nil, errors.Wrap(err, "update attestation bitmap") + return nil, sdkerr.Wrap(err, "update attestation bitmap") } if err := k.SetSignature(ctx, msg.Height, msg.Validator, vote.Signature); err != nil { - return nil, errors.Wrap(err, "store signature") + return nil, sdkerr.Wrap(err, "store signature") } if err := k.updateEpochBitmap(ctx, uint64(msg.Height), valIndexPos); err != nil { @@ -91,7 +91,7 @@ func (k msgServer) updateEpochBitmap(ctx sdk.Context, votedEpoch uint64, index u } k.bitmapHelper.SetBit(epochBitmap, int(index)) if err := k.SetEpochBitmap(ctx, votedEpoch, epochBitmap); err != nil { - return errors.Wrap(err, "set epoch bitmap") + return sdkerr.Wrap(err, "set epoch bitmap") } return nil } @@ -100,15 +100,15 @@ func (k msgServer) updateEpochBitmap(ctx sdk.Context, votedEpoch uint64, index u func (k msgServer) validateAttestation(ctx sdk.Context, msg *types.MsgAttest) error { if k.GetParams(ctx).SignMode == types.SignMode_SIGN_MODE_CHECKPOINT && !k.IsCheckpointHeight(ctx, msg.Height) { - return errors.Wrapf(sdkerrors.ErrInvalidRequest, "height %d is not a checkpoint", msg.Height) + return sdkerr.Wrapf(sdkerrors.ErrInvalidRequest, "height %d is not a checkpoint", msg.Height) } has, err := k.IsInAttesterSet(ctx, msg.Validator) if err != nil { - return errors.Wrapf(err, "in attester set") + return sdkerr.Wrapf(err, "in attester set") } if !has { - return errors.Wrapf(sdkerrors.ErrUnauthorized, "validator %s not in attester set", msg.Validator) + return sdkerr.Wrapf(sdkerrors.ErrUnauthorized, "validator %s not in attester set", msg.Validator) } return nil } @@ -116,7 +116,7 @@ func (k msgServer) validateAttestation(ctx sdk.Context, msg *types.MsgAttest) er // updateAttestationBitmap handles bitmap operations for attestation func (k msgServer) updateAttestationBitmap(ctx sdk.Context, msg *types.MsgAttest, index uint16) error { bitmap, err := k.GetAttestationBitmap(ctx, msg.Height) - if err != nil && !errors.IsOf(err, collections.ErrNotFound) { + if err != nil && !sdkerr.IsOf(err, collections.ErrNotFound) { return err } @@ -135,13 +135,13 @@ func (k msgServer) updateAttestationBitmap(ctx sdk.Context, msg *types.MsgAttest } if k.bitmapHelper.IsSet(bitmap, int(index)) { - return errors.Wrapf(sdkerrors.ErrInvalidRequest, "validator %s already attested for height %d", msg.Validator, msg.Height) + return sdkerr.Wrapf(sdkerrors.ErrInvalidRequest, "validator %s already attested for height %d", msg.Validator, msg.Height) } k.bitmapHelper.SetBit(bitmap, int(index)) if err := k.SetAttestationBitmap(ctx, msg.Height, bitmap); err != nil { - return errors.Wrap(err, "set attestation bitmap") + return sdkerr.Wrap(err, "set attestation bitmap") } return nil } @@ -150,56 +150,36 @@ func (k msgServer) updateAttestationBitmap(ctx sdk.Context, msg *types.MsgAttest func (k msgServer) verifyVote(ctx sdk.Context, msg *types.MsgAttest) (*cmtproto.Vote, error) { var vote cmtproto.Vote if err := proto.Unmarshal(msg.Vote, &vote); err != nil { - return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "unmarshal vote: %s", err) + return nil, sdkerr.Wrapf(sdkerrors.ErrInvalidRequest, "unmarshal vote: %s", err) } if msg.Height != vote.Height { - return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "vote height does not match attestation height") + return nil, sdkerr.Wrapf(sdkerrors.ErrInvalidRequest, "vote height does not match attestation height") } if senderAddr, err := sdk.ValAddressFromBech32(msg.Validator); err != nil { - return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid validator address: %s", err) + return nil, sdkerr.Wrapf(sdkerrors.ErrInvalidRequest, "invalid validator address: %s", err) } else if !bytes.Equal(senderAddr, vote.ValidatorAddress) { - return nil, errors.Wrapf(sdkerrors.ErrUnauthorized, "vote validator address does not match attestation validator address") + return nil, sdkerr.Wrapf(sdkerrors.ErrUnauthorized, "vote validator address does not match attestation validator address") } if len(vote.Signature) == 0 { return nil, sdkerrors.ErrInvalidRequest.Wrap("empty signature") } - headerSource, err := HeaderSource(ctx) - if err != nil { - return nil, sdkerrors.ErrInvalidType.Wrap(err.Error()) - } - header, _, err := headerSource.GetBlockData(ctx, uint64(vote.Height)) - if err != nil { - return nil, errors.Wrapf(err, "block data for height %d", vote.Height) - } - if header == nil { - return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "block header not found for height %d", vote.Height) - } - - if !bytes.Equal(vote.BlockID.Hash, header.AppHash) { // todo (Alex): is this the correct hash? - return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "vote block ID hash does not match app hash for height %d", vote.Height) - } - - maxClockDrift := k.GetParams(ctx).MaxClockDrift - if drift := vote.Timestamp.Sub(header.Time()); drift < 0 || drift > maxClockDrift { - return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "vote timestamp drift exceeds limit: %s", drift) - } + // todo (Alex): validate app hash match, vote clock drift validator, err := k.stakingKeeper.GetValidator(ctx, vote.ValidatorAddress) if err != nil { - return nil, errors.Wrapf(err, "get validator") + return nil, sdkerr.Wrapf(err, "get validator") } pubKey, err := validator.ConsPubKey() if err != nil { - return nil, errors.Wrapf(err, "pubkey") + return nil, sdkerr.Wrapf(err, "pubkey") } if !bytes.Equal(pubKey.Address(), vote.ValidatorAddress) { - return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "pubkey address does not match validator address") + return nil, sdkerr.Wrapf(sdkerrors.ErrInvalidRequest, "pubkey address does not match validator address") } - - voteSignBytes := cmttypes.VoteSignBytes(header.ChainID(), &vote) + voteSignBytes := cmttypes.VoteSignBytes(ctx.ChainID(), &vote) if !pubKey.VerifySignature(voteSignBytes, vote.Signature) { - return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "invalid vote signature") + return nil, sdkerr.Wrapf(sdkerrors.ErrInvalidRequest, "invalid vote signature") } return &vote, nil @@ -211,7 +191,7 @@ func (k msgServer) JoinAttesterSet(goCtx context.Context, msg *types.MsgJoinAtte valAddr, err := sdk.ValAddressFromBech32(msg.Validator) if err != nil { - return nil, errors.Wrapf(sdkerrors.ErrInvalidAddress, "invalid validator address: %s", err) + return nil, sdkerr.Wrapf(sdkerrors.ErrInvalidAddress, "invalid validator address: %s", err) } validator, err := k.stakingKeeper.GetValidator(ctx, valAddr) @@ -220,19 +200,19 @@ func (k msgServer) JoinAttesterSet(goCtx context.Context, msg *types.MsgJoinAtte } if !validator.IsBonded() { - return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "validator must be bonded to join attester set") + return nil, sdkerr.Wrapf(sdkerrors.ErrInvalidRequest, "validator must be bonded to join attester set") } has, err := k.IsInAttesterSet(ctx, msg.Validator) if err != nil { - return nil, errors.Wrapf(err, "in attester set") + return nil, sdkerr.Wrapf(err, "in attester set") } if has { - return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "validator already in attester set") + return nil, sdkerr.Wrapf(sdkerrors.ErrInvalidRequest, "validator already in attester set") } // TODO (Alex): the valset should be updated at the end of an epoch only if err := k.SetAttesterSetMember(ctx, msg.Validator); err != nil { - return nil, errors.Wrap(err, "set attester set member") + return nil, sdkerr.Wrap(err, "set attester set member") } ctx.EventManager().EmitEvent( @@ -251,15 +231,15 @@ func (k msgServer) LeaveAttesterSet(goCtx context.Context, msg *types.MsgLeaveAt has, err := k.IsInAttesterSet(ctx, msg.Validator) if err != nil { - return nil, errors.Wrapf(err, "in attester set") + return nil, sdkerr.Wrapf(err, "in attester set") } if !has { - return nil, errors.Wrapf(sdkerrors.ErrInvalidRequest, "validator not in attester set") + return nil, sdkerr.Wrapf(sdkerrors.ErrInvalidRequest, "validator not in attester set") } // TODO (Alex): the valset should be updated at the end of an epoch only if err := k.RemoveAttesterSetMember(ctx, msg.Validator); err != nil { - return nil, errors.Wrap(err, "remove attester set member") + return nil, sdkerr.Wrap(err, "remove attester set member") } ctx.EventManager().EmitEvent( @@ -277,7 +257,7 @@ func (k msgServer) UpdateParams(goCtx context.Context, msg *types.MsgUpdateParam ctx := sdk.UnwrapSDKContext(goCtx) if k.GetAuthority() != msg.Authority { - return nil, errors.Wrapf(govtypes.ErrInvalidSigner, "invalid authority; expected %s, got %s", k.GetAuthority(), msg.Authority) + return nil, sdkerr.Wrapf(govtypes.ErrInvalidSigner, "invalid authority; expected %s, got %s", k.GetAuthority(), msg.Authority) } if err := msg.Params.Validate(); err != nil { diff --git a/modules/network/keeper/msg_server_test.go b/modules/network/keeper/msg_server_test.go index 27362948..c91b9b38 100644 --- a/modules/network/keeper/msg_server_test.go +++ b/modules/network/keeper/msg_server_test.go @@ -309,16 +309,6 @@ func TestVerifyVote(t *testing.T) { }, sender: valAddrStr, }, - "timestamp drift": { - voteFn: func(t *testing.T) *cmtproto.Vote { - return VoteFixture(myAppHash, validatorPrivKey, func(vote *cmtproto.Vote) { - vote.Timestamp = time.Now().Add(6 * time.Second) - vote.Signature = must(validatorPrivKey.Sign(cmttypes.VoteSignBytes("testing", vote))) - }) - }, - sender: valAddrStr, - expErr: sdkerrors.ErrInvalidRequest, - }, "block data not found": { voteFn: func(t *testing.T) *cmtproto.Vote { return VoteFixture(myAppHash, validatorPrivKey, func(vote *cmtproto.Vote) { @@ -329,14 +319,6 @@ func TestVerifyVote(t *testing.T) { sender: valAddrStr, expErr: sdkerrors.ErrInvalidRequest, }, - "vote and block hash mismatch": { - voteFn: func(t *testing.T) *cmtproto.Vote { - hash := sha256.Sum256([]byte("wrong_hash")) - return VoteFixture(hash[:], validatorPrivKey) - }, - sender: valAddrStr, - expErr: sdkerrors.ErrInvalidRequest, - }, "validator not found": { voteFn: func(t *testing.T) *cmtproto.Vote { return VoteFixture(myAppHash, ed25519.GenPrivKey()) diff --git a/modules/network/types/expected_keepers.go b/modules/network/types/expected_keepers.go index e6341912..8c11e6de 100644 --- a/modules/network/types/expected_keepers.go +++ b/modules/network/types/expected_keepers.go @@ -6,8 +6,6 @@ import ( "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - - rkttypes "github.com/rollkit/rollkit/types" ) // StakingKeeper defines the expected staking keeper interface @@ -27,10 +25,3 @@ type AccountKeeper interface { type BankKeeper interface { SpendableCoins(ctx context.Context, addr sdk.AccAddress) sdk.Coins } - -// HeaderSource is the block store -type HeaderSource interface { - // GetBlockData is deprecated - // Deprecated: use GetHeader when available in working rollkit main - GetBlockData(ctx context.Context, height uint64) (*rkttypes.SignedHeader, *rkttypes.Data, error) -} diff --git a/modules/network/types/params.go b/modules/network/types/params.go index ef57cf70..f7a68664 100644 --- a/modules/network/types/params.go +++ b/modules/network/types/params.go @@ -2,7 +2,6 @@ package types import ( "fmt" - "time" "cosmossdk.io/math" paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" @@ -33,7 +32,6 @@ func NewParams( minParticipation math.LegacyDec, pruneAfter uint64, signMode SignMode, - maxClockDrift time.Duration, ) Params { return Params{ EpochLength: epochLength, @@ -41,7 +39,6 @@ func NewParams( MinParticipation: minParticipation.String(), PruneAfter: pruneAfter, SignMode: signMode, - MaxClockDrift: maxClockDrift, } } @@ -53,7 +50,6 @@ func DefaultParams() Params { DefaultMinParticipation, DefaultPruneAfter, DefaultSignMode, - 5*time.Second, ) } @@ -85,9 +81,6 @@ func (p Params) Validate() error { if err := validateSignMode(p.SignMode); err != nil { return err } - if p.MaxClockDrift < 0 { - return fmt.Errorf("max clock drift must be positive: %s", p.MaxClockDrift) - } return nil } diff --git a/modules/network/types/types.pb.go b/modules/network/types/types.pb.go index e57e2f51..a95a6c79 100644 --- a/modules/network/types/types.pb.go +++ b/modules/network/types/types.pb.go @@ -6,22 +6,18 @@ package types import ( fmt "fmt" _ "github.com/cosmos/cosmos-proto" - _ "github.com/cosmos/cosmos-sdk/types/tx/amino" _ "github.com/cosmos/gogoproto/gogoproto" proto "github.com/cosmos/gogoproto/proto" - github_com_cosmos_gogoproto_types "github.com/cosmos/gogoproto/types" _ "google.golang.org/protobuf/types/known/durationpb" io "io" math "math" math_bits "math/bits" - time "time" ) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf var _ = math.Inf -var _ = time.Kitchen // This is a compile-time assertion to ensure that this generated file // is compatible with the proto package it is being compiled against. @@ -69,8 +65,6 @@ type Params struct { PruneAfter uint64 `protobuf:"varint,4,opt,name=prune_after,json=pruneAfter,proto3" json:"prune_after,omitempty"` // sign_mode determines when validators must sign SignMode SignMode `protobuf:"varint,5,opt,name=sign_mode,json=signMode,proto3,enum=rollkitsdk.network.v1.SignMode" json:"sign_mode,omitempty"` - // max_clock_drift maximum a timestamp for a vote can drift - MaxClockDrift time.Duration `protobuf:"bytes,6,opt,name=max_clock_drift,json=maxClockDrift,proto3,stdduration" json:"max_clock_drift"` } func (m *Params) Reset() { *m = Params{} } @@ -141,13 +135,6 @@ func (m *Params) GetSignMode() SignMode { return SignMode_SIGN_MODE_UNSPECIFIED } -func (m *Params) GetMaxClockDrift() time.Duration { - if m != nil { - return m.MaxClockDrift - } - return 0 -} - // AttestationBitmap stores the bitmap and metadata for a checkpoint type AttestationBitmap struct { // height is the checkpoint height @@ -304,47 +291,43 @@ func init() { func init() { proto.RegisterFile("rollkitsdk/network/v1/types.proto", fileDescriptor_6ae6db90f9054c5e) } var fileDescriptor_6ae6db90f9054c5e = []byte{ - // 630 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x93, 0xcd, 0x4e, 0xdb, 0x40, - 0x14, 0x85, 0x63, 0x7e, 0x52, 0x18, 0x48, 0x00, 0x2b, 0x54, 0x86, 0x85, 0x13, 0x90, 0x2a, 0x45, - 0x48, 0xb1, 0x05, 0x5d, 0x54, 0x6a, 0x2b, 0x55, 0xe4, 0x87, 0x36, 0x6a, 0x81, 0xc8, 0xa1, 0x5d, - 0x74, 0x63, 0x4d, 0xec, 0x89, 0x33, 0x8a, 0xed, 0xeb, 0x8e, 0xc7, 0x90, 0xbe, 0x45, 0x97, 0x7d, - 0x84, 0x2e, 0xba, 0xe8, 0x82, 0x87, 0x60, 0x89, 0x58, 0x75, 0xd5, 0x56, 0x64, 0xd1, 0xd7, 0xa8, - 0x66, 0x3c, 0x29, 0x2c, 0xd8, 0x44, 0x73, 0xce, 0xfd, 0x6e, 0x74, 0xe7, 0x5c, 0x0f, 0xda, 0x61, - 0x10, 0x86, 0x63, 0xca, 0x53, 0x7f, 0x6c, 0xc7, 0x84, 0x5f, 0x00, 0x1b, 0xdb, 0xe7, 0xfb, 0x36, - 0xff, 0x9c, 0x90, 0xd4, 0x4a, 0x18, 0x70, 0xd0, 0x37, 0xef, 0x10, 0x4b, 0x21, 0xd6, 0xf9, 0xfe, - 0x76, 0x25, 0x80, 0x00, 0x24, 0x61, 0x8b, 0x53, 0x0e, 0x6f, 0x6f, 0x79, 0x90, 0x46, 0x90, 0xba, - 0x79, 0x21, 0x17, 0xaa, 0x64, 0x06, 0x00, 0x41, 0x48, 0x6c, 0xa9, 0x06, 0xd9, 0xd0, 0xf6, 0x33, - 0x86, 0x39, 0x85, 0x58, 0xd5, 0x37, 0x70, 0x44, 0x63, 0xb0, 0xe5, 0x6f, 0x6e, 0xed, 0xde, 0xcc, - 0xa1, 0x62, 0x0f, 0x33, 0x1c, 0xa5, 0xfa, 0x0e, 0x5a, 0x25, 0x09, 0x78, 0x23, 0x37, 0x24, 0x71, - 0xc0, 0x47, 0x86, 0x56, 0xd3, 0xea, 0x0b, 0xce, 0x8a, 0xf4, 0xde, 0x49, 0x4b, 0x7f, 0x86, 0xd6, - 0x3e, 0x65, 0xc0, 0xb2, 0xc8, 0x1d, 0x32, 0xec, 0x89, 0x7f, 0x36, 0xe6, 0x6a, 0x5a, 0x7d, 0xb9, - 0x59, 0xbe, 0xb9, 0x6c, 0x20, 0x35, 0x4b, 0x9b, 0x78, 0x4e, 0x39, 0xc7, 0x8e, 0x14, 0xa5, 0xbf, - 0x40, 0x1b, 0x11, 0x8d, 0xdd, 0x04, 0x33, 0x4e, 0x3d, 0x9a, 0xc8, 0xa1, 0x8c, 0xf9, 0x07, 0x5b, - 0xd7, 0x23, 0x1a, 0xf7, 0xee, 0x73, 0x7a, 0x15, 0xad, 0x24, 0x2c, 0x8b, 0x89, 0x8b, 0x87, 0x9c, - 0x30, 0x63, 0x41, 0xce, 0x85, 0xa4, 0x75, 0x28, 0x1c, 0xfd, 0x25, 0x5a, 0x4e, 0x69, 0x10, 0xbb, - 0x11, 0xf8, 0xc4, 0x58, 0xac, 0x69, 0xf5, 0xf2, 0x41, 0xd5, 0x7a, 0x30, 0x53, 0xab, 0x4f, 0x83, - 0xf8, 0x18, 0x7c, 0xe2, 0x2c, 0xa5, 0xea, 0xa4, 0xf7, 0xd0, 0x5a, 0x84, 0x27, 0xae, 0x17, 0x82, - 0x37, 0x76, 0x7d, 0x46, 0x87, 0xdc, 0x28, 0xd6, 0xb4, 0xfa, 0xca, 0xc1, 0x96, 0x95, 0xe7, 0x69, - 0xcd, 0xf2, 0xb4, 0xda, 0x2a, 0xcf, 0x66, 0xe9, 0xea, 0x57, 0xb5, 0xf0, 0xf5, 0x77, 0x55, 0xfb, - 0xf6, 0xf7, 0xc7, 0x9e, 0xe6, 0x94, 0x22, 0x3c, 0x69, 0x89, 0xfe, 0xb6, 0x68, 0xdf, 0xfd, 0xae, - 0xa1, 0x8d, 0x43, 0xce, 0x49, 0xca, 0x73, 0x9a, 0xf2, 0x08, 0x27, 0xfa, 0x63, 0x54, 0x1c, 0x11, - 0x1a, 0x8c, 0xb8, 0x4c, 0x76, 0xde, 0x51, 0x4a, 0xf8, 0x03, 0x49, 0xc8, 0x2c, 0x57, 0x1d, 0xa5, - 0xc4, 0xb5, 0xcf, 0x81, 0x13, 0xdf, 0x4d, 0xe0, 0x82, 0x30, 0x99, 0xd6, 0x82, 0x83, 0xa4, 0xd5, - 0x13, 0x8e, 0x00, 0x38, 0x70, 0x1c, 0x2a, 0x40, 0xe5, 0x22, 0xad, 0x1c, 0x78, 0x82, 0xca, 0x29, - 0x0c, 0xb9, 0xeb, 0x41, 0x3c, 0xa4, 0x2c, 0x22, 0xbe, 0x0c, 0x67, 0xc9, 0x29, 0x09, 0xb7, 0x35, - 0x33, 0x77, 0x13, 0x54, 0xfe, 0x80, 0x43, 0xea, 0x63, 0x0e, 0xac, 0x1b, 0xfb, 0x64, 0xa2, 0x1f, - 0xa0, 0x47, 0xd8, 0xf7, 0x19, 0x49, 0x53, 0x39, 0xeb, 0x72, 0xd3, 0xb8, 0xb9, 0x6c, 0x54, 0xd4, - 0x92, 0x0e, 0xf3, 0x4a, 0x9f, 0x33, 0x1a, 0x07, 0xce, 0x0c, 0xd4, 0x2b, 0x68, 0x91, 0x8a, 0x66, - 0x79, 0x8b, 0x92, 0x93, 0x0b, 0xe1, 0xde, 0x1f, 0x3f, 0x17, 0x7b, 0xaf, 0xd0, 0xd2, 0x6c, 0x11, - 0xfa, 0x16, 0xda, 0xec, 0x77, 0x5f, 0x9f, 0xb8, 0xc7, 0xa7, 0xed, 0x8e, 0xfb, 0xfe, 0xa4, 0xdf, - 0xeb, 0xb4, 0xba, 0x47, 0xdd, 0x4e, 0x7b, 0xbd, 0xa0, 0x1b, 0xa8, 0x72, 0x57, 0x6a, 0xbd, 0xe9, - 0xb4, 0xde, 0xf6, 0x4e, 0xbb, 0x27, 0x67, 0xeb, 0x5a, 0xf3, 0xec, 0xea, 0xd6, 0xd4, 0xae, 0x6f, - 0x4d, 0xed, 0xcf, 0xad, 0xa9, 0x7d, 0x99, 0x9a, 0x85, 0xeb, 0xa9, 0x59, 0xf8, 0x39, 0x35, 0x0b, - 0x1f, 0x9f, 0x07, 0x94, 0x8f, 0xb2, 0x81, 0xe5, 0x41, 0x64, 0xab, 0x4f, 0xc0, 0x0e, 0xa0, 0x41, - 0x26, 0xc4, 0xcb, 0xc4, 0x32, 0x1a, 0x78, 0xe0, 0x51, 0x3b, 0x02, 0x3f, 0x0b, 0x49, 0xfa, 0xff, - 0x41, 0xca, 0xd7, 0x38, 0x28, 0xca, 0x45, 0x3f, 0xfd, 0x17, 0x00, 0x00, 0xff, 0xff, 0xa5, 0x75, - 0x6d, 0x19, 0xb3, 0x03, 0x00, 0x00, + // 562 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x93, 0x4f, 0x6f, 0xd3, 0x30, + 0x14, 0xc0, 0x9b, 0xfd, 0xa3, 0xf5, 0xb6, 0xd2, 0x45, 0x1d, 0xca, 0x76, 0xc8, 0xba, 0x4a, 0x48, + 0x15, 0x52, 0x13, 0x6d, 0x1c, 0x90, 0x00, 0x09, 0xf5, 0xdf, 0xa0, 0x82, 0x75, 0x55, 0x3a, 0x38, + 0x70, 0x89, 0xdc, 0xc4, 0x4d, 0xad, 0x26, 0x79, 0xc1, 0x76, 0xba, 0x71, 0xe3, 0x23, 0xf0, 0x41, + 0x38, 0xee, 0x43, 0x70, 0x9c, 0x76, 0xe2, 0x88, 0xda, 0x2f, 0x82, 0xe2, 0xb8, 0x6c, 0x87, 0xdd, + 0xfc, 0x7e, 0xef, 0xe7, 0xf8, 0xbd, 0xe7, 0x18, 0x1d, 0x33, 0x08, 0xc3, 0x19, 0x15, 0xdc, 0x9f, + 0xd9, 0x31, 0x11, 0x57, 0xc0, 0x66, 0xf6, 0xfc, 0xc4, 0x16, 0xdf, 0x13, 0xc2, 0xad, 0x84, 0x81, + 0x00, 0x7d, 0xff, 0x5e, 0xb1, 0x94, 0x62, 0xcd, 0x4f, 0x0e, 0xab, 0x01, 0x04, 0x20, 0x0d, 0x3b, + 0x5b, 0xe5, 0xf2, 0xe1, 0x81, 0x07, 0x3c, 0x02, 0xee, 0xe6, 0x89, 0x3c, 0x50, 0x29, 0x33, 0x00, + 0x08, 0x42, 0x62, 0xcb, 0x68, 0x9c, 0x4e, 0x6c, 0x3f, 0x65, 0x58, 0x50, 0x88, 0xf3, 0x7c, 0xfd, + 0xc7, 0x1a, 0xda, 0x1a, 0x62, 0x86, 0x23, 0xae, 0x1f, 0xa3, 0x1d, 0x92, 0x80, 0x37, 0x75, 0x43, + 0x12, 0x07, 0x62, 0x6a, 0x68, 0x35, 0xad, 0xb1, 0xe1, 0x6c, 0x4b, 0xf6, 0x49, 0x22, 0xfd, 0x15, + 0x7a, 0xfa, 0x2d, 0x05, 0x96, 0x46, 0xee, 0x84, 0x61, 0x2f, 0xfb, 0x8c, 0xb1, 0x56, 0xd3, 0x1a, + 0xa5, 0x76, 0xf9, 0xee, 0xa6, 0x89, 0xd4, 0xc1, 0x5d, 0xe2, 0x39, 0xe5, 0x5c, 0x3b, 0x53, 0x96, + 0xfe, 0x06, 0xed, 0x45, 0x34, 0x76, 0x13, 0xcc, 0x04, 0xf5, 0x68, 0x22, 0x2b, 0x30, 0xd6, 0x1f, + 0xdd, 0x5a, 0x89, 0x68, 0x3c, 0x7c, 0xe8, 0xe9, 0x47, 0x68, 0x3b, 0x61, 0x69, 0x4c, 0x5c, 0x3c, + 0x11, 0x84, 0x19, 0x1b, 0xb2, 0x2e, 0x24, 0x51, 0x2b, 0x23, 0xfa, 0x5b, 0x54, 0xe2, 0x34, 0x88, + 0xdd, 0x08, 0x7c, 0x62, 0x6c, 0xd6, 0xb4, 0x46, 0xf9, 0xf4, 0xc8, 0x7a, 0x74, 0x80, 0xd6, 0x88, + 0x06, 0xf1, 0x39, 0xf8, 0xc4, 0x29, 0x72, 0xb5, 0xaa, 0xff, 0xd2, 0xd0, 0x5e, 0x4b, 0x08, 0xc2, + 0x85, 0x3c, 0xae, 0x4d, 0x45, 0x84, 0x13, 0xfd, 0x19, 0xda, 0x9a, 0x12, 0x1a, 0x4c, 0x85, 0x9c, + 0xc3, 0xba, 0xa3, 0xa2, 0x8c, 0x8f, 0xa5, 0x21, 0x3b, 0xdf, 0x71, 0x54, 0x94, 0x15, 0x39, 0x07, + 0x41, 0x7c, 0x37, 0x81, 0x2b, 0xc2, 0x64, 0x6f, 0x1b, 0x0e, 0x92, 0x68, 0x98, 0x91, 0x4c, 0x10, + 0x20, 0x70, 0xa8, 0x04, 0xd5, 0x85, 0x44, 0xb9, 0xf0, 0x1c, 0x95, 0x39, 0x4c, 0x84, 0xeb, 0x41, + 0x3c, 0xa1, 0x2c, 0x22, 0xbe, 0x6c, 0xa5, 0xe8, 0xec, 0x66, 0xb4, 0xb3, 0x82, 0xf5, 0x04, 0x95, + 0xbf, 0xe0, 0x90, 0xfa, 0x58, 0x00, 0xeb, 0xc7, 0x3e, 0xb9, 0xd6, 0x4f, 0xd1, 0x13, 0xec, 0xfb, + 0x8c, 0x70, 0x2e, 0x6b, 0x2d, 0xb5, 0x8d, 0xbb, 0x9b, 0x66, 0x55, 0x8d, 0xb4, 0x95, 0x67, 0x46, + 0x82, 0xd1, 0x38, 0x70, 0x56, 0xa2, 0x5e, 0x45, 0x9b, 0x34, 0xdb, 0x2c, 0xbb, 0xd8, 0x75, 0xf2, + 0x20, 0xa3, 0x0f, 0xcb, 0xcf, 0x83, 0x17, 0xef, 0x50, 0x71, 0x35, 0x36, 0xfd, 0x00, 0xed, 0x8f, + 0xfa, 0xef, 0x07, 0xee, 0xf9, 0x45, 0xb7, 0xe7, 0x7e, 0x1e, 0x8c, 0x86, 0xbd, 0x4e, 0xff, 0xac, + 0xdf, 0xeb, 0x56, 0x0a, 0xba, 0x81, 0xaa, 0xf7, 0xa9, 0xce, 0x87, 0x5e, 0xe7, 0xe3, 0xf0, 0xa2, + 0x3f, 0xb8, 0xac, 0x68, 0xed, 0xcb, 0xdf, 0x0b, 0x53, 0xbb, 0x5d, 0x98, 0xda, 0xdf, 0x85, 0xa9, + 0xfd, 0x5c, 0x9a, 0x85, 0xdb, 0xa5, 0x59, 0xf8, 0xb3, 0x34, 0x0b, 0x5f, 0x5f, 0x07, 0x54, 0x4c, + 0xd3, 0xb1, 0xe5, 0x41, 0x64, 0xab, 0x0b, 0xb3, 0x03, 0x68, 0x92, 0x6b, 0xe2, 0xa5, 0xd9, 0x65, + 0x34, 0xf1, 0xd8, 0xa3, 0x76, 0x04, 0x7e, 0x1a, 0x12, 0xfe, 0xff, 0xad, 0xc8, 0x87, 0x32, 0xde, + 0x92, 0x7f, 0xf0, 0xcb, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x77, 0x78, 0xad, 0x26, 0x4e, 0x03, + 0x00, 0x00, } func (m *Params) Marshal() (dAtA []byte, err error) { @@ -367,14 +350,6 @@ func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - n1, err1 := github_com_cosmos_gogoproto_types.StdDurationMarshalTo(m.MaxClockDrift, dAtA[i-github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.MaxClockDrift):]) - if err1 != nil { - return 0, err1 - } - i -= n1 - i = encodeVarintTypes(dAtA, i, uint64(n1)) - i-- - dAtA[i] = 0x32 if m.SignMode != 0 { i = encodeVarintTypes(dAtA, i, uint64(m.SignMode)) i-- @@ -536,8 +511,6 @@ func (m *Params) Size() (n int) { if m.SignMode != 0 { n += 1 + sovTypes(uint64(m.SignMode)) } - l = github_com_cosmos_gogoproto_types.SizeOfStdDuration(m.MaxClockDrift) - n += 1 + l + sovTypes(uint64(l)) return n } @@ -741,39 +714,6 @@ func (m *Params) Unmarshal(dAtA []byte) error { break } } - case 6: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field MaxClockDrift", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthTypes - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthTypes - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if err := github_com_cosmos_gogoproto_types.StdDurationUnmarshal(&m.MaxClockDrift, dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) diff --git a/modules/proto/rollkitsdk/network/v1/types.proto b/modules/proto/rollkitsdk/network/v1/types.proto index d0dc7291..625d527c 100644 --- a/modules/proto/rollkitsdk/network/v1/types.proto +++ b/modules/proto/rollkitsdk/network/v1/types.proto @@ -7,8 +7,6 @@ option go_package = "github.com/rollkit/go-execution-abci/modules/network/types" import "gogoproto/gogo.proto"; import "cosmos_proto/cosmos.proto"; import "google/protobuf/duration.proto"; -import "amino/amino.proto"; - // Params defines the parameters for the network module. message Params { @@ -26,11 +24,6 @@ message Params { // sign_mode determines when validators must sign SignMode sign_mode = 5; - - // max_clock_drift maximum a timestamp for a vote can drift - google.protobuf.Duration max_clock_drift = 6 - [(gogoproto.stdduration) = true, (gogoproto.nullable) = false, (amino.dont_omitempty) = true]; - } // SignMode defines when validators must sign From 42a6efc41d8a4f08aefe696ac481d9a83d58217f Mon Sep 17 00:00:00 2001 From: Alex Peters Date: Thu, 3 Jul 2025 09:12:04 +0200 Subject: [PATCH 16/17] Validate val address --- modules/network/keeper/msg_server.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/modules/network/keeper/msg_server.go b/modules/network/keeper/msg_server.go index 96dae215..1e89a0f3 100644 --- a/modules/network/keeper/msg_server.go +++ b/modules/network/keeper/msg_server.go @@ -155,18 +155,17 @@ func (k msgServer) verifyVote(ctx sdk.Context, msg *types.MsgAttest) (*cmtproto. if msg.Height != vote.Height { return nil, sdkerr.Wrapf(sdkerrors.ErrInvalidRequest, "vote height does not match attestation height") } - if senderAddr, err := sdk.ValAddressFromBech32(msg.Validator); err != nil { - return nil, sdkerr.Wrapf(sdkerrors.ErrInvalidRequest, "invalid validator address: %s", err) - } else if !bytes.Equal(senderAddr, vote.ValidatorAddress) { - return nil, sdkerr.Wrapf(sdkerrors.ErrUnauthorized, "vote validator address does not match attestation validator address") - } if len(vote.Signature) == 0 { return nil, sdkerrors.ErrInvalidRequest.Wrap("empty signature") } // todo (Alex): validate app hash match, vote clock drift - validator, err := k.stakingKeeper.GetValidator(ctx, vote.ValidatorAddress) + valAddress, err := sdk.ValAddressFromBech32(msg.Validator) + if err != nil { + return nil, sdkerr.Wrap(err, "invalid validator address") + } + validator, err := k.stakingKeeper.GetValidator(ctx, valAddress) if err != nil { return nil, sdkerr.Wrapf(err, "get validator") } @@ -174,7 +173,7 @@ func (k msgServer) verifyVote(ctx sdk.Context, msg *types.MsgAttest) (*cmtproto. if err != nil { return nil, sdkerr.Wrapf(err, "pubkey") } - if !bytes.Equal(pubKey.Address(), vote.ValidatorAddress) { + if !bytes.Equal(pubKey.Address().Bytes(), vote.ValidatorAddress) { return nil, sdkerr.Wrapf(sdkerrors.ErrInvalidRequest, "pubkey address does not match validator address") } voteSignBytes := cmttypes.VoteSignBytes(ctx.ChainID(), &vote) From 8bf214472b760190d5580df06bd7d19f8785958f Mon Sep 17 00:00:00 2001 From: Alex Peters Date: Tue, 8 Jul 2025 15:48:08 +0200 Subject: [PATCH 17/17] WIP --- hack/attester/main.go | 9 +- hack/ibc-connection-rly.sh | 10 +- modules/network/keeper/grpc_query.go | 12 +- modules/network/keeper/keeper.go | 22 ++- modules/network/keeper/msg_server.go | 12 +- modules/network/keeper/msg_server_test.go | 4 +- modules/network/types/params.go | 2 +- pkg/rpc/core/blocks.go | 166 +++++++--------------- 8 files changed, 95 insertions(+), 142 deletions(-) diff --git a/hack/attester/main.go b/hack/attester/main.go index 452e9faf..c8025561 100644 --- a/hack/attester/main.go +++ b/hack/attester/main.go @@ -335,6 +335,8 @@ func containsSpace(s string) bool { return false } +var accSeq uint64 = 0 + // broadcastTx executes a command to broadcast a transaction using the Cosmos SDK func broadcastTx(ctx context.Context, chainID, nodeAddr string, msg proto.Message, privKey *secp256k1.PrivKey, verbose bool) (string, error) { // Get validator address from private key @@ -375,7 +377,10 @@ func broadcastTx(ctx context.Context, chainID, nodeAddr string, msg proto.Messag } fmt.Printf("+++ chainid: %s, GetAccountNumber: %d\n", chainID, account.GetAccountNumber()) // Sign transaction using account sequence - accSeq := account.GetSequence() + if accSeq == 0 { + accSeq = account.GetSequence() + } + signerData := authsigning.SignerData{ Address: addr.String(), ChainID: chainID, @@ -448,7 +453,7 @@ func broadcastTx(ctx context.Context, chainID, nodeAddr string, msg proto.Messag if err != nil { return "", fmt.Errorf("broadcasting transaction: %w", err) } - + accSeq++ // Check if the transaction was successful if resp.Code != 0 { return "", fmt.Errorf("transaction failed with code %d: %s", resp.Code, resp.RawLog) diff --git a/hack/ibc-connection-rly.sh b/hack/ibc-connection-rly.sh index 910a46b2..fca0d882 100755 --- a/hack/ibc-connection-rly.sh +++ b/hack/ibc-connection-rly.sh @@ -64,8 +64,9 @@ cat < gaia-config.json "grpc-addr": "$GAIA_GRPC", "account-prefix": "cosmos", "keyring-backend": "test", - "gas-adjustment": 2.0, - "gas-prices": "0.1$GAIA_DENOM", + "dynamic-gas-price": true, + "gas-adjustment": 1.4, + "gas-prices": "3.5$GAIA_DENOM", "debug": true, "timeout": "10s", "output-format": "json", @@ -88,8 +89,9 @@ cat < wordled-config.json "grpc-addr": "$WORDLED_GRPC", "account-prefix": "gm", "keyring-backend": "test", - "gas-adjustment": 2.0, - "gas-prices": "0.1$WORDLED_DENOM", + "dynamic-gas-price": true, + "gas-adjustment": 1.4, + "gas-prices": "0.025$WORDLED_DENOM", "debug": true, "timeout": "10s", "output-format": "json", diff --git a/modules/network/keeper/grpc_query.go b/modules/network/keeper/grpc_query.go index 5135a8f0..e1c87a99 100644 --- a/modules/network/keeper/grpc_query.go +++ b/modules/network/keeper/grpc_query.go @@ -193,13 +193,17 @@ func (q *queryServer) SoftConfirmationStatus(c context.Context, req *types.Query // ValidatorSignature queries the signature of a validator for a specific height func (q *queryServer) ValidatorSignature(c context.Context, req *types.QueryValidatorSignatureRequest) (*types.QueryValidatorSignatureResponse, error) { + // TODO (Alex): refactor to vote + if req == nil { return nil, status.Error(codes.InvalidArgument, "invalid request") } + fmt.Printf("+++ enpoint ValidatorSignature addr: %X, height: %d\n", []byte(req.Validator), req.BlockHeight) + ctx := sdk.UnwrapSDKContext(c) - signature, err := q.keeper.GetSignature(ctx, req.BlockHeight, req.Validator) + signature, err := q.keeper.GetVote(ctx, req.BlockHeight, []byte(req.Validator)) if err != nil { if errors.Is(err, collections.ErrNotFound) { return &types.QueryValidatorSignatureResponse{ @@ -210,8 +214,12 @@ func (q *queryServer) ValidatorSignature(c context.Context, req *types.QueryVali return nil, status.Error(codes.Internal, fmt.Sprintf("failed to get signature: %v", err)) } + voteBz, err := signature.Marshal() + if err != nil { + return nil, status.Error(codes.Internal, fmt.Sprintf("marshal vote: %v", err)) + } return &types.QueryValidatorSignatureResponse{ - Signature: signature, + Signature: voteBz, Found: true, }, nil } diff --git a/modules/network/keeper/keeper.go b/modules/network/keeper/keeper.go index 05d22a85..4b0c017d 100644 --- a/modules/network/keeper/keeper.go +++ b/modules/network/keeper/keeper.go @@ -3,6 +3,7 @@ package keeper import ( "errors" "fmt" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" "cosmossdk.io/collections" "cosmossdk.io/core/store" @@ -29,7 +30,7 @@ type Keeper struct { AttestationBitmap collections.Map[int64, []byte] EpochBitmap collections.Map[uint64, []byte] AttesterSet collections.KeySet[string] - Signatures collections.Map[collections.Pair[int64, string], []byte] + Votes collections.Map[collections.Pair[int64, []byte], cmtproto.Vote] StoredAttestationInfo collections.Map[int64, types.AttestationBitmap] Params collections.Item[types.Params] Schema collections.Schema @@ -59,7 +60,7 @@ func NewKeeper( AttestationBitmap: collections.NewMap(sb, types.AttestationBitmapPrefix, "attestation_bitmap", collections.Int64Key, collections.BytesValue), EpochBitmap: collections.NewMap(sb, types.EpochBitmapPrefix, "epoch_bitmap", collections.Uint64Key, collections.BytesValue), AttesterSet: collections.NewKeySet(sb, types.AttesterSetPrefix, "attester_set", collections.StringKey), - Signatures: collections.NewMap(sb, types.SignaturePrefix, "signatures", collections.PairKeyCodec(collections.Int64Key, collections.StringKey), collections.BytesValue), + Votes: collections.NewMap(sb, types.SignaturePrefix, "votes", collections.PairKeyCodec(collections.Int64Key, collections.BytesKey), codec.CollValue[cmtproto.Vote](cdc)), StoredAttestationInfo: collections.NewMap(sb, types.StoredAttestationInfoPrefix, "stored_attestation_info", collections.Int64Key, codec.CollValue[types.AttestationBitmap](cdc)), // Initialize new collection Params: collections.NewItem(sb, types.ParamsKey, "params", codec.CollValue[types.Params](cdc)), } @@ -309,17 +310,14 @@ func (k Keeper) PruneOldBitmaps(ctx sdk.Context, currentEpoch uint64) error { return nil } -// SetSignature stores the vote signature for a given height and validator -func (k Keeper) SetSignature(ctx sdk.Context, height int64, validatorAddr string, signature []byte) error { - return k.Signatures.Set(ctx, collections.Join(height, validatorAddr), signature) -} +// SetVote stores the vote signature for a given height and validator +func (k Keeper) SetVote(ctx sdk.Context, height int64, validatorAddr []byte, signature cmtproto.Vote) error { + fmt.Printf("+++ SetVote addr: %X, height: %d\n", validatorAddr, height) -// GetSignature retrieves the vote signature for a given height and validator -func (k Keeper) GetSignature(ctx sdk.Context, height int64, validatorAddr string) ([]byte, error) { - return k.Signatures.Get(ctx, collections.Join(height, validatorAddr)) + return k.Votes.Set(ctx, collections.Join(height, validatorAddr), signature) } -// HasSignature checks if a signature exists for a given height and validator -func (k Keeper) HasSignature(ctx sdk.Context, height int64, validatorAddr string) (bool, error) { - return k.Signatures.Has(ctx, collections.Join(height, validatorAddr)) +// GetVote retrieves the vote signature for a given height and validator +func (k Keeper) GetVote(ctx sdk.Context, height int64, validatorAddr []byte) (cmtproto.Vote, error) { + return k.Votes.Get(ctx, collections.Join(height, validatorAddr)) } diff --git a/modules/network/keeper/msg_server.go b/modules/network/keeper/msg_server.go index 1e89a0f3..84c1d3a2 100644 --- a/modules/network/keeper/msg_server.go +++ b/modules/network/keeper/msg_server.go @@ -36,33 +36,41 @@ func (k msgServer) Attest(goCtx context.Context, msg *types.MsgAttest) (*types.M if err := k.validateAttestation(ctx, msg); err != nil { return nil, err } + println("+++ 1") // can vote only for the last epoch - if delta := ctx.BlockHeight() - msg.Height; delta < 0 || delta > int64(k.GetParams(ctx).EpochLength) { + if delta := ctx.BlockHeight() - msg.Height; delta < 0 || delta > int64(k.GetParams(ctx).EpochLength)*2 { // todo (Alex): does factor 2 make sense? return nil, sdkerr.Wrapf(sdkerrors.ErrInvalidRequest, "exceeded voting window: %d blocks", delta) } + println("+++ 2") valIndexPos, found := k.GetValidatorIndex(ctx, msg.Validator) if !found { return nil, sdkerr.Wrapf(sdkerrors.ErrNotFound, "validator index not found for %s", msg.Validator) } + println("+++ 3") vote, err := k.verifyVote(ctx, msg) if err != nil { return nil, err } + println("+++ 4") if err := k.updateAttestationBitmap(ctx, msg, valIndexPos); err != nil { return nil, sdkerr.Wrap(err, "update attestation bitmap") } - if err := k.SetSignature(ctx, msg.Height, msg.Validator, vote.Signature); err != nil { + println("+++ 5") + if err := k.SetVote(ctx, msg.Height, vote.ValidatorAddress, *vote); err != nil { + println("+++ 5b") return nil, sdkerr.Wrap(err, "store signature") } + println("+++ 6") if err := k.updateEpochBitmap(ctx, uint64(msg.Height), valIndexPos); err != nil { return nil, err } + println("+++ 7") // Emit event ctx.EventManager().EmitEvent( sdk.NewEvent( diff --git a/modules/network/keeper/msg_server_test.go b/modules/network/keeper/msg_server_test.go index c91b9b38..fcd7fad0 100644 --- a/modules/network/keeper/msg_server_test.go +++ b/modules/network/keeper/msg_server_test.go @@ -249,7 +249,7 @@ func TestAttest(t *testing.T) { require.Error(t, gotErr) require.ErrorIs(t, gotErr, spec.expErr) // and ensure the signature is not stored - _, err := testEnv.Keeper.GetSignature(ctx, srcMsg.Height, valAddrStr) + _, err := testEnv.Keeper.GetVote(ctx, srcMsg.Height, valAddrStr) assert.ErrorIs(t, err, collections.ErrNotFound) return } @@ -264,7 +264,7 @@ func TestAttest(t *testing.T) { require.Equal(t, byte(1), bitmap[0]) // and the signature was stored properly - gotSig, err := testEnv.Keeper.GetSignature(ctx, srcMsg.Height, valAddrStr) + gotSig, err := testEnv.Keeper.GetVote(ctx, srcMsg.Height, valAddrStr) require.NoError(t, err) var vote cmtproto.Vote require.NoError(t, proto.Unmarshal(srcMsg.Vote, &vote)) diff --git a/modules/network/types/params.go b/modules/network/types/params.go index f7a68664..8f12acaf 100644 --- a/modules/network/types/params.go +++ b/modules/network/types/params.go @@ -18,7 +18,7 @@ var ( // Default parameter values var ( - DefaultEpochLength = uint64(10) // todo (Alex): what is a good default? + DefaultEpochLength = uint64(1) // todo (Alex): what is a good default? DefaultQuorumFraction = math.LegacyNewDecWithPrec(667, 3) // 2/3 DefaultMinParticipation = math.LegacyNewDecWithPrec(5, 1) // 1/2 DefaultPruneAfter = uint64(7) diff --git a/pkg/rpc/core/blocks.go b/pkg/rpc/core/blocks.go index 00ce0310..fd1e5427 100644 --- a/pkg/rpc/core/blocks.go +++ b/pkg/rpc/core/blocks.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "sort" - "time" cmbytes "github.com/cometbft/cometbft/libs/bytes" cmquery "github.com/cometbft/cometbft/libs/pubsub/query" @@ -14,7 +13,7 @@ import ( ctypes "github.com/cometbft/cometbft/rpc/core/types" rpctypes "github.com/cometbft/cometbft/rpc/jsonrpc/types" cmttypes "github.com/cometbft/cometbft/types" - + "github.com/cosmos/gogoproto/proto" storepkg "github.com/rollkit/rollkit/pkg/store" rlktypes "github.com/rollkit/rollkit/types" @@ -129,73 +128,14 @@ func Block(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlock, error) } } - header, data, err := env.Adapter.RollkitStore.GetBlockData(ctx.Context(), heightValue) + block, err := xxxBlock(ctx, heightValue) if err != nil { return nil, err } - // Try to get attestation-based commit for the previous block first, fall back to getLastCommit if not available - var lastCommit *cmttypes.Commit - if heightValue > 0 { - previousHeight := heightValue - 1 - isSoftConfirmed, softConfirmationData, err := checkSoftConfirmation(ctx.Context(), previousHeight) - if err == nil && isSoftConfirmed && softConfirmationData != nil { - // Build commit with real signatures from attestations for the previous block - lastCommit, err = buildCommitFromAttestations(ctx.Context(), previousHeight, softConfirmationData) - if err != nil { - // Fall back to regular lastCommit if attestation-based commit fails - env.Logger.Debug("failed to build commit from attestations, falling back to getLastCommit", "height", previousHeight, "error", err) - lastCommit, err = getLastCommit(ctx.Context(), heightValue) - if err != nil { - return nil, fmt.Errorf("failed to get last commit for block %d: %w", heightValue, err) - } - } - } else { - // Use regular lastCommit - lastCommit, err = getLastCommit(ctx.Context(), heightValue) - if err != nil { - return nil, fmt.Errorf("failed to get last commit for block %d: %w", heightValue, err) - } - } - } else { - // For genesis block (height 0), there's no previous block, so no lastCommit - lastCommit = nil - } - - block, err := cometcompat.ToABCIBlock(header, data, lastCommit) - if err != nil { - return nil, err + if len(block.LastCommit.Signatures) == 0 { + return nil, nil // not found } - - // Then re-sign the final ABCI header if we have a signer - if env.Signer != nil { - // Create a vote for the final ABCI header - vote := cmtproto.Vote{ - Type: cmtproto.PrecommitType, - Height: int64(header.Height()), //nolint:gosec - Round: 0, - BlockID: cmtproto.BlockID{ - Hash: block.Header.Hash(), - PartSetHeader: cmtproto.PartSetHeader{}, - }, - Timestamp: block.Time, - ValidatorAddress: header.ProposerAddress, - ValidatorIndex: 0, - } - chainID := header.ChainID() - finalSignBytes := cmttypes.VoteSignBytes(chainID, &vote) - - newSignature, err := env.Signer.Sign(finalSignBytes) - if err != nil { - return nil, fmt.Errorf("failed to sign final ABCI header: %w", err) - } - - // Update the signature in the block - if len(block.LastCommit.Signatures) > 0 { - block.LastCommit.Signatures[0].Signature = newSignature - } - } - return &ctypes.ResultBlock{ BlockID: cmttypes.BlockID{Hash: block.Hash()}, Block: block, @@ -210,32 +150,9 @@ func BlockByHash(ctx *rpctypes.Context, hash []byte) (*ctypes.ResultBlock, error return nil, err } - // Try to get attestation-based commit for the previous block first, fall back to getLastCommit if not available - var lastCommit *cmttypes.Commit - if header.Height() > 0 { - previousHeight := header.Height() - 1 - isSoftConfirmed, softConfirmationData, err := checkSoftConfirmation(ctx.Context(), previousHeight) - if err == nil && isSoftConfirmed && softConfirmationData != nil { - // Build commit with real signatures from attestations for the previous block - lastCommit, err = buildCommitFromAttestations(ctx.Context(), previousHeight, softConfirmationData) - if err != nil { - // Fall back to regular lastCommit if attestation-based commit fails - env.Logger.Debug("failed to build commit from attestations, falling back to getLastCommit", "height", previousHeight, "error", err) - lastCommit, err = getLastCommit(ctx.Context(), header.Height()) - if err != nil { - return nil, fmt.Errorf("failed to get last commit for block %d: %w", header.Height(), err) - } - } - } else { - // Use regular lastCommit - lastCommit, err = getLastCommit(ctx.Context(), header.Height()) - if err != nil { - return nil, fmt.Errorf("failed to get last commit for block %d: %w", header.Height(), err) - } - } - } else { - // For genesis block (height 0), there's no previous block, so no lastCommit - lastCommit = nil + lastCommit, err := getLastCommit(ctx.Context(), header.Height()) + if err != nil { + return nil, fmt.Errorf("failed to get last commit for block %d: %w", header.Height(), err) } block, err := cometcompat.ToABCIBlock(header, data, lastCommit) @@ -264,10 +181,26 @@ func Commit(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultCommit, erro return nil, err } + block, err := xxxBlock(ctx, height) + if err != nil { + return nil, err + } + + return &ctypes.ResultCommit{ + SignedHeader: cmttypes.SignedHeader{ + Header: &block.Header, + Commit: block.LastCommit, + }, + CanonicalCommit: true, + }, nil +} + +func xxxBlock(ctx *rpctypes.Context, height uint64) (*cmttypes.Block, error) { + fmt.Printf("+++ height: %d\n", height) // Check if the block has soft confirmation first isSoftConfirmed, softConfirmationData, err := checkSoftConfirmation(ctx.Context(), height) if err != nil { - return nil, fmt.Errorf("failed to check soft confirmation status: %w", err) + return nil, fmt.Errorf("check soft confirmation status: %w", err) } //if !isSoftConfirmed { @@ -283,7 +216,7 @@ func Commit(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultCommit, erro // Build commit with attestations from soft confirmation data commit, err := buildCommitFromAttestations(ctx.Context(), height, softConfirmationData) if err != nil { - return nil, fmt.Errorf("failed to build commit from attestations: %w", err) + return nil, fmt.Errorf("build commit from attestations: %w", err) } block, err := cometcompat.ToABCIBlock(header, rollkitData, commit) @@ -294,14 +227,7 @@ func Commit(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultCommit, erro // Update the commit's BlockID to match the final ABCI block hash block.LastCommit.BlockID.Hash = block.Header.Hash() block.LastCommit.BlockID.PartSetHeader.Hash = block.LastCommit.BlockID.Hash - - return &ctypes.ResultCommit{ - SignedHeader: cmttypes.SignedHeader{ - Header: &block.Header, - Commit: block.LastCommit, - }, - CanonicalCommit: true, - }, nil + return block, nil } // BlockResults gets block results at a given height. @@ -477,12 +403,12 @@ func checkSoftConfirmation(ctx context.Context, height uint64) (bool, *networkty return false, nil, fmt.Errorf("failed to query attestation bitmap: %w", err) } - attestationResp := &networktypes.QueryAttestationBitmapResponse{} + var attestationResp networktypes.QueryAttestationBitmapResponse if err := attestationResp.Unmarshal(abciRes.Value); err != nil { return false, nil, fmt.Errorf("failed to unmarshal attestation bitmap response: %w", err) } - return true, attestationResp, nil + return true, &attestationResp, nil } // buildCommitFromAttestations constructs a commit with real signatures from attestations @@ -504,22 +430,20 @@ func buildCommitFromAttestations(ctx context.Context, height uint64, attestation for i, genesisValidator := range genesisValidators { // Check if this validator voted (bit is set in bitmap) if attestationData != nil && i < len(bitmap)*8 && (bitmap[i/8]&(1<<(i%8))) != 0 { - // This validator voted, get their signature - vote := cmttypes.CommitSig{ - BlockIDFlag: cmttypes.BlockIDFlagCommit, - ValidatorAddress: genesisValidator.Address, // Use real validator address - Timestamp: time.Now(), // Should be actual timestamp from attestation - Signature: nil, // We'll get this from the query below + // Try to get the real signature using the validator's address + validatorAddr := string(genesisValidator.Address.Bytes()) // todo (Alex): use proper format + vote, err := getValidatorSignatureFromQuery(ctx, int64(height), validatorAddr) + if err != nil { + return nil, fmt.Errorf("get validator signature for height %d: %w", height, err) } - // Try to get the real signature using the validator's address - validatorAddr := genesisValidator.Address.String() - signature, err := getValidatorSignatureFromQuery(ctx, int64(height), validatorAddr) - if err == nil { - vote.Signature = signature + votes[i] = cmttypes.CommitSig{ + BlockIDFlag: cmttypes.BlockIDFlagCommit, + ValidatorAddress: vote.ValidatorAddress, + Timestamp: vote.Timestamp, + Signature: vote.Signature, } - votes[i] = vote } else { // Validator didn't vote, add absent vote votes[i] = cmttypes.CommitSig{ @@ -545,7 +469,7 @@ func buildCommitFromAttestations(ctx context.Context, height uint64, attestation } // getValidatorSignatureFromQuery queries the signature for a specific validator -func getValidatorSignatureFromQuery(ctx context.Context, height int64, validatorAddr string) ([]byte, error) { +func getValidatorSignatureFromQuery(ctx context.Context, height int64, validatorAddr string) (*cmtproto.Vote, error) { sigReq := &networktypes.QueryValidatorSignatureRequest{ BlockHeight: height, Validator: validatorAddr, @@ -556,6 +480,8 @@ func getValidatorSignatureFromQuery(ctx context.Context, height int64, validator return nil, fmt.Errorf("failed to marshal signature request: %w", err) } + fmt.Printf("+++ getValidatorSignatureFromQuery addr: %X, height: %d\n", []byte(validatorAddr), height) + abciReq := &abci.RequestQuery{ Path: "/rollkitsdk.network.v1.Query/ValidatorSignature", Data: reqData, @@ -573,6 +499,12 @@ func getValidatorSignatureFromQuery(ctx context.Context, height int64, validator if err := sigResp.Unmarshal(res.Value); err != nil { return nil, fmt.Errorf("failed to unmarshal signature response: %w", err) } - - return sigResp.Signature, nil + if !sigResp.Found { + return nil, fmt.Errorf("vote not found") + } + var vote cmtproto.Vote + if err := proto.Unmarshal(sigResp.Signature, &vote); err != nil { + return nil, fmt.Errorf("failed to unmarshal signature payload: %w", err) + } + return &vote, nil }