399 lines
11 KiB
Go
399 lines
11 KiB
Go
package wallet
|
|
|
|
import (
|
|
"github.com/btcsuite/btcd/btcutil/base58"
|
|
"github.com/phamminh0811/private-grpc/crypto"
|
|
"github.com/phamminh0811/private-grpc/nockchain"
|
|
)
|
|
|
|
func ParseBalanceEntry(entry *nockchain.BalanceEntry) nockchain.NockchainNote {
|
|
name := &nockchain.NockchainName{
|
|
First: crypto.Tip5HashToBase58([5]uint64{
|
|
entry.Name.First.Belt_1.Value,
|
|
entry.Name.First.Belt_2.Value,
|
|
entry.Name.First.Belt_3.Value,
|
|
entry.Name.First.Belt_4.Value,
|
|
entry.Name.First.Belt_5.Value,
|
|
}),
|
|
Last: crypto.Tip5HashToBase58([5]uint64{
|
|
entry.Name.Last.Belt_1.Value,
|
|
entry.Name.Last.Belt_2.Value,
|
|
entry.Name.Last.Belt_3.Value,
|
|
entry.Name.Last.Belt_4.Value,
|
|
entry.Name.Last.Belt_5.Value,
|
|
}),
|
|
}
|
|
switch entry.Note.NoteVersion.(type) {
|
|
case *nockchain.Note_Legacy:
|
|
note := entry.Note.GetLegacy()
|
|
version := nockchain.Version(note.Version.Value)
|
|
pubkeys := []string{}
|
|
for _, pk := range note.Lock.SchnorrPubkeys {
|
|
pkPoint := crypto.CheetahPoint{
|
|
X: [6]crypto.Belt{
|
|
{Value: pk.Value.X.Belt_1.Value},
|
|
{Value: pk.Value.X.Belt_2.Value},
|
|
{Value: pk.Value.X.Belt_3.Value},
|
|
{Value: pk.Value.X.Belt_4.Value},
|
|
{Value: pk.Value.X.Belt_5.Value},
|
|
{Value: pk.Value.X.Belt_6.Value},
|
|
},
|
|
Y: [6]crypto.Belt{
|
|
{Value: pk.Value.Y.Belt_1.Value},
|
|
{Value: pk.Value.Y.Belt_2.Value},
|
|
{Value: pk.Value.Y.Belt_3.Value},
|
|
{Value: pk.Value.Y.Belt_4.Value},
|
|
{Value: pk.Value.Y.Belt_5.Value},
|
|
{Value: pk.Value.Y.Belt_6.Value},
|
|
},
|
|
Inf: pk.Value.Inf,
|
|
}
|
|
|
|
pkStr := base58.Encode(pkPoint.Bytes())
|
|
pubkeys = append(pubkeys, pkStr)
|
|
}
|
|
sourceHash := [5]uint64{
|
|
note.Source.Hash.Belt_1.Value,
|
|
note.Source.Hash.Belt_2.Value,
|
|
note.Source.Hash.Belt_3.Value,
|
|
note.Source.Hash.Belt_4.Value,
|
|
note.Source.Hash.Belt_5.Value,
|
|
}
|
|
return nockchain.NockchainNote{
|
|
Note: &nockchain.NockchainNote_V0{
|
|
V0: &nockchain.NockchainNoteV0{
|
|
Version: version,
|
|
OriginPage: note.OriginPage.Value,
|
|
Name: name,
|
|
Lock: &nockchain.NockchainLock{
|
|
KeysRequired: uint64(note.Lock.KeysRequired),
|
|
Pubkeys: pubkeys,
|
|
},
|
|
Source: &nockchain.NockchainSource{
|
|
Source: crypto.Tip5HashToBase58(sourceHash),
|
|
IsCoinbase: note.Source.Coinbase,
|
|
},
|
|
Asset: note.Assets.Value,
|
|
},
|
|
},
|
|
}
|
|
case *nockchain.Note_V1:
|
|
note := entry.Note.GetV1()
|
|
version := nockchain.Version(note.Version.Value)
|
|
if len(note.NoteData.Entries) != 1 || note.NoteData.Entries[0].Key != "lock" {
|
|
panic("invalid note data")
|
|
}
|
|
lock := DecodeNoteData(note.NoteData.Entries[0].Blob)
|
|
|
|
return nockchain.NockchainNote{
|
|
Note: &nockchain.NockchainNote_V1{
|
|
V1: &nockchain.NockchainNoteV1{
|
|
Version: version,
|
|
OriginPage: note.OriginPage.Value,
|
|
Name: name,
|
|
NoteData: lock,
|
|
Assets: note.Assets.Value,
|
|
},
|
|
},
|
|
}
|
|
default:
|
|
return nockchain.NockchainNote{}
|
|
}
|
|
}
|
|
|
|
func ConvertNamedSpend(spend *nockchain.NockchainNamedSpend) (*nockchain.SpendEntry, error) {
|
|
convertedSpend := &nockchain.Spend{}
|
|
switch spend.SpendKind.(type) {
|
|
case *nockchain.NockchainNamedSpend_Legacy:
|
|
legacySpend := spend.GetLegacy()
|
|
signature, err := ConvertSignatures(legacySpend.Signatures)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
seeds := []*nockchain.Seed{}
|
|
for _, seed := range legacySpend.Seeds {
|
|
seeds = append(seeds, &nockchain.Seed{
|
|
OutputSource: &nockchain.Source{
|
|
Hash: ParseHash(seed.OutputSource.Source),
|
|
Coinbase: seed.OutputSource.IsCoinbase,
|
|
},
|
|
LockRoot: ParseHash(seed.LockRoot),
|
|
NoteData: &nockchain.NoteData{
|
|
Entries: []*nockchain.NoteDataEntry{
|
|
{
|
|
Key: "lock",
|
|
Blob: EncodeNoteData(seed.NoteData),
|
|
},
|
|
},
|
|
},
|
|
Gift: &nockchain.Nicks{
|
|
Value: seed.Gift,
|
|
},
|
|
ParentHash: ParseHash(seed.ParentHash),
|
|
})
|
|
}
|
|
convertedSpend = &nockchain.Spend{
|
|
SpendKind: &nockchain.Spend_Legacy{
|
|
Legacy: &nockchain.LegacySpend{
|
|
Signature: signature,
|
|
Seeds: seeds,
|
|
MinerFeeNicks: &nockchain.Nicks{
|
|
Value: legacySpend.Fee,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
case *nockchain.NockchainNamedSpend_Witness:
|
|
witnessSpend := spend.GetWitness()
|
|
seeds := []*nockchain.Seed{}
|
|
for _, seed := range witnessSpend.Seeds {
|
|
seeds = append(seeds, &nockchain.Seed{
|
|
OutputSource: &nockchain.Source{
|
|
Hash: ParseHash(seed.OutputSource.Source),
|
|
Coinbase: seed.OutputSource.IsCoinbase,
|
|
},
|
|
LockRoot: ParseHash(seed.LockRoot),
|
|
NoteData: &nockchain.NoteData{
|
|
Entries: []*nockchain.NoteDataEntry{
|
|
{
|
|
Key: "lock",
|
|
Blob: EncodeNoteData(seed.NoteData),
|
|
},
|
|
},
|
|
},
|
|
Gift: &nockchain.Nicks{
|
|
Value: seed.Gift,
|
|
},
|
|
ParentHash: ParseHash(seed.ParentHash),
|
|
})
|
|
}
|
|
|
|
witness := witnessSpend.Witness[0]
|
|
lockHashes := []*nockchain.Hash{}
|
|
for i := 0; i < len(witness.Lmp.SpendCondition.Pubkeys); i++ {
|
|
lockHashes = append(lockHashes, ParseHash(witness.Lmp.SpendCondition.Pubkeys[i]))
|
|
}
|
|
|
|
pkhSigs := []*nockchain.PkhSignatureEntry{}
|
|
for _, pkh := range witness.Pkh {
|
|
pk, err := crypto.CheetaPointFromBytes(base58.Decode(pkh.Pubkey))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
schnorrPk, err := ParseSchnorrPubkey(pkh.Pubkey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
pkHash := HashPubkey(pk)
|
|
pkhSigs = append(pkhSigs, &nockchain.PkhSignatureEntry{
|
|
Hash: ParseHash(crypto.Tip5HashToBase58(pkHash)),
|
|
Pubkey: schnorrPk,
|
|
Signature: &nockchain.SchnorrSignature{
|
|
Chal: ParseEightBelt(pkh.Chal),
|
|
Sig: ParseEightBelt(pkh.Sig),
|
|
},
|
|
})
|
|
}
|
|
convertedSpend = &nockchain.Spend{
|
|
SpendKind: &nockchain.Spend_Witness{
|
|
Witness: &nockchain.WitnessSpend{
|
|
Witness: &nockchain.Witness{
|
|
LockMerkleProof: &nockchain.LockMerkleProof{
|
|
SpendCondition: &nockchain.SpendCondition{
|
|
Primitives: []*nockchain.LockPrimitive{
|
|
{
|
|
Primitive: &nockchain.LockPrimitive_Pkh{
|
|
Pkh: &nockchain.PkhLock{
|
|
M: witness.Lmp.SpendCondition.KeysRequired,
|
|
Hashes: lockHashes,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
Axis: witness.Lmp.Axis,
|
|
Proof: &nockchain.MerkleProof{
|
|
Root: ParseHash(witness.Lmp.MerkleRoot),
|
|
Path: []*nockchain.Hash{},
|
|
},
|
|
},
|
|
PkhSignature: &nockchain.PkhSignature{
|
|
Entries: pkhSigs,
|
|
},
|
|
},
|
|
Seeds: seeds,
|
|
Fee: &nockchain.Nicks{
|
|
Value: witnessSpend.Fee,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
// Conversion logic here
|
|
return &nockchain.SpendEntry{
|
|
Name: ConvertName(spend.Name),
|
|
Spend: convertedSpend,
|
|
}, nil
|
|
}
|
|
|
|
func ConvertSignatures(signatures []*nockchain.NockchainSignature) (*nockchain.Signature, error) {
|
|
entries := make([]*nockchain.SignatureEntry, len(signatures))
|
|
for i, sig := range signatures {
|
|
pubkey, err := ParseSchnorrPubkey(sig.Pubkey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
entries[i] = &nockchain.SignatureEntry{
|
|
SchnorrPubkey: pubkey,
|
|
Signature: &nockchain.SchnorrSignature{
|
|
Chal: ParseEightBelt(sig.Chal),
|
|
Sig: ParseEightBelt(sig.Sig),
|
|
},
|
|
}
|
|
}
|
|
return &nockchain.Signature{
|
|
Entries: entries,
|
|
}, nil
|
|
}
|
|
|
|
func ConvertName(name *nockchain.NockchainName) *nockchain.Name {
|
|
return &nockchain.Name{
|
|
First: ParseHash(name.First),
|
|
Last: ParseHash(name.Last),
|
|
}
|
|
}
|
|
func ConvertTimelockIntent(timelock *nockchain.TimelockIntent) *nockchain.TimeLockIntent {
|
|
if timelock == nil {
|
|
return nil
|
|
}
|
|
|
|
if timelock.Absolute != nil && timelock.Relative != nil {
|
|
return &nockchain.TimeLockIntent{
|
|
Value: &nockchain.TimeLockIntent_AbsoluteAndRelative{
|
|
AbsoluteAndRelative: &nockchain.TimeLockRangeAbsoluteAndRelative{
|
|
Absolute: &nockchain.TimeLockRangeAbsolute{
|
|
Min: (*nockchain.BlockHeight)(timelock.Absolute.Min),
|
|
Max: (*nockchain.BlockHeight)(timelock.Absolute.Max),
|
|
},
|
|
Relative: &nockchain.TimeLockRangeRelative{
|
|
Min: (*nockchain.BlockHeightDelta)(timelock.Relative.Min),
|
|
Max: (*nockchain.BlockHeightDelta)(timelock.Relative.Max),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
if timelock.Absolute != nil {
|
|
return &nockchain.TimeLockIntent{
|
|
Value: &nockchain.TimeLockIntent_Absolute{
|
|
Absolute: &nockchain.TimeLockRangeAbsolute{
|
|
Min: (*nockchain.BlockHeight)(timelock.Absolute.Min),
|
|
Max: (*nockchain.BlockHeight)(timelock.Absolute.Max),
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
if timelock.Relative != nil {
|
|
return &nockchain.TimeLockIntent{
|
|
Value: &nockchain.TimeLockIntent_Relative{
|
|
Relative: &nockchain.TimeLockRangeRelative{
|
|
Min: (*nockchain.BlockHeightDelta)(timelock.Relative.Min),
|
|
Max: (*nockchain.BlockHeightDelta)(timelock.Relative.Max),
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func ParseHash(hash string) *nockchain.Hash {
|
|
hashTip5 := crypto.Base58ToTip5Hash(hash)
|
|
return &nockchain.Hash{
|
|
Belt_1: &nockchain.Belt{Value: hashTip5[0]},
|
|
Belt_2: &nockchain.Belt{Value: hashTip5[1]},
|
|
Belt_3: &nockchain.Belt{Value: hashTip5[2]},
|
|
Belt_4: &nockchain.Belt{Value: hashTip5[3]},
|
|
Belt_5: &nockchain.Belt{Value: hashTip5[4]},
|
|
}
|
|
}
|
|
|
|
func ParseEightBelt(data []uint64) *nockchain.EightBelt {
|
|
return &nockchain.EightBelt{
|
|
Belt_1: &nockchain.Belt{Value: data[0]},
|
|
Belt_2: &nockchain.Belt{Value: data[1]},
|
|
Belt_3: &nockchain.Belt{Value: data[2]},
|
|
Belt_4: &nockchain.Belt{Value: data[3]},
|
|
Belt_5: &nockchain.Belt{Value: data[4]},
|
|
Belt_6: &nockchain.Belt{Value: data[5]},
|
|
Belt_7: &nockchain.Belt{Value: data[6]},
|
|
Belt_8: &nockchain.Belt{Value: data[7]},
|
|
}
|
|
}
|
|
func ParseSchnorrPubkey(key string) (*nockchain.SchnorrPubkey, error) {
|
|
pk, err := crypto.CheetaPointFromBytes(base58.Decode(key))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &nockchain.SchnorrPubkey{
|
|
Value: &nockchain.CheetahPoint{
|
|
X: &nockchain.SixBelt{
|
|
Belt_1: &nockchain.Belt{Value: pk.X[0].Value},
|
|
Belt_2: &nockchain.Belt{Value: pk.X[1].Value},
|
|
Belt_3: &nockchain.Belt{Value: pk.X[2].Value},
|
|
Belt_4: &nockchain.Belt{Value: pk.X[3].Value},
|
|
Belt_5: &nockchain.Belt{Value: pk.X[4].Value},
|
|
Belt_6: &nockchain.Belt{Value: pk.X[5].Value},
|
|
},
|
|
Y: &nockchain.SixBelt{
|
|
Belt_1: &nockchain.Belt{Value: pk.Y[0].Value},
|
|
Belt_2: &nockchain.Belt{Value: pk.Y[1].Value},
|
|
Belt_3: &nockchain.Belt{Value: pk.Y[2].Value},
|
|
Belt_4: &nockchain.Belt{Value: pk.Y[3].Value},
|
|
Belt_5: &nockchain.Belt{Value: pk.Y[4].Value},
|
|
Belt_6: &nockchain.Belt{Value: pk.Y[5].Value},
|
|
},
|
|
Inf: pk.Inf,
|
|
},
|
|
}, nil
|
|
}
|
|
func ParseTimelockIntent(timelock *nockchain.TimeLockIntent) *nockchain.TimelockIntent {
|
|
if timelock == nil {
|
|
return nil
|
|
}
|
|
switch timelock.Value.(type) {
|
|
case *nockchain.TimeLockIntent_Absolute:
|
|
return &nockchain.TimelockIntent{
|
|
Absolute: &nockchain.TimelockRange{
|
|
Min: (*nockchain.Timelock)(timelock.GetAbsolute().Min),
|
|
Max: (*nockchain.Timelock)(timelock.GetAbsolute().Max),
|
|
},
|
|
Relative: nil,
|
|
}
|
|
case *nockchain.TimeLockIntent_Relative:
|
|
return &nockchain.TimelockIntent{
|
|
Absolute: nil,
|
|
Relative: &nockchain.TimelockRange{
|
|
Min: (*nockchain.Timelock)(timelock.GetRelative().Min),
|
|
Max: (*nockchain.Timelock)(timelock.GetRelative().Max),
|
|
},
|
|
}
|
|
case *nockchain.TimeLockIntent_AbsoluteAndRelative:
|
|
return &nockchain.TimelockIntent{
|
|
Absolute: &nockchain.TimelockRange{
|
|
Min: (*nockchain.Timelock)(timelock.GetAbsoluteAndRelative().Absolute.Min),
|
|
Max: (*nockchain.Timelock)(timelock.GetAbsoluteAndRelative().Absolute.Max),
|
|
},
|
|
Relative: &nockchain.TimelockRange{
|
|
Min: (*nockchain.Timelock)(timelock.GetAbsoluteAndRelative().Relative.Min),
|
|
Max: (*nockchain.Timelock)(timelock.GetAbsoluteAndRelative().Relative.Max),
|
|
},
|
|
}
|
|
default:
|
|
return nil
|
|
}
|
|
}
|