feat(gl): expose ledger application and grpc operations

This commit is contained in:
2026-08-14 19:11:29 +03:30
parent 0b3eaa0c3d
commit 76905ab451
17 changed files with 1724 additions and 16 deletions
+10
View File
@@ -0,0 +1,10 @@
package ledger
import "errors"
var (
ErrNotFound = errors.New("ledger record not found")
ErrIdempotencyConflict = errors.New("idempotency key belongs to a different payload")
ErrIncompleteJournal = errors.New("idempotency key belongs to an unsealed journal")
ErrAlreadyReversed = errors.New("journal already has a reversal")
)
+74
View File
@@ -0,0 +1,74 @@
package ledger
import (
"encoding/hex"
"fmt"
"strings"
"time"
)
type TransactionState string
const (
TransactionStateCreated TransactionState = "CREATED"
TransactionStatePendingTransaction TransactionState = "PENDING_TRANSACTION"
TransactionStatePendingAdmin TransactionState = "PENDING_ADMIN"
TransactionStateSuccessful TransactionState = "SUCCESSFUL"
TransactionStateFailed TransactionState = "FAILED"
TransactionStateSuspended TransactionState = "SUSPENDED"
)
func (s TransactionState) Valid() bool {
switch s {
case TransactionStateCreated,
TransactionStatePendingTransaction,
TransactionStatePendingAdmin,
TransactionStateSuccessful,
TransactionStateFailed,
TransactionStateSuspended:
return true
default:
return false
}
}
type TransactionEvent struct {
ID string
SourceService string
IdempotencyKey string
SourceTransactionID string
TrackingCode string
EventVersion uint32
State TransactionState
ErrorCode string
ErrorMessage string
OccurredAt time.Time
RecordedAt time.Time
CorrelationID string
ActorID string
Blockchain BlockchainReference
Metadata map[string]string
PayloadHash string
}
func (e TransactionEvent) Validate() error {
if e.ID == "" || e.SourceService == "" || e.IdempotencyKey == "" || e.SourceTransactionID == "" {
return fmt.Errorf("transaction event identity fields are required")
}
if e.EventVersion == 0 {
return fmt.Errorf("event version must be positive")
}
if !e.State.Valid() {
return fmt.Errorf("invalid transaction state %q", e.State)
}
if e.OccurredAt.IsZero() {
return fmt.Errorf("occurred time is required")
}
if len(e.PayloadHash) != 64 || e.PayloadHash != strings.ToLower(e.PayloadHash) {
return fmt.Errorf("payload hash must be a lowercase SHA-256 hex string")
}
if _, err := hex.DecodeString(e.PayloadHash); err != nil {
return fmt.Errorf("payload hash must be a lowercase SHA-256 hex string")
}
return nil
}
+15
View File
@@ -80,6 +80,7 @@ type Journal struct {
Entries []Entry
ReversalOfJournalID string
OccurredAt time.Time
RecordedAt time.Time
CorrelationID string
ActorID string
Blockchain BlockchainReference
@@ -87,6 +88,20 @@ type Journal struct {
PayloadHash string
}
type AppendResult struct {
JournalID string
AlreadyExists bool
}
type JournalFilter struct {
Account *AccountReference
AssetID *int64
RecordedFrom *time.Time
RecordedTo *time.Time
Limit int
Offset int
}
func (j Journal) Validate() error {
if j.ID == "" || j.SourceService == "" || j.IdempotencyKey == "" || j.SourceTransactionID == "" || j.EffectKind == "" {
return fmt.Errorf("journal identity fields are required")