2025-11-07 11:16:19 +07:00

157 lines
3.7 KiB
Go

package wallet
import (
"math/bits"
"github.com/phamminh0811/private-grpc/crypto"
"github.com/phamminh0811/private-grpc/nockchain"
)
var InitialBits = []bool{true, false, false, true, true, false, true, false, false, false, false, false, false, false, true, true, true, true, false, false, false, false, false, true, true, true, false, true, true, false, true, false, true, true, false, false, false, false, true, false, true, true, true, false}
func NumBitsUint64(v uint64) int {
if v == 0 {
return 0
}
return 64 - bits.LeadingZeros64(v)
}
func Uint64ToBitsLSB0(x uint64) []bool {
bits := make([]bool, 64)
for i := 0; i < 64; i++ {
bits[i] = ((x >> i) & 1) == 1
}
return bits
}
func BitsToUint64LSB0(bits []bool, start, sz int) uint64 {
var data uint64
for i := 0; i < sz; i++ {
if bits[start+i] {
data |= 1 << i // LSB0: bit 0 = least significant
}
}
return data
}
func BoolsToBytesLSB0(bits []bool) []byte {
if len(bits) == 0 {
return nil
}
n := (len(bits) + 7) / 8 // ceil division
bytes := make([]byte, n)
for i, bit := range bits {
if bit {
byteIdx := i / 8
bitIdx := i % 8
bytes[byteIdx] |= 1 << bitIdx // LSB0: least-significant bit first
}
}
return bytes
}
func BytesToBoolsLSB0(data []byte) []bool {
bools := make([]bool, len(data)*8)
for i, b := range data {
for j := 0; j < 8; j++ {
bools[i*8+j] = ((b >> j) & 1) == 1 // LSB0 order
}
}
return bools
}
func EncodeNoteData(noteData *nockchain.NockchainLock) []byte {
bits := InitialBits
numPubkeys := noteData.KeysRequired
numPubkeysSz := NumBitsUint64(uint64(numPubkeys))
numPubkeysSzSz := NumBitsUint64(uint64(numPubkeysSz))
bits = append(bits, false)
for i := 0; i < numPubkeysSzSz; i++ {
bits = append(bits, false)
}
bits = append(bits, true)
if numPubkeysSzSz > 1 {
numPubkeysSzBits := Uint64ToBitsLSB0(uint64(numPubkeysSz))
bits = append(bits, numPubkeysSzBits[0:numPubkeysSzSz-1]...)
}
numPubkeysBits := Uint64ToBitsLSB0(uint64(numPubkeys))
bits = append(bits, numPubkeysBits[0:numPubkeysSz]...)
bits = append(bits, []bool{true, false}...)
for _, pkStr := range noteData.Pubkeys {
pkHash := crypto.Base58ToTip5Hash(pkStr)
for i, hash := range pkHash {
if i != 4 {
bits = append(bits, []bool{true, false}...)
}
hashSz := NumBitsUint64(hash)
hashSzSz := NumBitsUint64(uint64(hashSz))
bits = append(bits, false)
for i := 0; i < hashSzSz; i++ {
bits = append(bits, false)
}
bits = append(bits, true)
if hashSzSz > 1 {
hashSzBits := Uint64ToBitsLSB0(uint64(hashSz))
bits = append(bits, hashSzBits[0:hashSzSz-1]...)
}
hashBits := Uint64ToBitsLSB0(hash)
bits = append(bits, hashBits[0:hashSz]...)
}
}
bits = append(bits, []bool{true, false, false, true, false, true, false, true}...)
return BoolsToBytesLSB0(bits)
}
func DecodeNoteData(blob []byte) *nockchain.NockchainLock {
bits := BytesToBoolsLSB0(blob)
start := len(InitialBits) + 1
count := 0
for !bits[start] {
start += 1
count += 1
}
start += 1
numPksSz := uint64(1)
if count > 1 {
numPksSz = BitsToUint64LSB0(bits, start, count-1)
start += count - 1
}
numPks := BitsToUint64LSB0(bits, start, int(numPksSz))
start += int(numPksSz)
start += 2
pkHash := [5]uint64{}
for i := 0; i < 5; i++ {
if i != 4 {
start += 2
}
start += 1
count := 0
for !bits[start] {
start += 1
count += 1
}
start += 1
hashSz := uint64(count)
if count > 1 {
hashSz = BitsToUint64LSB0(bits, start, count-1) + 1<<(count-1)
start += count - 1
}
hash := BitsToUint64LSB0(bits, start, int(hashSz))
start += int(hashSz)
pkHash[i] = hash
}
return &nockchain.NockchainLock{
KeysRequired: numPks,
Pubkeys: []string{crypto.Tip5HashToBase58(pkHash)},
}
}