Files
threshold/protocols/lss/adapters/ton.go
T
Hanzo AI 9bce6722b4 threshold: Pulsar/Lens kernel adapters + corona KAT + Pulsar-SHA3 wiring
Squashed batch covering:

- protocols/pulsar/: lattice threshold lane (round-based wrapper for
  github.com/luxfi/pulsar kernel); doc.go documents Photon/Lumen/Beam/
  Pulsar/Pulse/Prism/Horizon/Quasar vocabulary stack from LP-105
- protocols/lens/: planned curve threshold sister kernel (design
  intent only; mirrors Pulsar shape; replaces stub LSS-FROST)

- protocols/lss/lss_pulsar.go + tests: LSS-Pulsar adapter
  (DynamicResharePulsar + PulsarSnapshotManager + BuildActivationTranscript).
  Now SHA3-suite-aware: PulsarConfig carries NebulaRoot, HashSuiteID,
  ImplementationVersion as optional transcript-binding fields that
  flow through to the activation message.

  10/10 acceptance tests pass:
    1. GroupKey preservation
    2. KeyEraID preservation
    3. Generation +1
    4. RollbackFrom=0 on forward
    5. t_old != t_new
    6. Disjoint set rotation
    7. Valid signature under unchanged GroupKey
    8. Pairwise material regenerated
    9. Rollback semantics
   10. Error surface
  Plus 1 new test for BuildActivationTranscript Nebula+suite fields.

- protocols/lss/lss_frost.go: marked DEPRECATED (placeholder Sign()/
  Refresh(), single-process simulation, hardcoded ChainKey/RID); will
  be replaced by lss_lens.go.

- corona KAT cross-oracle vs Go reference (N=16 deterministic seeds)
- corona+protocol+harness hardening against parallel-run flakes
- lss/adapters: drop NewXRPL/NewCardano panic; ship real XRPL address
  derivation
- corona SignWithConfig API expansion + test fixes
- v1.6.5 changelog
- go.sum h1 hashes for luxfi/log and 4 deps

Architecture (LP-105 / pulsar/DESIGN.md):
  LSS owns lifecycle (Generation, Rollback, snapshots, dealer/
  coordinator role separation). Pulsar owns lattice math (R_q shares,
  lattice Pedersen commits, Sign1/Sign2/Combine, Pulsar-SHA3 hash
  profile). The lss_pulsar adapter wires them. lss_lens (planned) does
  the same for curve math.

go.mod adds local replace github.com/luxfi/pulsar => ../pulsar.
2026-03-03 12:00:00 -08:00

421 lines
10 KiB
Go

