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)}, } }