zap: carry the Quasar EXPORT capability across the rpcchainvm boundary (wire)

Two-tier consensus v1.36 splits finality into Nova (local, reorgable accept tip)
and Quasar (⅔-by-stake EXPORT-final). A VM's `finalized`/`safe` tags and its
cross-chain (warp) export gate must resolve to the Quasar height. But the
C-Chain EVM runs as a SEPARATE rpcchainvm plugin process, and nothing carried
the export height across the ZAP boundary — so the whole export tier was dormant
in the real deploy (finalized/safe stuck at genesis).

Wire additions (OPTIONAL, capability-gated — generic VMs are unaffected):
- MsgSetQuasarFinalized (60) / MsgQuasarHeight (61): the two export RPCs. Kept
  < 0x40 so the response/error flags OR in cleanly, grouped after Warp (50-59).
- InitializeResponse.Capabilities (uint64) + CapQuasarExport bit: the plugin
  advertises the export capability in the handshake so the node wires the
  observer only for an export-capable VM. Appended to the struct codec (all four
  encode/decode sites go through the struct methods, so it stays consistent).
- SetQuasarFinalizedRequest / QuasarHeightResponse message structs.

Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
2026-07-10 10:14:31 -07:00
co-authored by Hanzo Dev
parent 1fcd6b64f0
commit 8bdf9cbf1b
2 changed files with 70 additions and 1 deletions
+60 -1
View File
@@ -115,8 +115,25 @@ type InitializeResponse struct {
Height uint64
Bytes []byte
Timestamp int64
// Capabilities is a bitfield of OPTIONAL cross-boundary add-ons the VM
// implements (see the Cap* constants). The node reads it once at handshake
// to decide which add-ons to wire; an unset bit → the node treats that
// capability as absent and stays on the generic path.
Capabilities uint64
}
// VM capability bits advertised in InitializeResponse.Capabilities. A VM sets a
// bit to tell the node it implements the corresponding OPTIONAL add-on that is
// NOT part of the generic ChainVM contract. Discovered at the handshake, never
// braided into ChainVM — generic VMs leave every bit clear.
const (
// CapQuasarExport: the VM tracks a Quasar (⅔-by-stake) EXPORT-FINAL height
// distinct from its reorgable local accept tip, and answers
// MsgSetQuasarFinalized / MsgQuasarHeight. Set by the C-Chain EVM; the node
// wires the consensus export-frontier observer only when this bit is set.
CapQuasarExport uint64 = 1 << 0
)
// Encode serializes InitializeResponse to the buffer
func (m *InitializeResponse) Encode(buf *Buffer) {
buf.WriteBytes(m.LastAcceptedID)
@@ -124,6 +141,7 @@ func (m *InitializeResponse) Encode(buf *Buffer) {
buf.WriteUint64(m.Height)
buf.WriteBytes(m.Bytes)
buf.WriteInt64(m.Timestamp)
buf.WriteUint64(m.Capabilities)
}
// Decode deserializes InitializeResponse from the reader
@@ -141,7 +159,10 @@ func (m *InitializeResponse) Decode(r *Reader) error {
if m.Bytes, err = r.ReadBytes(); err != nil {
return err
}
m.Timestamp, err = r.ReadInt64()
if m.Timestamp, err = r.ReadInt64(); err != nil {
return err
}
m.Capabilities, err = r.ReadUint64()
return err
}
@@ -850,3 +871,41 @@ func (m *NewHTTPHandlerResponse) Decode(r *Reader) error {
m.ServerAddr, err = r.ReadString()
return err
}
// SetQuasarFinalizedRequest carries a new Quasar (⅔-by-stake) EXPORT-FINAL height
// from the node's consensus export-frontier observer to the VM (MsgSetQuasarFinalized).
// The VM persists it; the response is empty.
type SetQuasarFinalizedRequest struct {
Height uint64
}
// Encode serializes SetQuasarFinalizedRequest to the buffer
func (m *SetQuasarFinalizedRequest) Encode(buf *Buffer) {
buf.WriteUint64(m.Height)
}
// Decode deserializes SetQuasarFinalizedRequest from the reader
func (m *SetQuasarFinalizedRequest) Decode(r *Reader) error {
var err error
m.Height, err = r.ReadUint64()
return err
}
// QuasarHeightResponse carries the VM's current accept-tip-CLAMPED Quasar
// EXPORT-FINAL height (MsgQuasarHeight) — 0 before the first export forms. The
// MsgQuasarHeight request has no fields (empty payload).
type QuasarHeightResponse struct {
Height uint64
}
// Encode serializes QuasarHeightResponse to the buffer
func (m *QuasarHeightResponse) Encode(buf *Buffer) {
buf.WriteUint64(m.Height)
}
// Decode deserializes QuasarHeightResponse from the reader
func (m *QuasarHeightResponse) Decode(r *Reader) error {
var err error
m.Height, err = r.ReadUint64()
return err
}
+10
View File
@@ -75,6 +75,16 @@ const (
MsgWarpGetPublicKey MessageType = 51
MsgWarpBatchSign MessageType = 52
// Quasar EXPORT methods (60-63): the two-tier consensus (v1.36) export
// frontier — Quasar = ⅔-by-stake, the reorg-safe finality a VM's
// `finalized`/`safe` tags and cross-chain (warp) export gate must resolve
// to instead of the reorgable Nova accept tip — carried across the plugin
// process boundary. OPTIONAL: only a VM that advertises CapQuasarExport in
// the Initialize handshake (see vm.go) is ever sent these; a generic VM is
// never asked. Kept < 0x40 so the response/error flags OR in cleanly.
MsgSetQuasarFinalized MessageType = 60
MsgQuasarHeight MessageType = 61
// Response flag - set on response messages (bit 7 / high bit).
// All message types must be < 64 (0x40) to allow OR with this flag
// AND with MsgErrorFlag below.