// Package adapters - TON blockchain adapter for Ed25519 threshold signatures
package adapters
import (
"crypto/sha256"
"encoding/binary"
"errors"
"fmt"
"github.com/luxfi/threshold/pkg/math/curve"
)
// TONAdapter implements SignerAdapter for TON blockchain
// TON uses Ed25519 for signatures and supports Curve25519 conversion
type TONAdapter struct {
group curve.Curve
workchain int32 // TON workchain ID (-1 for masterchain, 0 for basechain)
}
// NewTONAdapter creates a new TON adapter
func NewTONAdapter(workchain int32) *TONAdapter {
return &TONAdapter{
group: curve.Ed25519{},
workchain: workchain,
}
}
// Digest computes TON message digest using BOC (Bag of Cells) hash
func (t *TONAdapter) Digest(tx interface{}) ([]byte, error) {
switch v := tx.(type) {
case *TONMessage:
return t.digestMessage(v)
case *TONTransaction:
return t.digestTransaction(v)
case []byte:
// Raw BOC bytes
return t.hashBOC(v), nil
default:
return nil, fmt.Errorf("unsupported TON transaction type: %T", tx)
}
}
// digestMessage computes digest for TON message
func (t *TONAdapter) digestMessage(msg *TONMessage) ([]byte, error) {
// TON uses BOC representation hash
boc := t.serializeToBOC(msg)
return t.hashBOC(boc), nil
}
// digestTransaction computes digest for TON transaction
func (t *TONAdapter) digestTransaction(tx *TONTransaction) ([]byte, error) {
// Serialize transaction to BOC format
boc := t.serializeTransactionToBOC(tx)
return t.hashBOC(boc), nil
}
// hashBOC computes SHA-256 hash of Bag of Cells
func (t *TONAdapter) hashBOC(boc []byte) []byte {
h := sha256.New()
h.Write(boc)
return h.Sum(nil)
}
// SignEC creates Ed25519 partial signature for TON
func (t *TONAdapter) SignEC(digest []byte, share Share) (PartialSig, error) {
// This would integrate with FROST protocol for Ed25519
// For testing, provide a placeholder R value
z, err := coerceScalar(t.group, share.Value)
if err != nil {
return nil, fmt.Errorf("ton: %w", err)
}
return &EdDSAPartialSig{
PartyID: share.ID,
R: t.group.NewBasePoint(), // Placeholder for testing
Z: z,
}, nil
}
// AggregateEC combines Ed25519 partial signatures
func (t *TONAdapter) AggregateEC(parts []PartialSig) (FullSig, error) {
if len(parts) == 0 {
return nil, errors.New("no partial signatures")
}
var r curve.Point
z := t.group.NewScalar()
expectedCurve := t.group.Name()
for i, part := range parts {
eddsaPart, ok := part.(*EdDSAPartialSig)
if !ok {
return nil, errors.New("invalid Ed25519 partial signature")
}
if eddsaPart.Z == nil {
return nil, fmt.Errorf("ton: ed25519 partial[%d] has nil scalar Z", i)
}
if got := eddsaPart.Z.Curve().Name(); got != expectedCurve {
return nil, fmt.Errorf("ton: ed25519 partial[%d] scalar Z on wrong curve (got %s, want %s)", i, got, expectedCurve)
}
if r == nil && eddsaPart.R != nil {
r = eddsaPart.R
}
z = z.Add(eddsaPart.Z)
}
return &EdDSAFullSig{
R: r,
Z: z,
}, nil
}
// Encode formats Ed25519 signature for TON
func (t *TONAdapter) Encode(full FullSig) ([]byte, error) {
eddsaSig, ok := full.(*EdDSAFullSig)
if !ok {
return nil, errors.New("invalid Ed25519 signature")
}
// Ed25519 signature: R (32 bytes) || z (32 bytes)
sig := make([]byte, 64)
// Copy R
rBytes, _ := eddsaSig.R.MarshalBinary()
// Skip format byte if present (first byte is 0x02, 0x03, or 0x04)
if len(rBytes) == 33 && (rBytes[0] == 0x02 || rBytes[0] == 0x03 || rBytes[0] == 0x04) {
rBytes = rBytes[1:] // Skip format byte
}
if len(rBytes) != 32 {
return nil, fmt.Errorf("invalid R length: %d", len(rBytes))
}
copy(sig[:32], rBytes)
// Copy z
zBytes, _ := eddsaSig.Z.MarshalBinary()
if len(zBytes) > 32 {
return nil, fmt.Errorf("invalid z length: %d", len(zBytes))
}
copy(sig[32+(32-len(zBytes)):], zBytes)
return sig, nil
}
// ValidateConfig validates TON-specific configuration
func (t *TONAdapter) ValidateConfig(config *UnifiedConfig) error {
if config.SignatureScheme != SignatureEdDSA {
return errors.New("TON requires Ed25519 signatures")
}
if _, ok := config.Group.(curve.Ed25519); !ok {
return errors.New("TON requires Ed25519 curve")
}
return nil
}
// TON-specific structures
type TONMessage struct {
Info TONMessageInfo
Init *TONStateInit // Optional
Body []byte
}
type TONMessageInfo struct {
IHRDisabled bool
Bounce bool
Bounced bool
Source TONAddress
Destination TONAddress
Value TONCurrencyCollection
IHRFee uint64
FwdFee uint64
CreatedLt uint64
CreatedAt uint32
}
type TONAddress struct {
Workchain int32
Hash [32]byte
}
type TONStateInit struct {
Code []byte
Data []byte
Library []byte
}
type TONCurrencyCollection struct {
Grams uint64
ExtraCurrencies map[uint32]uint64
}
type TONTransaction struct {
Account TONAddress
Lt uint64
PrevTransHash [32]byte
PrevTransLt uint64
Now uint32
OutMsgCount uint16
OrigStatus AccountStatus
EndStatus AccountStatus
InMsg *TONMessage
OutMsgs []*TONMessage
}
type AccountStatus byte
const (
AccountUninit AccountStatus = iota
AccountActive
AccountFrozen
)
// serializeToBOC serializes a TON message to Bag of Cells format
func (t *TONAdapter) serializeToBOC(msg *TONMessage) []byte {
// Simplified BOC serialization
// Actual implementation would follow TON's BOC specification
var boc []byte
// Serialize message info
boc = append(boc, t.serializeMessageInfo(&msg.Info)...)
// Serialize optional init
if msg.Init != nil {
boc = append(boc, 1) // Has init
boc = append(boc, t.serializeStateInit(msg.Init)...)
} else {
boc = append(boc, 0) // No init
}
// Serialize body
boc = append(boc, msg.Body...)
return boc
}
// serializeTransactionToBOC serializes a TON transaction to BOC format
func (t *TONAdapter) serializeTransactionToBOC(tx *TONTransaction) []byte {
var boc []byte
// Account address
boc = append(boc, t.serializeAddress(&tx.Account)...)
// Logical time
ltBytes := make([]byte, 8)
binary.BigEndian.PutUint64(ltBytes, tx.Lt)
boc = append(boc, ltBytes...)
// Previous transaction hash
boc = append(boc, tx.PrevTransHash[:]...)
// Previous transaction lt
prevLtBytes := make([]byte, 8)
binary.BigEndian.PutUint64(prevLtBytes, tx.PrevTransLt)
boc = append(boc, prevLtBytes...)
// Timestamp
nowBytes := make([]byte, 4)
binary.BigEndian.PutUint32(nowBytes, tx.Now)
boc = append(boc, nowBytes...)
// Out messages count
outCountBytes := make([]byte, 2)
binary.BigEndian.PutUint16(outCountBytes, tx.OutMsgCount)
boc = append(boc, outCountBytes...)
// Account statuses
boc = append(boc, byte(tx.OrigStatus))
boc = append(boc, byte(tx.EndStatus))
return boc
}
// serializeMessageInfo serializes TON message info
func (t *TONAdapter) serializeMessageInfo(info *TONMessageInfo) []byte {
var data []byte
// Flags
flags := byte(0)
if info.IHRDisabled {
flags |= 0x01
}
if info.Bounce {
flags |= 0x02
}
if info.Bounced {
flags |= 0x04
}
data = append(data, flags)
// Addresses
data = append(data, t.serializeAddress(&info.Source)...)
data = append(data, t.serializeAddress(&info.Destination)...)
// Value
valueBytes := make([]byte, 8)
binary.BigEndian.PutUint64(valueBytes, info.Value.Grams)
data = append(data, valueBytes...)
// Fees
ihrFeeBytes := make([]byte, 8)
binary.BigEndian.PutUint64(ihrFeeBytes, info.IHRFee)
data = append(data, ihrFeeBytes...)
fwdFeeBytes := make([]byte, 8)
binary.BigEndian.PutUint64(fwdFeeBytes, info.FwdFee)
data = append(data, fwdFeeBytes...)
// Logical time and creation time
ltBytes := make([]byte, 8)
binary.BigEndian.PutUint64(ltBytes, info.CreatedLt)
data = append(data, ltBytes...)
atBytes := make([]byte, 4)
binary.BigEndian.PutUint32(atBytes, info.CreatedAt)
data = append(data, atBytes...)
return data
}
// serializeAddress serializes a TON address
func (t *TONAdapter) serializeAddress(addr *TONAddress) []byte {
var data []byte
// Workchain (variable length integer)
wcBytes := make([]byte, 4)
binary.BigEndian.PutUint32(wcBytes, uint32(addr.Workchain))
data = append(data, wcBytes...)
// Address hash
data = append(data, addr.Hash[:]...)
return data
}
// serializeStateInit serializes TON state init
func (t *TONAdapter) serializeStateInit(init *TONStateInit) []byte {
var data []byte
// Has code
if len(init.Code) > 0 {
data = append(data, 1)
data = append(data, init.Code...)
} else {
data = append(data, 0)
}
// Has data
if len(init.Data) > 0 {
data = append(data, 1)
data = append(data, init.Data...)
} else {
data = append(data, 0)
}
// Has library
if len(init.Library) > 0 {
data = append(data, 1)
data = append(data, init.Library...)
} else {
data = append(data, 0)
}
return data
}
// GenerateTONAddress generates a TON address from public key
func (t *TONAdapter) GenerateTONAddress(publicKey [32]byte) TONAddress {
// TON address = workchain:hash(stateInit)
// Simplified version - actual would compute from StateInit
h := sha256.Sum256(publicKey[:])
return TONAddress{
Workchain: t.workchain,
Hash: h,
}
}
// CreateWalletStateInit creates initial state for TON wallet
func (t *TONAdapter) CreateWalletStateInit(publicKey [32]byte, walletID uint32) *TONStateInit {
// This would create the actual wallet contract StateInit
// Simplified placeholder
return &TONStateInit{
Code: []byte("wallet_v4_code"), // Actual wallet code
Data: append(publicKey[:], make([]byte, 4)...), // pubkey + wallet_id
}
}
// EstimateGas estimates gas for TON transaction
func (t *TONAdapter) EstimateGas(msg *TONMessage) uint64 {
// Base computation cost
baseCost := uint64(10000)
// Message size cost
msgSize := len(t.serializeToBOC(msg))
sizeCost := uint64(msgSize * 10)
// Storage cost if deploying contract
storageCost := uint64(0)
if msg.Init != nil {
storageCost = 50000
}
return baseCost + sizeCost + storageCost
}
// GetTONConfig returns default TON configuration
func GetDefaultTONConfig(workchain int32) map[string]interface{} {
return map[string]interface{}{
"workchain": workchain,
"signature_type": SignatureEdDSA,
"curve": "Ed25519",
"hash_algorithm": "SHA256",
"boc_format": "standard",
"wallet_version": "v4r2",
}
}