github.com/MetalBlockchain/metalgo@v1.11.9/vms/proposervm/block/block.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package block
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"time"
    10  
    11  	"github.com/MetalBlockchain/metalgo/ids"
    12  	"github.com/MetalBlockchain/metalgo/staking"
    13  	"github.com/MetalBlockchain/metalgo/utils/hashing"
    14  	"github.com/MetalBlockchain/metalgo/utils/wrappers"
    15  )
    16  
    17  var (
    18  	_ SignedBlock = (*statelessBlock)(nil)
    19  
    20  	errUnexpectedSignature = errors.New("signature provided when none was expected")
    21  	errInvalidCertificate  = errors.New("invalid certificate")
    22  )
    23  
    24  type Block interface {
    25  	ID() ids.ID
    26  	ParentID() ids.ID
    27  	Block() []byte
    28  	Bytes() []byte
    29  
    30  	initialize(bytes []byte) error
    31  	verify(chainID ids.ID) error
    32  }
    33  
    34  type SignedBlock interface {
    35  	Block
    36  
    37  	PChainHeight() uint64
    38  	Timestamp() time.Time
    39  
    40  	// Proposer returns the ID of the node that proposed this block. If no node
    41  	// signed this block, [ids.EmptyNodeID] will be returned.
    42  	Proposer() ids.NodeID
    43  }
    44  
    45  type statelessUnsignedBlock struct {
    46  	ParentID     ids.ID `serialize:"true"`
    47  	Timestamp    int64  `serialize:"true"`
    48  	PChainHeight uint64 `serialize:"true"`
    49  	Certificate  []byte `serialize:"true"`
    50  	Block        []byte `serialize:"true"`
    51  }
    52  
    53  type statelessBlock struct {
    54  	StatelessBlock statelessUnsignedBlock `serialize:"true"`
    55  	Signature      []byte                 `serialize:"true"`
    56  
    57  	id        ids.ID
    58  	timestamp time.Time
    59  	cert      *staking.Certificate
    60  	proposer  ids.NodeID
    61  	bytes     []byte
    62  }
    63  
    64  func (b *statelessBlock) ID() ids.ID {
    65  	return b.id
    66  }
    67  
    68  func (b *statelessBlock) ParentID() ids.ID {
    69  	return b.StatelessBlock.ParentID
    70  }
    71  
    72  func (b *statelessBlock) Block() []byte {
    73  	return b.StatelessBlock.Block
    74  }
    75  
    76  func (b *statelessBlock) Bytes() []byte {
    77  	return b.bytes
    78  }
    79  
    80  func (b *statelessBlock) initialize(bytes []byte) error {
    81  	b.bytes = bytes
    82  
    83  	// The serialized form of the block is the unsignedBytes followed by the
    84  	// signature, which is prefixed by a uint32. So, we need to strip off the
    85  	// signature as well as it's length prefix to get the unsigned bytes.
    86  	lenUnsignedBytes := len(bytes) - wrappers.IntLen - len(b.Signature)
    87  	unsignedBytes := bytes[:lenUnsignedBytes]
    88  	b.id = hashing.ComputeHash256Array(unsignedBytes)
    89  
    90  	b.timestamp = time.Unix(b.StatelessBlock.Timestamp, 0)
    91  	if len(b.StatelessBlock.Certificate) == 0 {
    92  		return nil
    93  	}
    94  
    95  	var err error
    96  	b.cert, err = staking.ParseCertificate(b.StatelessBlock.Certificate)
    97  	if err != nil {
    98  		return fmt.Errorf("%w: %w", errInvalidCertificate, err)
    99  	}
   100  
   101  	b.proposer = ids.NodeIDFromCert(b.cert)
   102  	return nil
   103  }
   104  
   105  func (b *statelessBlock) verify(chainID ids.ID) error {
   106  	if len(b.StatelessBlock.Certificate) == 0 {
   107  		if len(b.Signature) > 0 {
   108  			return errUnexpectedSignature
   109  		}
   110  		return nil
   111  	}
   112  
   113  	header, err := BuildHeader(chainID, b.StatelessBlock.ParentID, b.id)
   114  	if err != nil {
   115  		return err
   116  	}
   117  
   118  	headerBytes := header.Bytes()
   119  	return staking.CheckSignature(
   120  		b.cert,
   121  		headerBytes,
   122  		b.Signature,
   123  	)
   124  }
   125  
   126  func (b *statelessBlock) PChainHeight() uint64 {
   127  	return b.StatelessBlock.PChainHeight
   128  }
   129  
   130  func (b *statelessBlock) Timestamp() time.Time {
   131  	return b.timestamp
   132  }
   133  
   134  func (b *statelessBlock) Proposer() ids.NodeID {
   135  	return b.proposer
   136  }