diff --git a/core/header/service.go b/core/header/service.go index 53b7a261d559..15e6d4257425 100644 --- a/core/header/service.go +++ b/core/header/service.go @@ -33,14 +33,11 @@ func (i *Info) Bytes() ([]byte, error) { binary.LittleEndian.PutUint64(heightBytes, uint64(i.Height)) buf = append(buf, heightBytes...) - // TODO; permit empty hash OK for genesis block? - if len(i.Hash) == 0 { - i.Hash = make([]byte, hashSize) - } // Encode Hash if len(i.Hash) != hashSize { return nil, errors.New("invalid hash size") } + buf = append(buf, i.Hash...) // Encode Time @@ -48,11 +45,8 @@ func (i *Info) Bytes() ([]byte, error) { binary.LittleEndian.PutUint64(timeBytes, uint64(i.Time.Unix())) buf = append(buf, timeBytes...) - if len(i.AppHash) == 0 { - i.AppHash = make([]byte, hashSize) - } // Encode AppHash - if len(i.Hash) != hashSize { + if len(i.AppHash) != hashSize { return nil, errors.New("invalid hash size") } buf = append(buf, i.AppHash...) diff --git a/server/v2/cometbft/abci.go b/server/v2/cometbft/abci.go index d51a3724f932..b1a0afeb038c 100644 --- a/server/v2/cometbft/abci.go +++ b/server/v2/cometbft/abci.go @@ -2,6 +2,7 @@ package cometbft import ( "context" + "crypto/sha256" "errors" "fmt" "sync/atomic" @@ -103,7 +104,7 @@ func (c *Consensus[T]) CheckTx(ctx context.Context, req *abciproto.CheckTxReques return nil, err } - cometResp := &abci.CheckTxResponse{ + cometResp := &abciproto.CheckTxResponse{ Code: resp.Code, GasWanted: uint64ToInt64(resp.GasWanted), GasUsed: uint64ToInt64(resp.GasUsed), @@ -137,7 +138,7 @@ func (c *Consensus[T]) Info(ctx context.Context, _ *abciproto.InfoRequest) (*abc return nil, err } - return &abci.InfoResponse{ + return &abciproto.InfoResponse{ Data: c.cfg.Name, Version: c.cfg.Version, // AppVersion: cp.GetVersion().App, @@ -226,11 +227,19 @@ func (c *Consensus[T]) InitChain(ctx context.Context, req *abciproto.InitChainRe }) } + ci, err := c.store.LastCommitID() + if err != nil { + return nil, err + } + + // populate hash with empty byte slice instead of nil + bz := sha256.Sum256([]byte{}) + br := &coreappmgr.BlockRequest[T]{ Height: uint64(req.InitialHeight - 1), Time: req.Time, - Hash: nil, - AppHash: nil, + Hash: bz[:], + AppHash: ci.Hash, ChainId: req.ChainId, ConsensusMessages: consMessages, IsGenesis: true, @@ -271,7 +280,7 @@ func (c *Consensus[T]) InitChain(ctx context.Context, req *abciproto.InitChainRe return nil, fmt.Errorf("unable to write the changeset: %w", err) } - return &abci.InitChainResponse{ + return &abciproto.InitChainResponse{ ConsensusParams: req.ConsensusParams, Validators: validatorUpdates, AppHash: stateRoot, @@ -317,7 +326,7 @@ func (c *Consensus[T]) PrepareProposal( encodedTxs[i] = tx.Bytes() } - return &abci.PrepareProposalResponse{ + return &abciproto.PrepareProposalResponse{ Txs: encodedTxs, }, nil } @@ -350,13 +359,13 @@ func (c *Consensus[T]) ProcessProposal( err := c.processProposalHandler(ciCtx, c.app, decodedTxs, req) if err != nil { c.logger.Error("failed to process proposal", "height", req.Height, "time", req.Time, "hash", fmt.Sprintf("%X", req.Hash), "err", err) - return &abci.ProcessProposalResponse{ - Status: abci.PROCESS_PROPOSAL_STATUS_REJECT, + return &abciproto.ProcessProposalResponse{ + Status: abciproto.PROCESS_PROPOSAL_STATUS_REJECT, }, nil } - return &abci.ProcessProposalResponse{ - Status: abci.PROCESS_PROPOSAL_STATUS_ACCEPT, + return &abciproto.ProcessProposalResponse{ + Status: abciproto.PROCESS_PROPOSAL_STATUS_ACCEPT, }, nil } @@ -529,7 +538,7 @@ func (c *Consensus[T]) VerifyVoteExtension( resp, err := c.verifyVoteExt(ctx, latestStore, req) if err != nil { c.logger.Error("failed to verify vote extension", "height", req.Height, "err", err) - return &abci.VerifyVoteExtensionResponse{Status: abci.VERIFY_VOTE_EXTENSION_STATUS_REJECT}, nil + return &abciproto.VerifyVoteExtensionResponse{Status: abciproto.VERIFY_VOTE_EXTENSION_STATUS_REJECT}, nil } return resp, err @@ -565,7 +574,7 @@ func (c *Consensus[T]) ExtendVote(ctx context.Context, req *abciproto.ExtendVote resp, err := c.extendVote(ctx, latestStore, req) if err != nil { c.logger.Error("failed to verify vote extension", "height", req.Height, "err", err) - return &abci.ExtendVoteResponse{}, nil + return &abciproto.ExtendVoteResponse{}, nil } return resp, err diff --git a/server/v2/stf/core_header_service.go b/server/v2/stf/core_header_service.go index 1931c28a6a70..4448627828ca 100644 --- a/server/v2/stf/core_header_service.go +++ b/server/v2/stf/core_header_service.go @@ -4,6 +4,7 @@ import ( "context" "cosmossdk.io/core/header" + "cosmossdk.io/core/store" ) var _ header.Service = (*HeaderService)(nil) @@ -13,3 +14,42 @@ type HeaderService struct{} func (h HeaderService) HeaderInfo(ctx context.Context) header.Info { return ctx.(*executionContext).headerInfo } + +const headerInfoPrefix = 0x37 + +// setHeaderInfo sets the header info in the state to be used by queries in the future. +func (s STF[T]) setHeaderInfo(state store.WriterMap, headerInfo header.Info) error { + // TODO storing header info is too low level here, stf should be stateless. + // We should have a keeper that does this. + runtimeStore, err := state.GetWriter(Identity) + if err != nil { + return err + } + bz, err := headerInfo.Bytes() + if err != nil { + return err + } + err = runtimeStore.Set([]byte{headerInfoPrefix}, bz) + if err != nil { + return err + } + return nil +} + +// getHeaderInfo gets the header info from the state. It should only be used for queries +func (s STF[T]) getHeaderInfo(state store.WriterMap) (i header.Info, err error) { + runtimeStore, err := state.GetWriter(Identity) + if err != nil { + return header.Info{}, err + } + v, err := runtimeStore.Get([]byte{headerInfoPrefix}) + if err != nil { + return header.Info{}, err + } + if v == nil { + return header.Info{}, nil + } + + err = i.FromBytes(v) + return i, err +} diff --git a/server/v2/stf/stf.go b/server/v2/stf/stf.go index 763cae4edd8c..633a356c36d1 100644 --- a/server/v2/stf/stf.go +++ b/server/v2/stf/stf.go @@ -421,45 +421,6 @@ func (s STF[T]) validatorUpdates( return ctx.events, valSetUpdates, nil } -const headerInfoPrefix = 0x37 - -// setHeaderInfo sets the header info in the state to be used by queries in the future. -func (s STF[T]) setHeaderInfo(state store.WriterMap, headerInfo header.Info) error { - // TODO storing header info is too low level here, stf should be stateless. - // We should have a keeper that does this. - runtimeStore, err := state.GetWriter(Identity) - if err != nil { - return err - } - bz, err := headerInfo.Bytes() - if err != nil { - return err - } - err = runtimeStore.Set([]byte{headerInfoPrefix}, bz) - if err != nil { - return err - } - return nil -} - -// getHeaderInfo gets the header info from the state. It should only be used for queries -func (s STF[T]) getHeaderInfo(state store.WriterMap) (i header.Info, err error) { - runtimeStore, err := state.GetWriter(Identity) - if err != nil { - return header.Info{}, err - } - v, err := runtimeStore.Get([]byte{headerInfoPrefix}) - if err != nil { - return header.Info{}, err - } - if v == nil { - return header.Info{}, nil - } - - err = i.FromBytes(v) - return i, err -} - // Simulate simulates the execution of a tx on the provided state. func (s STF[T]) Simulate( ctx context.Context, diff --git a/store/v2/root/store.go b/store/v2/root/store.go index 98f52ed63f73..997f9d0f485e 100644 --- a/store/v2/root/store.go +++ b/store/v2/root/store.go @@ -2,6 +2,7 @@ package root import ( "bytes" + "crypto/sha256" "errors" "fmt" "sync" @@ -148,8 +149,10 @@ func (s *Store) LastCommitID() (proof.CommitID, error) { if err != nil { return proof.CommitID{}, err } + // if the latest version is 0, we return a CommitID with version 0 and a hash of an empty byte slice + bz := sha256.Sum256([]byte{}) - return proof.CommitID{Version: latestVersion}, nil + return proof.CommitID{Version: latestVersion, Hash: bz[:]}, nil } // GetLatestVersion returns the latest version based on the latest internal