diff --git a/modules/rollkitmngr/keeper/rollkit_block_test.go b/modules/rollkitmngr/keeper/rollkit_block_test.go new file mode 100644 index 00000000..94eda251 --- /dev/null +++ b/modules/rollkitmngr/keeper/rollkit_block_test.go @@ -0,0 +1,508 @@ +package keeper_test + +import ( + "context" + "testing" + "time" + + "github.com/cometbft/cometbft/crypto/ed25519" + "github.com/cometbft/cometbft/libs/math" + "github.com/cometbft/cometbft/light" + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + cmttypes "github.com/cometbft/cometbft/types" + "github.com/libp2p/go-libp2p/core/crypto" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + + "github.com/rollkit/go-execution-abci/pkg/cometcompat" + "github.com/rollkit/rollkit/pkg/signer/noop" + rollkittypes "github.com/rollkit/rollkit/types" +) + +// ValidatorInfo holds information about a validator +type ValidatorInfo struct { + PrivKey crypto.PrivKey + PubKey crypto.PubKey + CmtPrivKey ed25519.PrivKey + CmtPubKey ed25519.PubKey + Signer *noop.NoopSigner + Address []byte + CmtAddress []byte + CmtValidator *cmttypes.Validator +} + +// MockRollkitStore implements a mock for RollkitStore interface +type MockRollkitStore struct { + mock.Mock +} + +func (m *MockRollkitStore) GetBlockData(ctx context.Context, height uint64) (*rollkittypes.SignedHeader, *rollkittypes.Data, error) { + args := m.Called(ctx, height) + return args.Get(0).(*rollkittypes.SignedHeader), args.Get(1).(*rollkittypes.Data), args.Error(2) +} + +func (m *MockRollkitStore) Height(ctx context.Context) (uint64, error) { + args := m.Called(ctx) + return args.Get(0).(uint64), args.Error(1) +} + +func (m *MockRollkitStore) Close() error { + args := m.Called() + return args.Error(0) +} + +func (m *MockRollkitStore) GetBlockByHash(ctx context.Context, hash []byte) (*rollkittypes.SignedHeader, *rollkittypes.Data, error) { + args := m.Called(ctx, hash) + return args.Get(0).(*rollkittypes.SignedHeader), args.Get(1).(*rollkittypes.Data), args.Error(2) +} + +func (m *MockRollkitStore) GetMetadata(ctx context.Context, key string) ([]byte, error) { + args := m.Called(ctx, key) + return args.Get(0).([]byte), args.Error(1) +} + +func (m *MockRollkitStore) GetState(ctx context.Context) (*rollkittypes.State, error) { + args := m.Called(ctx) + return args.Get(0).(*rollkittypes.State), args.Error(1) +} + +func (m *MockRollkitStore) GetSignature(ctx context.Context, height uint64) (*rollkittypes.Signature, error) { + args := m.Called(ctx, height) + return args.Get(0).(*rollkittypes.Signature), args.Error(1) +} + +func (m *MockRollkitStore) GetSignatureByHash(ctx context.Context, hash []byte) (*rollkittypes.Signature, error) { + args := m.Called(ctx, hash) + return args.Get(0).(*rollkittypes.Signature), args.Error(1) +} + +// MockSigner implements the signer.Signer interface for testing +type MockSigner struct { + mock.Mock +} + +func (m *MockSigner) Sign(message []byte) ([]byte, error) { + args := m.Called(message) + if fn, ok := args.Get(0).(func([]byte) []byte); ok { + return fn(message), args.Error(1) + } + return args.Get(0).([]byte), args.Error(1) +} + +func (m *MockSigner) GetPublic() (crypto.PubKey, error) { + args := m.Called() + return args.Get(0).(crypto.PubKey), args.Error(1) +} + +func (m *MockSigner) GetAddress() ([]byte, error) { + args := m.Called() + return args.Get(0).([]byte), args.Error(1) +} + +// TestRollkitConsecutiveBlocksWithMultipleValidators tests using the real Commit RPC endpoint +func TestRollkitConsecutiveBlocksWithMultipleValidators(t *testing.T) { + chainID := "test-multi-validator-chain" + + // ========================= + // SETUP: CREATE 3 VALIDATORS + // ========================= + t.Log("🔧 Setting up validator set with 3 validators...") + + validators := make([]*ValidatorInfo, 3) + cmtValidators := make([]*cmttypes.Validator, 3) + + // Create 3 validators + for i := 0; i < 3; i++ { + // Create CometBFT keys first + cmtPrivKey := ed25519.GenPrivKey() + cmtPubKey := cmtPrivKey.PubKey() + + // Convert CometBFT private key to libp2p for compatibility + privKey, err := crypto.UnmarshalEd25519PrivateKey(cmtPrivKey.Bytes()) + require.NoError(t, err, "Failed to convert CometBFT key to libp2p for validator %d", i) + + // Create noop signer + signer, err := noop.NewNoopSigner(privKey) + require.NoError(t, err, "Failed to create signer for validator %d", i) + + // Get Rollkit address (32 bytes) for compatibility with GetFirstSignedHeader + address, err := signer.GetAddress() + require.NoError(t, err, "Failed to get address for validator %d", i) + + // Create CometBFT validator with equal voting power + cmtValidator := cmttypes.NewValidator(cmtPubKey, 1) + + validators[i] = &ValidatorInfo{ + PrivKey: privKey, + PubKey: privKey.GetPublic(), + CmtPrivKey: cmtPrivKey, + CmtPubKey: cmtPubKey.(ed25519.PubKey), + Signer: signer.(*noop.NoopSigner), + Address: address, + CmtAddress: cmtPubKey.Address(), + CmtValidator: cmtValidator, + } + cmtValidators[i] = cmtValidator + + t.Logf(" ✅ Validator %d: Address %x", i, address) + } + + // Sequencer is the first validator + sequencer := validators[0] + + // Create a validator set for light client verification + valSet := &cmttypes.ValidatorSet{ + Validators: cmtValidators, + Proposer: cmtValidators[0], // Sequencer is the proposer + } + + // ========================= + // SETUP RPC ENVIRONMENT (exactly like blocks_test.go) + // ========================= + + // Create mock signer that will be used by the RPC Commit endpoint + mockSigner := &MockSigner{} + mockSigner.On("Sign", mock.AnythingOfType("[]uint8")).Return(func(message []byte) []byte { + // Sign with the sequencer's private key + signature, _ := sequencer.CmtPrivKey.Sign(message) + return signature + }, nil) + mockSigner.On("GetPublic").Return(sequencer.PubKey, nil) + mockSigner.On("GetAddress").Return(sequencer.Address, nil) + + // Create mock rollkit store + mockRollkitStore := &MockRollkitStore{} + + t.Log("✨ Simulating multi-validator commits and verifying with light client...") + t.Log("🔐 Using ABCI-compatible signature payload provider for realistic signing") + + // ========================= + // CREATE BLOCKS WITH MULTI-VALIDATOR SUPPORT + // ========================= + + blocks := make([]*rollkittypes.SignedHeader, 3) + blockData := make([]*rollkittypes.Data, 3) + + var lastCommit *cmttypes.Commit + var trustedHeader *cmttypes.SignedHeader + + // Create 3 blocks + for i := 0; i < 3; i++ { + height := uint64(i + 1) + + var block *rollkittypes.SignedHeader + var data *rollkittypes.Data + var err error + + if i == 0 { + // Genesis block + t.Logf("📦 Creating Genesis Block (Height %d)...", height) + block, err = rollkittypes.GetFirstSignedHeader(sequencer.Signer, chainID) + require.NoError(t, err, "Failed to create genesis block") + lastCommit = &cmttypes.Commit{Height: 0} + } else { + // Subsequent blocks + t.Logf("📦 Creating Block %d (Height %d)...", i+1, height) + block, err = rollkittypes.GetRandomNextSignedHeader(blocks[i-1], sequencer.Signer, chainID) + require.NoError(t, err, "Failed to create block %d", i+1) + } + + // Create block data + data = &rollkittypes.Data{ + Txs: make(rollkittypes.Txs, i+3), // Variable number of transactions + } + for j := range data.Txs { + data.Txs[j] = rollkittypes.GetRandomTx() + } + + // Update block with proper data hash + block.DataHash = data.DACommitment() + + // Re-sign block with updated data hash using ABCI payload provider + payloadProvider := cometcompat.PayloadProvider() + payload, err := payloadProvider(&block.Header) + require.NoError(t, err, "ABCI payload generation should succeed for block %d", i+1) + + signature, err := sequencer.Signer.Sign(payload) + require.NoError(t, err, "Block %d signing should succeed", i+1) + block.Signature = signature + + // Set custom verifier + err = block.SetCustomVerifier(rollkittypes.SignatureVerifier(payloadProvider)) + require.NoError(t, err, "Block %d custom verifier should be set", i+1) + + // Validate block + err = block.ValidateBasic() + require.NoError(t, err, "Block %d validation should pass", i+1) + + blocks[i] = block + blockData[i] = data + + // Mock the store to return our block for the RPC call (exactly like blocks_test.go) + mockRollkitStore.On("GetBlockData", mock.Anything, height).Return(block, data, nil).Once() + mockRollkitStore.On("Height", mock.Anything).Return(height, nil).Maybe() + + t.Logf("✅ Block %d created: Height=%d, Hash=%x, Transactions=%d", + i+1, block.Height(), block.Hash(), len(data.Txs)) + + // ========================= + // MULTI-VALIDATOR COMMIT & LIGHT CLIENT VERIFICATION + // ========================= + + // Create a copy of the block to modify for CometBFT compatibility + cometCompatBlock := *block + cometCompatBlock.ProposerAddress = sequencer.CmtAddress + + // Get the block hash (BlockID) for signing the commit + cometBlock, err := cometcompat.ToABCIBlock(&cometCompatBlock, data, lastCommit, valSet) + require.NoError(t, err, "Failed to convert to CometBFT block for block %d", i+1) + blockID := cmttypes.BlockID{Hash: cometBlock.Hash()} + + // Create a commit with signatures from all validators + currentCommit := createMultiValidatorCommit(t, &block.Header, blockID, valSet, validators, chainID) + + // Create a CometBFT signed header for light client verification + cmtSignedHeader := &cmttypes.SignedHeader{ + Header: &cometBlock.Header, + Commit: currentCommit, + } + + // Perform light client verification + if i == 0 { + // For the first block, we trust it and verify its own commit + trustedHeader = cmtSignedHeader + err = valSet.VerifyCommitLight(chainID, trustedHeader.Commit.BlockID, trustedHeader.Height, trustedHeader.Commit) + require.NoError(t, err, "Light client verification of first block commit should pass") + } else { + // For subsequent blocks, verify against the previously trusted header + trustingPeriod := 3 * time.Hour + trustLevel := math.Fraction{Numerator: 2, Denominator: 3} + maxClockDrift := 10 * time.Second + now := block.Time() + + err = light.Verify(trustedHeader, valSet, cmtSignedHeader, valSet, trustingPeriod, now, maxClockDrift, trustLevel) + require.NoError(t, err, "Light client verification should pass for block %d", i+1) + trustedHeader = cmtSignedHeader + } + t.Logf("✅ Light client verification passed for block %d", i+1) + + // The current commit becomes the lastCommit for the next block + lastCommit = currentCommit + } + + t.Log("🎉 MULTI-VALIDATOR BLOCKCHAIN DEMONSTRATION COMPLETE!") + t.Log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") + t.Logf("✅ Chain ID: %s", chainID) + t.Logf("✅ Total Blocks: %d", len(blocks)) + t.Logf("✅ Total Validators: %d", len(validators)) + t.Logf("✅ Multi-validator capable: true") + for i, block := range blocks { + t.Logf("✅ Block %d: Height %d, Hash %x", i+1, block.Height(), block.Hash()) + } + t.Logf("✅ Total Transactions: %d", len(blockData[0].Txs)+len(blockData[1].Txs)+len(blockData[2].Txs)) + t.Log("✅ All blocks created and validated") + t.Log("✅ ABCI-compatible signature payload provider used") + t.Log("✅ Multi-validator commits simulated successfully") + t.Log("✅ Light client verification passed for all blocks") + t.Log("🎯 Test demonstrates multi-validator infrastructure for Rollkit and light client compatibility") +} + +// createMultiValidatorCommit creates a CometBFT commit with signatures from all specified validators. +func createMultiValidatorCommit(t *testing.T, header *rollkittypes.Header, blockID cmttypes.BlockID, valSet *cmttypes.ValidatorSet, validators []*ValidatorInfo, chainID string) *cmttypes.Commit { + t.Helper() + commitSigs := []cmttypes.CommitSig{} + + // Sign with all validators + for _, validator := range validators { + index, val := valSet.GetByAddress(validator.CmtPubKey.Address()) + require.NotNil(t, val, "validator %X not found in valset", validator.CmtPubKey.Address()) + + vote := cmtproto.Vote{ + Type: cmtproto.PrecommitType, + Height: int64(header.Height()), + Round: 0, + BlockID: cmtproto.BlockID{Hash: blockID.Hash, PartSetHeader: blockID.PartSetHeader.ToProto()}, + Timestamp: header.Time(), + ValidatorAddress: validator.CmtPubKey.Address(), + ValidatorIndex: int32(index), + } + + signBytes := cmttypes.VoteSignBytes(chainID, &vote) + signature, err := validator.CmtPrivKey.Sign(signBytes) + require.NoError(t, err) + + commitSigs = append(commitSigs, cmttypes.CommitSig{ + BlockIDFlag: cmttypes.BlockIDFlagCommit, + ValidatorAddress: validator.CmtPubKey.Address(), + Timestamp: header.Time(), + Signature: signature, + }) + } + + return &cmttypes.Commit{ + Height: int64(header.Height()), + Round: 0, + BlockID: blockID, + Signatures: commitSigs, + } +} + +// TestRollkitAsyncValidatorSignatures tests a scenario where block production by the sequencer +// is decoupled from the finalization by the validator set. It first creates a series of +// blocks signed only by the sequencer, and only then simulates the asynchronous arrival +// of validator signatures to create the final commits, which are then verified by a light client. +func TestRollkitAsyncValidatorSignatures(t *testing.T) { + chainID := "test-async-sigs-chain" + + // ========================= + // SETUP: CREATE 3 VALIDATORS + // ========================= + t.Log("🔧 Setting up validator set with 3 validators...") + + validators := make([]*ValidatorInfo, 3) + cmtValidators := make([]*cmttypes.Validator, 3) + + for i := 0; i < 3; i++ { + cmtPrivKey := ed25519.GenPrivKey() + cmtPubKey := cmtPrivKey.PubKey() + privKey, err := crypto.UnmarshalEd25519PrivateKey(cmtPrivKey.Bytes()) + require.NoError(t, err) + signer, err := noop.NewNoopSigner(privKey) + require.NoError(t, err) + address, err := signer.GetAddress() + require.NoError(t, err) + cmtValidator := cmttypes.NewValidator(cmtPubKey, 1) + + validators[i] = &ValidatorInfo{ + PrivKey: privKey, + PubKey: privKey.GetPublic(), + CmtPrivKey: cmtPrivKey, + CmtPubKey: cmtPubKey.(ed25519.PubKey), + Signer: signer.(*noop.NoopSigner), + Address: address, + CmtAddress: cmtPubKey.Address(), + CmtValidator: cmtValidator, + } + cmtValidators[i] = cmtValidator + } + + sequencer := validators[0] + valSet := &cmttypes.ValidatorSet{ + Validators: cmtValidators, + Proposer: cmtValidators[0], + } + + // ========================= + // SETUP RPC ENVIRONMENT + // ========================= + mockRollkitStore := &MockRollkitStore{} + + // ========================= + // PHASE 1: BLOCK PRODUCTION (SEQUENCER ONLY) + // ========================= + t.Log("PHASE 1: Sequencer is producing blocks ahead of finalization...") + + blocks := make([]*rollkittypes.SignedHeader, 3) + blockData := make([]*rollkittypes.Data, 3) + + for i := 0; i < 3; i++ { + height := uint64(i + 1) + var block *rollkittypes.SignedHeader + var data *rollkittypes.Data + var err error + + if i == 0 { + t.Logf("📦 Proposing Genesis Block (Height %d)...", height) + block, err = rollkittypes.GetFirstSignedHeader(sequencer.Signer, chainID) + require.NoError(t, err, "Failed to create genesis block") + } else { + t.Logf("📦 Proposing Block %d (Height %d)...", i+1, height) + block, err = rollkittypes.GetRandomNextSignedHeader(blocks[i-1], sequencer.Signer, chainID) + require.NoError(t, err, "Failed to create block %d", i+1) + } + + data = &rollkittypes.Data{ + Txs: make(rollkittypes.Txs, i+1), + } + for j := range data.Txs { + data.Txs[j] = rollkittypes.GetRandomTx() + } + block.DataHash = data.DACommitment() + + payloadProvider := cometcompat.PayloadProvider() + payload, err := payloadProvider(&block.Header) + require.NoError(t, err) + signature, err := sequencer.Signer.Sign(payload) + require.NoError(t, err) + block.Signature = signature + + err = block.SetCustomVerifier(rollkittypes.SignatureVerifier(payloadProvider)) + require.NoError(t, err) + err = block.ValidateBasic() + require.NoError(t, err) + + blocks[i] = block + blockData[i] = data + + mockRollkitStore.On("GetBlockData", mock.Anything, height).Return(block, data, nil).Once() + mockRollkitStore.On("Height", mock.Anything).Return(height, nil).Maybe() + + t.Logf("✅ Block %d proposed: Height=%d, Hash=%x", i+1, block.Height(), block.Hash()) + } + + t.Log("🎉 All blocks produced by sequencer. Now simulating delayed finalization.") + + // ========================= + // PHASE 2: ASYNCHRONOUS FINALIZATION & VERIFICATION + // ========================= + t.Log("PHASE 2: Simulating delayed validator signatures and running light client verification...") + + var lastCommit *cmttypes.Commit + var trustedHeader *cmttypes.SignedHeader + + for i := 0; i < 3; i++ { + t.Logf("⏳ Finalizing block %d (Height %d)...", i+1, blocks[i].Height()) + block := blocks[i] + data := blockData[i] + + if i == 0 { + lastCommit = &cmttypes.Commit{Height: 0} + } + + cometCompatBlock := *block + cometCompatBlock.ProposerAddress = sequencer.CmtAddress + + cometBlock, err := cometcompat.ToABCIBlock(&cometCompatBlock, data, lastCommit, valSet) + require.NoError(t, err, "Failed to convert to CometBFT block for block %d", i+1) + blockID := cmttypes.BlockID{Hash: cometBlock.Hash()} + + t.Logf(" ✍️ Aggregating signatures for block %d...", i+1) + currentCommit := createMultiValidatorCommit(t, &block.Header, blockID, valSet, validators, chainID) + + cmtSignedHeader := &cmttypes.SignedHeader{ + Header: &cometBlock.Header, + Commit: currentCommit, + } + + if i == 0 { + trustedHeader = cmtSignedHeader + err = valSet.VerifyCommitLight(chainID, trustedHeader.Commit.BlockID, trustedHeader.Height, trustedHeader.Commit) + require.NoError(t, err, "Light client verification of first block commit should pass") + } else { + trustingPeriod := 3 * time.Hour + trustLevel := math.Fraction{Numerator: 2, Denominator: 3} + maxClockDrift := 10 * time.Second + now := block.Time() + + err = light.Verify(trustedHeader, valSet, cmtSignedHeader, valSet, trustingPeriod, now, maxClockDrift, trustLevel) + require.NoError(t, err, "Light client verification should pass for block %d", i+1) + trustedHeader = cmtSignedHeader + } + t.Logf(" ✅ Light client verification passed for block %d", i+1) + + lastCommit = currentCommit + } + + t.Log("🎉 ASYNCHRONOUS SIGNATURE DEMONSTRATION COMPLETE!") + t.Log("✅ All blocks successfully verified by the light client after delayed finalization.") +} diff --git a/pkg/adapter/adapter.go b/pkg/adapter/adapter.go index 0b8baa00..43f77297 100644 --- a/pkg/adapter/adapter.go +++ b/pkg/adapter/adapter.go @@ -327,7 +327,7 @@ func (a *Adapter) ExecuteTxs( return nil, 0, fmt.Errorf("get last commit: %w", err) } - emptyBlock, err := cometcompat.ToABCIBlock(header, &types.Data{}, lastCommit) + emptyBlock, err := cometcompat.ToABCIBlock(header, &types.Data{}, lastCommit) // todo (Alex): is an empty valset correct? if err != nil { return nil, 0, fmt.Errorf("compute header hash: %w", err) } @@ -732,6 +732,6 @@ outerLoop: return nil } -func (a *Adapter) GetExecutionMode() execution.ExecutionMode { - return execution.ExecutionModeDelayed -} +//func (a *Adapter) GetExecutionMode() execution.ExecutionMode { +// return execution.ExecutionModeDelayed +//} diff --git a/pkg/adapter/store.go b/pkg/adapter/store.go index 02736293..90a24033 100644 --- a/pkg/adapter/store.go +++ b/pkg/adapter/store.go @@ -11,6 +11,7 @@ import ( proto "github.com/cosmos/gogoproto/proto" ds "github.com/ipfs/go-datastore" kt "github.com/ipfs/go-datastore/keytransform" + rollkittypes "github.com/rollkit/rollkit/types" ) const ( @@ -105,3 +106,22 @@ func (s *Store) GetBlockResponse(ctx context.Context, height uint64) (*abci.Resp return resp, nil } + +func ValidatorHasher(store *Store) func() (rollkittypes.Hash, error) { + return func() (rollkittypes.Hash, error) { + s, err := store.LoadState(context.Background()) + + if err != nil { + return make(rollkittypes.Hash, 32), err + } + + hash := s.Validators.Hash() + + fmt.Printf("validator hash: %x\n", hash) + + if hash == nil { + return make(rollkittypes.Hash, 32), nil + } + return hash, nil + } +} diff --git a/pkg/cometcompat/convert.go b/pkg/cometcompat/convert.go index 47e22725..7f60777b 100644 --- a/pkg/cometcompat/convert.go +++ b/pkg/cometcompat/convert.go @@ -2,8 +2,6 @@ package cometcompat import ( "errors" - "fmt" - cmbytes "github.com/cometbft/cometbft/libs/bytes" cmprotoversion "github.com/cometbft/cometbft/proto/tendermint/version" cmttypes "github.com/cometbft/cometbft/types" @@ -13,7 +11,11 @@ import ( ) // ToABCIBlock converts Rolkit block into block format defined by ABCI. -func ToABCIBlock(header *rlktypes.SignedHeader, data *rlktypes.Data, lastCommit *cmttypes.Commit) (*cmttypes.Block, error) { +func ToABCIBlock( + header *rlktypes.SignedHeader, + data *rlktypes.Data, + lastCommit *cmttypes.Commit, +) (*cmttypes.Block, error) { abciHeader, err := ToABCIHeader(&header.Header) if err != nil { return nil, err @@ -27,15 +29,9 @@ func ToABCIBlock(header *rlktypes.SignedHeader, data *rlktypes.Data, lastCommit // set commit hash abciHeader.LastCommitHash = lastCommit.Hash() - // set validator hash - if header.Signer.Address != nil { - validatorHash, err := validatorHasher(header.ProposerAddress, header.Signer.PubKey) - if err != nil { - return nil, fmt.Errorf("failed to compute validator hash: %w", err) - } - abciHeader.ValidatorsHash = cmbytes.HexBytes(validatorHash) - abciHeader.NextValidatorsHash = cmbytes.HexBytes(validatorHash) - } + validatorHash := header.ValidatorHash + abciHeader.ValidatorsHash = cmbytes.HexBytes(validatorHash) + abciHeader.NextValidatorsHash = cmbytes.HexBytes(validatorHash) abciBlock := cmttypes.Block{ Header: abciHeader, @@ -54,8 +50,13 @@ func ToABCIBlock(header *rlktypes.SignedHeader, data *rlktypes.Data, lastCommit } // ToABCIBlockMeta converts Rollkit block into BlockMeta format defined by ABCI -func ToABCIBlockMeta(header *rlktypes.SignedHeader, data *rlktypes.Data, lastCommit *cmttypes.Commit) (*cmttypes.BlockMeta, error) { +func ToABCIBlockMeta( + header *rlktypes.SignedHeader, + data *rlktypes.Data, + lastCommit *cmttypes.Commit, +) (*cmttypes.BlockMeta, error) { cmblock, err := ToABCIBlock(header, data, lastCommit) + if err != nil { return nil, err } diff --git a/pkg/cometcompat/validator_hasher.go b/pkg/cometcompat/validator_hasher.go deleted file mode 100644 index 12dbb4c3..00000000 --- a/pkg/cometcompat/validator_hasher.go +++ /dev/null @@ -1,54 +0,0 @@ -package cometcompat - -import ( - "bytes" - stdsha256 "crypto/sha256" - "encoding/hex" - "fmt" - - tmcryptoed25519 "github.com/cometbft/cometbft/crypto/ed25519" - tmtypes "github.com/cometbft/cometbft/types" - "github.com/libp2p/go-libp2p/core/crypto" - - rollkittypes "github.com/rollkit/rollkit/types" -) - -// validatorHasher returns a function that calculates the ValidatorHash -// compatible with CometBFT. This function is intended to be injected into Rollkit's Manager. -func validatorHasher(proposerAddress []byte, pubKey crypto.PubKey) (rollkittypes.Hash, error) { - var calculatedHash rollkittypes.Hash - - var cometBftPubKey tmcryptoed25519.PubKey - if pubKey.Type() == crypto.Ed25519 { - rawKey, err := pubKey.Raw() - if err != nil { - return calculatedHash, fmt.Errorf("failed to get raw bytes from libp2p public key: %w", err) - } - if len(rawKey) != tmcryptoed25519.PubKeySize { - return calculatedHash, fmt.Errorf("libp2p public key size (%d) does not match CometBFT Ed25519 PubKeySize (%d)", len(rawKey), tmcryptoed25519.PubKeySize) - } - cometBftPubKey = rawKey - } else { - return calculatedHash, fmt.Errorf("unsupported public key type '%s', expected Ed25519 for CometBFT compatibility", pubKey.Type()) - } - - votingPower := int64(1) - sequencerValidator := tmtypes.NewValidator(cometBftPubKey, votingPower) - - derivedAddress := sequencerValidator.Address.Bytes() - if !bytes.Equal(derivedAddress, proposerAddress) { - return calculatedHash, fmt.Errorf("CRITICAL MISMATCH - derived validator address (%s) does not match expected proposer address (%s). PubKey used for derivation: %s", - hex.EncodeToString(derivedAddress), - hex.EncodeToString(proposerAddress), - hex.EncodeToString(cometBftPubKey.Bytes())) - } - - sequencerValidatorSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{sequencerValidator}) - - hashSumBytes := sequencerValidatorSet.Hash() - - calculatedHash = make(rollkittypes.Hash, stdsha256.Size) - copy(calculatedHash, hashSumBytes) - - return calculatedHash, nil -} diff --git a/pkg/rpc/core/blocks.go b/pkg/rpc/core/blocks.go index c422eac9..a05738a8 100644 --- a/pkg/rpc/core/blocks.go +++ b/pkg/rpc/core/blocks.go @@ -135,8 +135,7 @@ func Block(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlock, error) return nil, fmt.Errorf("failed to get last commit for block %d: %w", heightValue, err) } - // First apply ToABCIBlock to get the final header with all transformations - abciBlock, err := cometcompat.ToABCIBlock(header, data, lastCommit) + block, err := cometcompat.ToABCIBlock(header, data, lastCommit) if err != nil { return nil, err } @@ -149,10 +148,10 @@ func Block(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlock, error) Height: int64(header.Height()), //nolint:gosec Round: 0, BlockID: cmtproto.BlockID{ - Hash: abciBlock.Header.Hash(), + Hash: block.Header.Hash(), PartSetHeader: cmtproto.PartSetHeader{}, }, - Timestamp: abciBlock.Time, + Timestamp: block.Time, ValidatorAddress: header.ProposerAddress, ValidatorIndex: 0, } @@ -165,14 +164,14 @@ func Block(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlock, error) } // Update the signature in the block - if len(abciBlock.LastCommit.Signatures) > 0 { - abciBlock.LastCommit.Signatures[0].Signature = newSignature + if len(block.LastCommit.Signatures) > 0 { + block.LastCommit.Signatures[0].Signature = newSignature } } return &ctypes.ResultBlock{ - BlockID: cmttypes.BlockID{Hash: abciBlock.Hash()}, - Block: abciBlock, + BlockID: cmttypes.BlockID{Hash: block.Hash()}, + Block: block, }, nil } @@ -189,7 +188,7 @@ func BlockByHash(ctx *rpctypes.Context, hash []byte) (*ctypes.ResultBlock, error return nil, fmt.Errorf("failed to get last commit for block %d: %w", header.Height(), err) } - abciBlock, err := cometcompat.ToABCIBlock(header, data, lastCommit) + block, err := cometcompat.ToABCIBlock(header, data, lastCommit) if err != nil { return nil, err } @@ -202,7 +201,7 @@ func BlockByHash(ctx *rpctypes.Context, hash []byte) (*ctypes.ResultBlock, error Hash: nil, }, }, - Block: abciBlock, + Block: block, }, nil } @@ -236,8 +235,7 @@ func Commit(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultCommit, erro }}, } - // First apply ToABCIBlock to get the final header with all transformations - abciBlock, err := cometcompat.ToABCIBlock(header, rollkitData, abciCommit) + block, err := cometcompat.ToABCIBlock(header, rollkitData, abciCommit) if err != nil { return nil, err } @@ -250,10 +248,10 @@ func Commit(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultCommit, erro Height: int64(header.Height()), //nolint:gosec Round: 0, BlockID: cmtproto.BlockID{ - Hash: abciBlock.Header.Hash(), + Hash: block.Header.Hash(), PartSetHeader: cmtproto.PartSetHeader{}, }, - Timestamp: abciBlock.Time, + Timestamp: block.Time, ValidatorAddress: header.ProposerAddress, ValidatorIndex: 0, } @@ -266,16 +264,16 @@ func Commit(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultCommit, erro } // Update the commit with the new signature - abciBlock.LastCommit.Signatures[0].Signature = newSignature + block.LastCommit.Signatures[0].Signature = newSignature } // Update the commit's BlockID to match the final ABCI block hash - abciBlock.LastCommit.BlockID.Hash = abciBlock.Header.Hash() + block.LastCommit.BlockID.Hash = block.Header.Hash() return &ctypes.ResultCommit{ SignedHeader: cmttypes.SignedHeader{ - Header: &abciBlock.Header, - Commit: abciBlock.LastCommit, + Header: &block.Header, + Commit: block.LastCommit, }, CanonicalCommit: true, }, nil diff --git a/pkg/rpc/core/blocks_test.go b/pkg/rpc/core/blocks_test.go index 125267d8..c2e7bece 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) + abciBlock, err := cometcompat.ToABCIBlock(tempSignedHeader, data, tempCommit, nil) // todo (Alex): set correct valset require.NoError(t, err) vote := cmtproto.Vote{ diff --git a/pkg/rpc/core/consensus.go b/pkg/rpc/core/consensus.go index a6750d6d..8bbe5f82 100644 --- a/pkg/rpc/core/consensus.go +++ b/pkg/rpc/core/consensus.go @@ -28,21 +28,20 @@ func Validators(ctx *rpctypes.Context, heightPtr *int64, pagePtr, perPagePtr *in } // Since it's a centralized sequencer // changed behavior to get this from genesis - genesisValidator := genesisValidators[0] - validator := cmttypes.Validator{ - Address: genesisValidator.Address, - PubKey: genesisValidator.PubKey, - VotingPower: int64(1), - ProposerPriority: int64(1), + valSet := make([]*cmttypes.Validator, len(genesisValidators)) + for i, v := range genesisValidators { + valSet[i] = &cmttypes.Validator{ + Address: v.Address, + PubKey: v.PubKey, + VotingPower: v.Power, + ProposerPriority: int64(1), + } } - return &coretypes.ResultValidators{ BlockHeight: int64(height), //nolint:gosec - Validators: []*cmttypes.Validator{ - &validator, - }, - Count: 1, - Total: 1, + Validators: valSet, + Count: len(valSet), + Total: len(valSet), }, nil } diff --git a/pkg/rpc/core/utils.go b/pkg/rpc/core/utils.go index 88821cca..a04c9933 100644 --- a/pkg/rpc/core/utils.go +++ b/pkg/rpc/core/utils.go @@ -84,15 +84,9 @@ func getBlockMeta(ctx context.Context, n uint64) *cmttypes.BlockMeta { } // Create empty commit for ToABCIBlockMeta call - emptyCommit := &cmttypes.Commit{ - Height: int64(header.Height()), - Round: 0, - BlockID: cmttypes.BlockID{}, - Signatures: []cmttypes.CommitSig{}, - } - + emptyCommit := &cmttypes.Commit{} // Assuming ToABCIBlockMeta is now in pkg/rpc/provider/provider_utils.go - bmeta, err := cometcompat.ToABCIBlockMeta(header, data, emptyCommit) // Removed rpc. prefix + bmeta, err := cometcompat.ToABCIBlockMeta(header, data, emptyCommit) if err != nil { env.Logger.Error("Failed to convert block to ABCI block meta", "height", n, "err", err) return nil diff --git a/server/start.go b/server/start.go index 719a7a82..457b54a0 100644 --- a/server/start.go +++ b/server/start.go @@ -473,6 +473,7 @@ func setupNodeAndExecutor( metrics, NewLogAdapter(logger), cometcompat.PayloadProvider(), + adapter.ValidatorHasher(executor.Store), ) if err != nil { return nil, nil, cleanupFn, err