protocol/submission
Package submission implements TwoListBase — the shared anonymous state machine foundation embedded by every shyware domain state machine (shyvoting, shywire, shystore, shychat).
Import path: github.com/NickCarducci/Shyware-SDK/protocol/submission
TwoListBase
TwoListBase owns the LevelDB-backed canonical state, enforces the two-list invariant, manages period lifecycle, and delegates KMS-signed period-close attestation.
type TwoListBase struct { /* unexported */ }
func NewTwoListBase(ctx context.Context, db dbm.DB, kmsKeyID string, logger log.Logger) (*TwoListBase, error)
Every domain state machine embeds TwoListBase and delegates atomic writes, period management, and attestation to it. Domain-specific logic (validation predicates, tx routing) is layered on top.
Period lifecycle
// Open a new period (poll, mailbox, bucket, etc.)
err := b.CreatePeriod(periodID, domainType, startTime, endTime)
// Query a period
rec := b.GetPeriod(periodID) // → *PeriodRecord
// Close with KMS attestation
closure, events, err := b.ClosePeriod(ctx, periodID, closingHeight)
// Rolling attestation checkpoint (without closing)
events, err := b.CommitRollingAttestation(ctx, periodID)
Two-list writes
// Atomic List 1 + List 2 write — the core protocol operation
err := b.SubmitToLists(periodID, submissionID, submissionData, identityHash, participantData)
// Sealed List 2 identity attribute variant (shybrowser dual-sealing)
err := b.SubmitToListsWithSealedAttrs(periodID, submissionID, submissionData, identityHash, sealedAttrs)
// Direction change — replace List 1 entry; List 2 unchanged
err := b.UpdateSubmission(periodID, oldSubmissionID, newSubmissionID, newData, identityHash)
// Bilateral withdrawal — remove from both lists atomically
err := b.WithdrawFromLists(periodID, submissionID, identityHash)
Presence and count queries
b.HasParticipant(periodID, identityHash) bool
b.HasSubmission(periodID, submissionID) bool
b.SubmissionPresence(periodID, submissionID) ([]byte, error)
l1, l2 := b.CountsForPeriod(periodID) // count-match verification
CountsForPeriod is used by domain state machines to verify |L1| == |L2| before committing a period close.
Write-only posture
b.SetWriteOnly(true)
b.IsWriteOnly() bool
When write-only is active, SubmitToLists succeeds but the submission receipt is not written to the off-chain linkage store. The participant retains only the direction-free submissionID.
Beacon
b.RecordBeacon(height int64, blockHashHex string)
window := b.BeaconWindow() // map[int64]string — recent block heights → hashes
The beacon window enables submission_identifier_derivation: "nonce_plus_payload" — clients include a recent block hash in the submission identifier to bind submissions to a specific block height, defeating replay at the ABCI layer.
Validator management
events, err := b.RegisterValidator(pubKeyBase64 string, power int64, name string)
updates := b.GetPendingValidatorUpdates()
Commit
appHash, err := b.Commit()
Called by the ABCI app's Commit handler. Returns the new app hash.
Core types
type SubmissionRecord struct {
ID string `json:"id"`
PeriodID string `json:"period_id"`
Data json.RawMessage `json:"data"`
Height int64 `json:"height"`
}
type ParticipantRecord struct {
IdentityHash string `json:"identity_hash"`
PeriodID string `json:"period_id"`
Height int64 `json:"height"`
}
type PeriodRecord struct {
ID string `json:"id"`
DomainType string `json:"domain_type"`
StartTime int64 `json:"start_time"`
EndTime int64 `json:"end_time"`
Closed bool `json:"closed"`
}
type ClosureRecord struct {
PeriodID string `json:"period_id"`
TotalSubmissions int64 `json:"total_submissions"`
MerkleRoot1 string `json:"merkle_root_1"` // over submission IDs (List 1)
MerkleRoot2 string `json:"merkle_root_2"` // over identity hashes (List 2)
ClosingHeight int64 `json:"closing_height"`
PublicKey string `json:"public_key"` // KMS public key (DER)
Signature string `json:"signature"` // KMS signature
}
type AttestationCheckpoint struct {
PeriodID string `json:"period_id"`
Height int64 `json:"height"`
Signature string `json:"signature"`
}
Sentinel errors
type ErrCountMismatch struct { L1, L2 int } // |L1| ≠ |L2| at period close
type ErrDuplicateParticipant struct { Identity string } // identity already in List 2
type ErrPeriodNotFound struct { PeriodID string }
type ErrWriteOnlyPosture struct{} // receipt suppressed in write-only mode