Pure package detail
tendermint
gno.land/p/aib/ibc/lightclient/tendermint
Indexed deployment identity with independently loaded latest RPC source. Functions and Render are realm-only RPC capabilities.
Indexed deployment
Identity
- Package path
- gno.land/p/aib/ibc/lightclient/tendermint
- Block
- 21886
- Deployed (UTC)
- Transaction
- MptDW04iDOhGquUI7KCLniGTIIf1hanuFS7NXdHOB6g=
Latest RPC state
Source
package tendermint
import (
"bytes"
"encoding/binary"
"encoding/hex"
"errors"
"time"
"gno.land/p/aib/encoding/proto"
"gno.land/p/aib/ibc/lightclient"
"gno.land/p/aib/ibc/types"
"gno.land/p/aib/merkle"
"gno.land/p/nt/ufmt/v0"
)
// MsgHeader defines the Tendermint client consensus Header.
// It encapsulates all the information necessary to update from a trusted
// Tendermint ConsensusState. The inclusion of TrustedHeight and
// TrustedValidators allows this update to process correctly, so long as the
// ConsensusState for the TrustedHeight exists, this removes race conditions
// among relayers The SignedHeader and ValidatorSet are the new untrusted update
// fields for the client. The TrustedHeight is the height of a stored
// ConsensusState on the client that will be used to verify the new untrusted
// header. The Trusted ConsensusState must be within the unbonding period of
// current time in order to correctly verify, and the TrustedValidators must
// hash to TrustedConsensusState.NextValidatorsHash since that is the last
// trusted validator set at the TrustedHeight.
type MsgHeader struct {
Header *Header
Commit *Commit
ValidatorSet *ValidatorSet
TrustedHeight types.Height
TrustedValidators *ValidatorSet
}
// Implements lightclient.ClientMessage
func (*MsgHeader) ClientType() string {
return lightclient.Tendermint
}
// Implements lightclient.ClientMessage
// NOTE: TrustedHeight and TrustedValidators may be empty when creating client
// with MsgCreateClient
func (h *MsgHeader) ValidateBasic() error {
if h.Header == nil {
return errors.New("missing header")
}
if h.Commit == nil {
return errors.New("missing commit")
}
if err := h.Header.ValidateBasic(); err != nil {
return ufmt.Errorf("invalid header: %v", err)
}
if err := h.Commit.ValidateBasic(); err != nil {
return ufmt.Errorf("invalid commit: %v", err)
}
// Make sure the header is consistent with the commit.
if h.Header.Height != h.Commit.Height {
return ufmt.Errorf("header and commit height mismatch: %d vs %d", h.Header.Height, h.Commit.Height)
}
if hhash, chash := h.Header.Hash(), h.Commit.BlockID.Hash; !bytes.Equal(hhash, chash) {
cs, hs := hex.EncodeToString(chash), hex.EncodeToString(hhash)
return ufmt.Errorf("expected BlockID#Hash and Header#Hash to be the same, got %q != %q", cs, hs)
}
// TrustedHeight is less than Header for updates and misbehaviour
if h.TrustedHeight.GTE(h.GetHeight()) {
return ufmt.Errorf("trustedHeight %s must be less than header height %s",
h.TrustedHeight, h.GetHeight())
}
if h.ValidatorSet == nil {
return ufmt.Errorf("validator set is nil")
}
if h.TrustedValidators == nil {
return ufmt.Errorf("trusted validator set is nil")
}
if !bytes.Equal(h.Header.ValidatorsHash, h.ValidatorSet.Hash()) {
return ufmt.Errorf("validator set does not match hash")
}
if len(h.ValidatorSet.Validators) != len(h.Commit.Signatures) {
return ufmt.Errorf("Invalid commit -- wrong set size: %v vs %v",
len(h.ValidatorSet.Validators), len(h.Commit.Signatures))
}
return nil
}
// ConsensusState returns the updated consensus state associated with the
// header
func (h MsgHeader) ConsensusState() ConsensusState {
return ConsensusState{
Timestamp: h.Header.Time,
Root: NewMerkleRoot(h.Header.AppHash),
NextValidatorsHash: h.Header.NextValidatorsHash,
}
}
// GetHeight returns the current height. It returns 0 if the tendermint
// header is nil.
// NOTE: the header.Header is checked to be non nil in ValidateBasic.
func (h MsgHeader) GetHeight() types.Height {
revision := types.ParseChainID(h.Header.ChainID)
return types.NewHeight(revision, h.Header.Height)
}
// Header defines the structure of a block header.
type Header struct {
// basic block info
Version Consensus
ChainID string
Height uint64
Time time.Time
// prev block info
LastBlockID BlockID
// hashes of block data
// commit info from validators from the last block
LastCommitHash []byte
// transactions
DataHash []byte
// hashes from the app output form the preb block
// validators for the current block
ValidatorsHash []byte
// validators for the next block
NextValidatorsHash []byte
// consensus params for the current block
ConsensusHash []byte
// state afterx txs from the previous block
AppHash []byte
// root hash of all results from the txs from the previous block
LastResultsHash []byte
// consensus info
// evidence included in the block
EvidenceHash []byte
//original proposer of the block
ProposerAddress []byte
}
const (
// BlockProtocol versions all block data structures and processing.
// This includes validity of blocks and state updates.
BlockProtocol uint64 = 11
AddressSize = 20
// MaxSignatureSize is a maximum allowed signature size for the Proposal
// and Vote.
// XXX: secp256k1 does not have Size nor MaxSize defined.
MaxSignatureSize = 64
)
// ValidateBasic performs stateless validation on a Header returning an error if any
// validation fails.
//
// NOTE: Timestamp validation is subtle and handled elsewhere.
func (h Header) ValidateBasic() error {
if h.Version.Block != BlockProtocol {
return ufmt.Errorf("block protocol is incorrect: got: %d, want: %d ", h.Version.Block, BlockProtocol)
}
if len(h.ChainID) > MaxChainIDLen {
return ufmt.Errorf("chainID is too long; got: %d, max: %d", len(h.ChainID), MaxChainIDLen)
}
if h.Height == 0 {
return errors.New("zero Height")
}
if err := h.LastBlockID.ValidateBasic(); err != nil {
return ufmt.Errorf("wrong LastBlockID: %v", err)
}
if err := validateHash(h.LastCommitHash); err != nil {
return ufmt.Errorf("wrong LastCommitHash: %v", err)
}
if err := validateHash(h.DataHash); err != nil {
return ufmt.Errorf("wrong DataHash: %v", err)
}
if err := validateHash(h.EvidenceHash); err != nil {
return ufmt.Errorf("wrong EvidenceHash: %v", err)
}
if len(h.ProposerAddress) != AddressSize {
return ufmt.Errorf(
"invalid ProposerAddress length; got: %d, expected: %d",
len(h.ProposerAddress), AddressSize,
)
}
// Basic validation of hashes related to application data.
// Will validate fully against state in state#ValidateBlock.
if err := validateHash(h.ValidatorsHash); err != nil {
return ufmt.Errorf("wrong ValidatorsHash: %v", err)
}
if err := validateHash(h.NextValidatorsHash); err != nil {
return ufmt.Errorf("wrong NextValidatorsHash: %v", err)
}
if err := validateHash(h.ConsensusHash); err != nil {
return ufmt.Errorf("wrong ConsensusHash: %v", err)
}
// NOTE: AppHash is arbitrary length
if err := validateHash(h.LastResultsHash); err != nil {
return ufmt.Errorf("wrong LastResultsHash: %v", err)
}
return nil
}
func (h *Header) Hash() []byte {
if h == nil || len(h.ValidatorsHash) == 0 {
return nil
}
bz := merkle.HashFromByteSlices([][]byte{
h.Version.ProtoMarshal(),
proto.AppendLengthDelimited(nil, 1, []byte(h.ChainID)),
proto.AppendVarint(nil, 1, h.Height),
proto.TimeMarshal(h.Time),
h.LastBlockID.ProtoMarshal(),
proto.AppendLengthDelimited(nil, 1, h.LastCommitHash),
proto.AppendLengthDelimited(nil, 1, h.DataHash),
proto.AppendLengthDelimited(nil, 1, h.ValidatorsHash),
proto.AppendLengthDelimited(nil, 1, h.NextValidatorsHash),
proto.AppendLengthDelimited(nil, 1, h.ConsensusHash),
proto.AppendLengthDelimited(nil, 1, h.AppHash),
proto.AppendLengthDelimited(nil, 1, h.LastResultsHash),
proto.AppendLengthDelimited(nil, 1, h.EvidenceHash),
proto.AppendLengthDelimited(nil, 1, h.ProposerAddress),
})
// For debugging purposes:
// hex := hex.EncodeToString
// println("VERSION", hex(h.Version.ProtoMarshal()))
// println("CHAIN", hex(proto.AppendLengthDelimited(nil, 1, []byte(h.ChainID))))
// println("HEIGHT", hex(proto.AppendVarint(nil, 1, h.Height)))
// println("TIME", hex(proto.TimeMarshal(h.Time)))
// println("LAST", hex(h.LastBlockID.ProtoMarshal()))
// println("COMMITHASH", hex(proto.AppendLengthDelimited(nil, 1, h.LastCommitHash)))
// println("DATAHASH", hex(proto.AppendLengthDelimited(nil, 1, h.DataHash)))
// println("VALHASH", hex(proto.AppendLengthDelimited(nil, 1, h.ValidatorsHash)))
// println("NEXTVALHASH", hex(proto.AppendLengthDelimited(nil, 1, h.NextValidatorsHash)))
// println("CONSHASH", hex(proto.AppendLengthDelimited(nil, 1, h.ConsensusHash)))
// println("APPHASH", hex(proto.AppendLengthDelimited(nil, 1, h.AppHash)))
// println("RES", hex(proto.AppendLengthDelimited(nil, 1, h.LastResultsHash)))
// println("EV", hex(proto.AppendLengthDelimited(nil, 1, h.EvidenceHash)))
// println("PROP", hex(proto.AppendLengthDelimited(nil, 1, h.ProposerAddress)))
// println("MERKLE", hex(bz))
return bz
}
func (h Header) Expired(trustingPeriod time.Duration) error {
var (
expirationTime = h.Time.Add(trustingPeriod)
now = time.Now()
)
if !expirationTime.After(now) {
return ufmt.Errorf("header %d has expired at %v (now: %v)", h.Height, expirationTime, now)
}
return nil
}
// Commit contains the evidence that a block was committed by a set of validators.
type Commit struct {
Height uint64
Round int32
BlockID BlockID
Signatures []CommitSig
}
// BytesToSign returns the bytes-to-sign for a validator's vote.
// These bytes are produced from a protobuf marshaled type [CanonicalVote][1].
// Since proto is not available in Gno, the function tries to generate these
// bytes manually, using the same logic as protobuf.
//
// [1]: https://buf.build/tendermint/tendermint/docs/main:tendermint.types#tendermint.types.CanonicalVote
func (c *Commit) BytesToSign(chainID string, valIdx int) []byte {
var buf []byte
// Field 1: type (SignedMsgType - varint)
buf = proto.AppendVarint(buf, 1, 2) // type precommit
// Field 2: height (sfixed64 - fixed64)
buf = proto.AppendFixed64(buf, 2, uint64(c.Height))
// Field 3: round (sfixed64 - fixed64)
buf = proto.AppendFixed64(buf, 3, uint64(c.Round))
// Field 4: block_id (CanonicalBlockID - length-delimited)
if c.Signatures[valIdx].BlockIDFlag == BlockIDFlagCommit {
buf = proto.AppendLengthDelimited(buf, 4, c.BlockID.ProtoMarshal())
}
// Field 5: timestamp (google.protobuf.Timestamp - length-delimited)
buf = proto.AppendTime(buf, 5, c.Signatures[valIdx].Timestamp)
// Field 6: chain_id (string - length-delimited)
buf = proto.AppendLengthDelimited(buf, 6, []byte(chainID))
// Prefix with total length
length := len(buf)
buf = append(binary.AppendUvarint(nil, uint64(length)), buf...)
return buf
}
// ValidateBasic performs basic validation that doesn't involve state data.
// Does not actually check the cryptographic signatures.
func (c *Commit) ValidateBasic() error {
if c.Round < 0 {
return errors.New("negative Round")
}
if c.Height >= 1 {
if c.BlockID.IsZero() {
return errors.New("commit cannot be for nil block")
}
if len(c.Signatures) == 0 {
return errors.New("no signatures in commit")
}
for i, commitSig := range c.Signatures {
if err := commitSig.ValidateBasic(); err != nil {
return ufmt.Errorf("wrong CommitSig #%d: %v", i, err)
}
}
}
return nil
}
// CommitSig is a part of the Vote included in a Commit.
type CommitSig struct {
BlockIDFlag BlockIDFlag
ValidatorAddress []byte
Timestamp time.Time
Signature []byte
}
// ValidateBasic performs basic validation.
func (cs CommitSig) ValidateBasic() error {
switch cs.BlockIDFlag {
case BlockIDFlagAbsent:
case BlockIDFlagCommit:
case BlockIDFlagNil:
default:
return ufmt.Errorf("unknown BlockIDFlag: %v", cs.BlockIDFlag)
}
switch cs.BlockIDFlag {
case BlockIDFlagAbsent:
if len(cs.ValidatorAddress) != 0 {
return errors.New("validator address is present")
}
if !cs.Timestamp.IsZero() {
return errors.New("time is present")
}
if len(cs.Signature) != 0 {
return errors.New("signature is present")
}
default:
if len(cs.ValidatorAddress) != AddressSize {
return ufmt.Errorf("expected ValidatorAddress size to be %d bytes, got %d bytes",
AddressSize,
len(cs.ValidatorAddress),
)
}
// NOTE: Timestamp validation is subtle and handled elsewhere.
if len(cs.Signature) == 0 {
return errors.New("signature is missing")
}
if len(cs.Signature) > MaxSignatureSize {
return ufmt.Errorf("signature is too big (max: %d)", MaxSignatureSize)
}
}
return nil
}
// BlockIdFlag indicates which BlockID the signature is for
type BlockIDFlag byte
const (
BlockIDFlagUnknown BlockIDFlag = 0
BlockIDFlagAbsent BlockIDFlag = 1
BlockIDFlagCommit BlockIDFlag = 2
BlockIDFlagNil BlockIDFlag = 3
)
// BlockID
type BlockID struct {
Hash []byte
PartSetHeader PartSetHeader
}
// Equals returns true if the BlockID matches the given BlockID
func (blockID BlockID) Equals(other BlockID) bool {
return bytes.Equal(blockID.Hash, other.Hash) &&
blockID.PartSetHeader.Equals(other.PartSetHeader)
}
// IsZero returns true if this is the BlockID of a nil block.
func (blockID BlockID) IsZero() bool {
return len(blockID.Hash) == 0 &&
blockID.PartSetHeader.IsZero()
}
// ValidateBasic performs basic validation.
func (blockID BlockID) ValidateBasic() error {
// Hash can be empty in case of POLBlockID in Proposal.
if err := validateHash(blockID.Hash); err != nil {
return ufmt.Errorf("wrong blockID Hash: %v", err)
}
if err := blockID.PartSetHeader.ValidateBasic(); err != nil {
return ufmt.Errorf("wrong PartSetHeader: %v", err)
}
return nil
}
func (b BlockID) ProtoMarshal() (bz []byte) {
if len(b.Hash) > 0 {
bz = proto.AppendLengthDelimited(bz, 1, b.Hash)
}
bz = proto.AppendLengthDelimited(bz, 2, b.PartSetHeader.ProtoMarshal())
return
}
// PartsetHeader
type PartSetHeader struct {
Total uint32
Hash []byte
}
func (psh PartSetHeader) IsZero() bool {
return psh.Total == 0 && len(psh.Hash) == 0
}
func (psh PartSetHeader) Equals(other PartSetHeader) bool {
return psh.Total == other.Total && bytes.Equal(psh.Hash, other.Hash)
}
// ValidateBasic performs basic validation.
func (psh PartSetHeader) ValidateBasic() error {
// Hash can be empty in case of POLBlockID.PartSetHeader in Proposal.
if err := validateHash(psh.Hash); err != nil {
return ufmt.Errorf("wrong parSetHeader Hash: %v", err)
}
return nil
}
func (psh PartSetHeader) ProtoMarshal() (bz []byte) {
if psh.Total > 0 {
bz = proto.AppendVarint(bz, 1, uint64(psh.Total))
}
if len(psh.Hash) > 0 {
bz = proto.AppendLengthDelimited(bz, 2, psh.Hash)
}
return
}
// Consensus captures the consensus rules for processing a block in the blockchain,
// including all blockchain data structures and the rules of the application's
// state transition machine.
type Consensus struct {
Block uint64
App uint64
}
func (c Consensus) ProtoMarshal() (bz []byte) {
if c.Block != 0 {
bz = proto.AppendVarint(bz, 1, c.Block)
}
if c.App != 0 {
bz = proto.AppendVarint(bz, 2, c.App)
}
return
}
The verified vm/qfuncs operation accepts realm paths only.
Pure packages expose source files but do not have Realm Render.