package blockchain import ( "bytes" "crypto/ed25519" "crypto/sha256" "encoding/binary" "encoding/hex" "encoding/json" "errors" "time" ) // Block is the fundamental unit of the chain. type Block struct { Index uint64 `json:"index"` Timestamp time.Time `json:"timestamp"` Transactions []*Transaction `json:"transactions"` PrevHash []byte `json:"prev_hash"` Hash []byte `json:"hash"` // SHA-256 over canonical fields ValidatorSig []byte `json:"validator_sig"` // Ed25519 sig over Hash Validator string `json:"validator"` // hex pub key of signing validator // TotalFees collected in this block (credited to Validator) TotalFees uint64 `json:"total_fees"` } // canonicalBytes returns a deterministic byte slice for hashing. // Order: index | timestamp | prev_hash | tx_hashes | total_fees | validator func (b *Block) canonicalBytes() []byte { var buf bytes.Buffer // 8-byte big-endian index idxBuf := make([]byte, 8) binary.BigEndian.PutUint64(idxBuf, b.Index) buf.Write(idxBuf) // 8-byte unix nano timestamp tsBuf := make([]byte, 8) binary.BigEndian.PutUint64(tsBuf, uint64(b.Timestamp.UnixNano())) buf.Write(tsBuf) buf.Write(b.PrevHash) // Hash each transaction and include its hash for _, tx := range b.Transactions { h := txHash(tx) buf.Write(h) } // 8-byte fees feesBuf := make([]byte, 8) binary.BigEndian.PutUint64(feesBuf, b.TotalFees) buf.Write(feesBuf) buf.WriteString(b.Validator) return buf.Bytes() } // txHash returns SHA-256 of the canonical transaction bytes. func txHash(tx *Transaction) []byte { data, _ := json.Marshal(tx) h := sha256.Sum256(data) return h[:] } // ComputeHash fills b.Hash from the canonical bytes. func (b *Block) ComputeHash() { sum := sha256.Sum256(b.canonicalBytes()) b.Hash = sum[:] } // Sign signs b.Hash with the given Ed25519 private key and stores the signature. func (b *Block) Sign(privKey ed25519.PrivateKey) { b.ValidatorSig = ed25519.Sign(privKey, b.Hash) } // Validate checks the block's structural integrity: // 1. Hash matches canonical bytes // 2. ValidatorSig is a valid Ed25519 signature over Hash // 3. PrevHash is provided (except genesis) func (b *Block) Validate(prevHash []byte) error { // Recompute and compare hash sum := sha256.Sum256(b.canonicalBytes()) if !bytes.Equal(sum[:], b.Hash) { return errors.New("block hash mismatch") } // Verify validator signature pubKeyBytes, err := hex.DecodeString(b.Validator) if err != nil { return errors.New("invalid validator pub key hex") } if !ed25519.Verify(ed25519.PublicKey(pubKeyBytes), b.Hash, b.ValidatorSig) { return errors.New("invalid validator signature") } // Check chain linkage (skip for genesis) if b.Index > 0 { if !bytes.Equal(b.PrevHash, prevHash) { return errors.New("prev_hash mismatch") } } // Validate each transaction's fee minimum var totalFees uint64 for _, tx := range b.Transactions { if tx.Fee < MinFee { return errors.New("transaction fee below minimum") } totalFees += tx.Fee } if totalFees != b.TotalFees { return errors.New("total_fees mismatch") } return nil } // GenesisBlock creates the first block with no transactions. // It is signed by the bootstrap validator. func GenesisBlock(validatorPubHex string, privKey ed25519.PrivateKey) *Block { b := &Block{ Index: 0, Timestamp: time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC), Transactions: []*Transaction{}, PrevHash: bytes.Repeat([]byte{0}, 32), Validator: validatorPubHex, TotalFees: 0, } b.ComputeHash() b.Sign(privKey) return b } // HashHex returns the block hash as a hex string. func (b *Block) HashHex() string { return hex.EncodeToString(b.Hash) }