package native import ( "encoding/binary" "github.com/tutus-one/tutus-chain/pkg/core/dao" "github.com/tutus-one/tutus-chain/pkg/core/storage" "github.com/tutus-one/tutus-chain/pkg/crypto/hash" "github.com/tutus-one/tutus-chain/pkg/util" ) // LOW-001: Event archival system for managing event log growth. type EventArchivalConfig struct { RetentionBlocks uint32 ArchiveEnabled bool CommitmentInterval uint32 } func DefaultEventArchivalConfig() *EventArchivalConfig { return &EventArchivalConfig{ RetentionBlocks: 2592000, ArchiveEnabled: false, CommitmentInterval: 8640, } } type EventArchivalState struct { LastArchivedBlock uint32 LastCommitmentBlock uint32 TotalEventsArchived uint64 TotalCommitments uint64 } type EventCommitment struct { CommitmentID uint64 StartBlock uint32 EndBlock uint32 MerkleRoot util.Uint256 EventCount uint64 CreatedAt uint32 } const ( eventArchivalPrefixConfig byte = 0xE0 eventArchivalPrefixState byte = 0xE1 eventArchivalPrefixCommitment byte = 0xE2 eventArchivalPrefixByBlock byte = 0xE3 ) type EventArchiver struct { contractID int32 } func NewEventArchiver(contractID int32) *EventArchiver { return &EventArchiver{contractID: contractID} } func (ea *EventArchiver) GetConfig(d *dao.Simple) *EventArchivalConfig { key := []byte{eventArchivalPrefixConfig} si := d.GetStorageItem(ea.contractID, key) if si == nil { return DefaultEventArchivalConfig() } cfg := &EventArchivalConfig{} cfg.RetentionBlocks = binary.BigEndian.Uint32(si[:4]) cfg.ArchiveEnabled = si[4] == 1 cfg.CommitmentInterval = binary.BigEndian.Uint32(si[5:9]) return cfg } func (ea *EventArchiver) PutConfig(d *dao.Simple, cfg *EventArchivalConfig) { key := []byte{eventArchivalPrefixConfig} data := make([]byte, 9) binary.BigEndian.PutUint32(data[:4], cfg.RetentionBlocks) if cfg.ArchiveEnabled { data[4] = 1 } binary.BigEndian.PutUint32(data[5:9], cfg.CommitmentInterval) d.PutStorageItem(ea.contractID, key, data) } func (ea *EventArchiver) GetState(d *dao.Simple) *EventArchivalState { key := []byte{eventArchivalPrefixState} si := d.GetStorageItem(ea.contractID, key) if si == nil { return &EventArchivalState{} } st := &EventArchivalState{} st.LastArchivedBlock = binary.BigEndian.Uint32(si[:4]) st.LastCommitmentBlock = binary.BigEndian.Uint32(si[4:8]) st.TotalEventsArchived = binary.BigEndian.Uint64(si[8:16]) st.TotalCommitments = binary.BigEndian.Uint64(si[16:24]) return st } func (ea *EventArchiver) PutState(d *dao.Simple, st *EventArchivalState) { key := []byte{eventArchivalPrefixState} data := make([]byte, 24) binary.BigEndian.PutUint32(data[:4], st.LastArchivedBlock) binary.BigEndian.PutUint32(data[4:8], st.LastCommitmentBlock) binary.BigEndian.PutUint64(data[8:16], st.TotalEventsArchived) binary.BigEndian.PutUint64(data[16:24], st.TotalCommitments) d.PutStorageItem(ea.contractID, key, data) } func (ea *EventArchiver) CreateCommitment(d *dao.Simple, startBlock, endBlock uint32, events [][]byte) *EventCommitment { st := ea.GetState(d) merkleRoot := computeEventsMerkleRoot(events) commitment := &EventCommitment{ CommitmentID: st.TotalCommitments + 1, StartBlock: startBlock, EndBlock: endBlock, MerkleRoot: merkleRoot, EventCount: uint64(len(events)), CreatedAt: endBlock, } ea.putCommitment(d, commitment) st.LastCommitmentBlock = endBlock st.TotalCommitments++ st.TotalEventsArchived += uint64(len(events)) ea.PutState(d, st) return commitment } func (ea *EventArchiver) GetCommitment(d *dao.Simple, commitmentID uint64) *EventCommitment { key := make([]byte, 9) key[0] = eventArchivalPrefixCommitment binary.BigEndian.PutUint64(key[1:], commitmentID) si := d.GetStorageItem(ea.contractID, key) if si == nil { return nil } c := &EventCommitment{} c.CommitmentID = binary.BigEndian.Uint64(si[:8]) c.StartBlock = binary.BigEndian.Uint32(si[8:12]) c.EndBlock = binary.BigEndian.Uint32(si[12:16]) copy(c.MerkleRoot[:], si[16:48]) c.EventCount = binary.BigEndian.Uint64(si[48:56]) c.CreatedAt = binary.BigEndian.Uint32(si[56:60]) return c } func (ea *EventArchiver) putCommitment(d *dao.Simple, c *EventCommitment) { key := make([]byte, 9) key[0] = eventArchivalPrefixCommitment binary.BigEndian.PutUint64(key[1:], c.CommitmentID) data := make([]byte, 60) binary.BigEndian.PutUint64(data[:8], c.CommitmentID) binary.BigEndian.PutUint32(data[8:12], c.StartBlock) binary.BigEndian.PutUint32(data[12:16], c.EndBlock) copy(data[16:48], c.MerkleRoot[:]) binary.BigEndian.PutUint64(data[48:56], c.EventCount) binary.BigEndian.PutUint32(data[56:60], c.CreatedAt) d.PutStorageItem(ea.contractID, key, data) blockKey := make([]byte, 5) blockKey[0] = eventArchivalPrefixByBlock binary.BigEndian.PutUint32(blockKey[1:], c.EndBlock) binary.BigEndian.PutUint64(data[:8], c.CommitmentID) d.PutStorageItem(ea.contractID, blockKey, data[:8]) } func (ea *EventArchiver) GetCommitmentForBlock(d *dao.Simple, blockHeight uint32) *EventCommitment { var foundID uint64 prefix := []byte{eventArchivalPrefixByBlock} d.Seek(ea.contractID, storage.SeekRange{Prefix: prefix}, func(k, v []byte) bool { if len(k) >= 4 && len(v) >= 8 { endBlock := binary.BigEndian.Uint32(k[:4]) if endBlock >= blockHeight { foundID = binary.BigEndian.Uint64(v[:8]) return false } } return true }) if foundID == 0 { return nil } return ea.GetCommitment(d, foundID) } func (ea *EventArchiver) ShouldCreateCommitment(d *dao.Simple, currentBlock uint32) bool { cfg := ea.GetConfig(d) if !cfg.ArchiveEnabled { return false } st := ea.GetState(d) return currentBlock >= st.LastCommitmentBlock+cfg.CommitmentInterval } func (ea *EventArchiver) CanPruneEvents(d *dao.Simple, blockHeight, currentBlock uint32) bool { cfg := ea.GetConfig(d) if !cfg.ArchiveEnabled { return false } if currentBlock < blockHeight+cfg.RetentionBlocks { return false } commitment := ea.GetCommitmentForBlock(d, blockHeight) return commitment != nil } func computeEventsMerkleRoot(events [][]byte) util.Uint256 { if len(events) == 0 { return util.Uint256{} } hashes := make([]util.Uint256, len(events)) for i, event := range events { hashes[i] = hash.Sha256(event) } for len(hashes) > 1 { if len(hashes)%2 == 1 { hashes = append(hashes, hashes[len(hashes)-1]) } newHashes := make([]util.Uint256, len(hashes)/2) for i := 0; i < len(hashes); i += 2 { combined := make([]byte, 64) copy(combined[:32], hashes[i][:]) copy(combined[32:], hashes[i+1][:]) newHashes[i/2] = hash.Sha256(combined) } hashes = newHashes } return hashes[0] } func VerifyEventInCommitment(eventData []byte, merkleProof []util.Uint256, index int, merkleRoot util.Uint256) bool { h := hash.Sha256(eventData) for _, proofElement := range merkleProof { var combined []byte if index%2 == 0 { combined = append(h[:], proofElement[:]...) } else { combined = append(proofElement[:], h[:]...) } h = hash.Sha256(combined) index /= 2 } return h == merkleRoot }