Compare commits
2 Commits
262f7d3bc0
...
2d9185a5ec
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2d9185a5ec | ||
| 2816e5333b |
@ -13,11 +13,12 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
DomainSeparator = []byte("Nockchain seed")
|
DomainSeparator = []byte("Nockchain seed")
|
||||||
PrivateKeyStart = []byte{4, 178, 67, 11}
|
PrivateKeyStart = []byte{1, 16, 99, 49}
|
||||||
PublicKeyStart = []byte{234, 230, 92}
|
PublicKeyStart = []byte{12, 14, 187, 9}
|
||||||
MagicDyckForPoint = []uint64{0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1}
|
MagicDyckForPoint = []uint64{0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1}
|
||||||
MagicDyckForT8 = []uint64{0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}
|
MagicDyckForT8 = []uint64{0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}
|
||||||
MagicDyckForSeed = []uint64{0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}
|
MagicDyckForSeed = []uint64{0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}
|
||||||
|
MagicDyckForName = []uint64{0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1}
|
||||||
Tip5Zero = [5]uint64{1730770831742798981, 2676322185709933211, 8329210750824781744, 16756092452590401876, 3547445316740171466}
|
Tip5Zero = [5]uint64{1730770831742798981, 2676322185709933211, 8329210750824781744, 16756092452590401876, 3547445316740171466}
|
||||||
Tip5One = [5]uint64{6727110957294540849, 15606243244732609007, 11887284596344881785, 10646863421881571398, 8146872807338919620}
|
Tip5One = [5]uint64{6727110957294540849, 15606243244732609007, 11887284596344881785, 10646863421881571398, 8146872807338919620}
|
||||||
Tip5ZeroZero = [5]uint64{4372149332062030091, 17876920912185183887, 13348798570422431948, 8872865212694716527, 3385176510443841516}
|
Tip5ZeroZero = [5]uint64{4372149332062030091, 17876920912185183887, 13348798570422431948, 8872865212694716527, 3385176510443841516}
|
||||||
@ -29,6 +30,13 @@ type MasterKey struct {
|
|||||||
ChainCode []byte
|
ChainCode []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type KeyType int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
KeyType_PRIVATE KeyType = 0
|
||||||
|
KeyType_PUBLIC KeyType = 1
|
||||||
|
)
|
||||||
|
|
||||||
func (m *MasterKey) DeriveChild(index uint64) (MasterKey, error) {
|
func (m *MasterKey) DeriveChild(index uint64) (MasterKey, error) {
|
||||||
idxBytes := make([]byte, 4)
|
idxBytes := make([]byte, 4)
|
||||||
binary.BigEndian.PutUint32(idxBytes, uint32(index))
|
binary.BigEndian.PutUint32(idxBytes, uint32(index))
|
||||||
@ -184,18 +192,29 @@ func BigIntToT8(data big.Int) [8]uint64 {
|
|||||||
res := rip(data)
|
res := rip(data)
|
||||||
return [8]uint64(res)
|
return [8]uint64(res)
|
||||||
}
|
}
|
||||||
func SerializeExtend(chainCode []byte, key []byte, version []byte) []byte {
|
func SerializeExtend(chainCode []byte, key []byte, keyType KeyType) []byte {
|
||||||
data := version
|
data := []byte{}
|
||||||
// dep: depth in chain
|
// dep: depth in chain
|
||||||
// idx: index at depth
|
// idx: index at depth
|
||||||
// pf: parent fingerprint
|
// pf: parent fingerprint
|
||||||
|
// ver: version
|
||||||
|
ver := 0
|
||||||
depth := 0
|
depth := 0
|
||||||
idx := []byte{0, 0, 0, 0}
|
idx := []byte{0, 0, 0, 0}
|
||||||
pf := []byte{0, 0, 0, 0}
|
pf := []byte{0, 0, 0, 0}
|
||||||
data = append(data, byte(depth%256))
|
var typ []byte
|
||||||
data = append(data, pf...)
|
switch keyType {
|
||||||
data = append(data, idx...)
|
case KeyType_PRIVATE:
|
||||||
data = append(data, chainCode...)
|
typ = PrivateKeyStart
|
||||||
|
case KeyType_PUBLIC:
|
||||||
|
typ = PublicKeyStart
|
||||||
|
}
|
||||||
|
data = append(data, typ...) // 4 bytes
|
||||||
|
data = append(data, byte(ver)) // 1 byte
|
||||||
|
data = append(data, byte(depth%256)) // 1 byte
|
||||||
|
data = append(data, pf...) // 4 bytes
|
||||||
|
data = append(data, idx...) // 4 bytes
|
||||||
|
data = append(data, chainCode...) // 32 bytes
|
||||||
data = append(data, key...)
|
data = append(data, key...)
|
||||||
return AddChecksum(data)
|
return AddChecksum(data)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -61,6 +61,24 @@ func HashName(name *nockchain.NockchainName) [5]uint64 {
|
|||||||
return crypto.Tip5RehashTenCell(firstNameHash, crypto.Tip5RehashTenCell(lastNameHash, crypto.Tip5Zero))
|
return crypto.Tip5RehashTenCell(firstNameHash, crypto.Tip5RehashTenCell(lastNameHash, crypto.Tip5Zero))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func HashNameVarLen(name *nockchain.NockchainName) [5]uint64 {
|
||||||
|
belts := []crypto.Belt{}
|
||||||
|
for _, i := range crypto.Base58ToTip5Hash(name.First) {
|
||||||
|
belts = append(belts, crypto.Belt{Value: i})
|
||||||
|
}
|
||||||
|
for _, i := range crypto.Base58ToTip5Hash(name.Last) {
|
||||||
|
belts = append(belts, crypto.Belt{Value: i})
|
||||||
|
}
|
||||||
|
belts = append(belts, crypto.BELT_ZERO)
|
||||||
|
size := len(belts)
|
||||||
|
belts = append([]crypto.Belt{{Value: uint64(size)}}, belts...)
|
||||||
|
for _, i := range crypto.MagicDyckForName {
|
||||||
|
belts = append(belts, crypto.Belt{Value: i})
|
||||||
|
}
|
||||||
|
res := crypto.Tip5HashBelts(belts)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
func HashNote(note *nockchain.NockchainNote) ([5]uint64, error) {
|
func HashNote(note *nockchain.NockchainNote) ([5]uint64, error) {
|
||||||
versionHash := crypto.Tip5HashBelts([]crypto.Belt{{Value: 1}, {Value: uint64(note.Version)}})
|
versionHash := crypto.Tip5HashBelts([]crypto.Belt{{Value: 1}, {Value: uint64(note.Version)}})
|
||||||
blockHash := crypto.Tip5HashBelts([]crypto.Belt{{Value: 1}, {Value: note.BlockHeight}})
|
blockHash := crypto.Tip5HashBelts([]crypto.Belt{{Value: 1}, {Value: note.BlockHeight}})
|
||||||
@ -225,86 +243,36 @@ func HashSpend(spend *nockchain.NockchainSpend) ([5]uint64, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return [5]uint64{}, err
|
return [5]uint64{}, err
|
||||||
}
|
}
|
||||||
seedsCount := len(spend.Seeds)
|
|
||||||
var finalSeedHash [5]uint64
|
|
||||||
if seedsCount == 1 {
|
|
||||||
seedHash, err := HashSeedWithoutSource(spend.Seeds[0])
|
|
||||||
if err != nil {
|
|
||||||
return [5]uint64{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
finalSeedHash = crypto.Tip5RehashTenCell(seedHash, crypto.Tip5ZeroZero)
|
seedsTree := NewZTree(
|
||||||
|
func(i interface{}) [5]uint64 {
|
||||||
|
if seed, ok := i.(*nockchain.NockchainSeed); ok {
|
||||||
|
seedHash, err := HashSeedVarLen(seed)
|
||||||
|
if err != nil {
|
||||||
|
return [5]uint64{}
|
||||||
|
}
|
||||||
|
return seedHash
|
||||||
} else {
|
} else {
|
||||||
seed1Hash, err := HashSeedWithoutSource(spend.Seeds[0])
|
return [5]uint64{}
|
||||||
if err != nil {
|
|
||||||
return [5]uint64{}, err
|
|
||||||
}
|
}
|
||||||
seed1HashVarLen, err := HashSeedVarLen(spend.Seeds[0])
|
},
|
||||||
if err != nil {
|
func(i interface{}) ([5]uint64, error) {
|
||||||
return [5]uint64{}, err
|
if seed, ok := i.(*nockchain.NockchainSeed); ok {
|
||||||
}
|
hash, err := HashSeedWithoutSource(seed)
|
||||||
seed1DoubleHash := crypto.Tip5RehashTenCell(seed1HashVarLen, seed1HashVarLen)
|
return hash, err
|
||||||
seed2Hash, err := HashSeedWithoutSource(spend.Seeds[1])
|
|
||||||
if err != nil {
|
|
||||||
return [5]uint64{}, err
|
|
||||||
}
|
|
||||||
seed2HashVarLen, err := HashSeedVarLen(spend.Seeds[1])
|
|
||||||
if err != nil {
|
|
||||||
return [5]uint64{}, err
|
|
||||||
}
|
|
||||||
seed2DoubleHash := crypto.Tip5RehashTenCell(seed2HashVarLen, seed2HashVarLen)
|
|
||||||
|
|
||||||
seedHash1BigInt := new(big.Int).SetBytes(base58.Decode(crypto.Tip5HashToBase58(seed1HashVarLen)))
|
|
||||||
seed1DoubleHashBigInt := new(big.Int).SetBytes(base58.Decode(crypto.Tip5HashToBase58(seed1DoubleHash)))
|
|
||||||
seedHash2BigInt := new(big.Int).SetBytes(base58.Decode(crypto.Tip5HashToBase58(seed2HashVarLen)))
|
|
||||||
seed2DoubleHashBigInt := new(big.Int).SetBytes(base58.Decode(crypto.Tip5HashToBase58(seed2DoubleHash)))
|
|
||||||
if seedHash1BigInt.Cmp(seedHash2BigInt) == -1 {
|
|
||||||
// seed 1 < seed 2
|
|
||||||
|
|
||||||
if seed1DoubleHashBigInt.Cmp(seed2DoubleHashBigInt) == -1 {
|
|
||||||
// seed1
|
|
||||||
// / \
|
|
||||||
// ~ seed2
|
|
||||||
// / \
|
|
||||||
// ~ ~
|
|
||||||
finalSeedHash = crypto.Tip5RehashTenCell(seed2Hash, crypto.Tip5ZeroZero)
|
|
||||||
finalSeedHash = crypto.Tip5RehashTenCell(crypto.Tip5Zero, finalSeedHash)
|
|
||||||
finalSeedHash = crypto.Tip5RehashTenCell(seed1Hash, finalSeedHash)
|
|
||||||
} else {
|
} else {
|
||||||
// seed2
|
return [5]uint64{}, fmt.Errorf("invalid input type")
|
||||||
// / \
|
}
|
||||||
// seed1 ~
|
},
|
||||||
// / \
|
)
|
||||||
// ~ ~
|
for _, seed := range spend.Seeds {
|
||||||
finalSeedHash = crypto.Tip5RehashTenCell(seed1Hash, crypto.Tip5ZeroZero)
|
seedsTree.Insert(seed, seed)
|
||||||
finalSeedHash = crypto.Tip5RehashTenCell(finalSeedHash, crypto.Tip5Zero)
|
}
|
||||||
finalSeedHash = crypto.Tip5RehashTenCell(seed2Hash, finalSeedHash)
|
finalSeedHash, err := seedsTree.Hash()
|
||||||
|
if err != nil {
|
||||||
|
return [5]uint64{}, err
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// seed 1 > seed 2
|
|
||||||
|
|
||||||
if seed1DoubleHashBigInt.Cmp(seed2DoubleHashBigInt) == -1 {
|
|
||||||
// seed1
|
|
||||||
// / \
|
|
||||||
// seed2 ~
|
|
||||||
// / \
|
|
||||||
// ~ ~
|
|
||||||
finalSeedHash = crypto.Tip5RehashTenCell(seed2Hash, crypto.Tip5ZeroZero)
|
|
||||||
finalSeedHash = crypto.Tip5RehashTenCell(finalSeedHash, crypto.Tip5Zero)
|
|
||||||
finalSeedHash = crypto.Tip5RehashTenCell(seed1Hash, finalSeedHash)
|
|
||||||
} else {
|
|
||||||
// seed2
|
|
||||||
// / \
|
|
||||||
// ~ seed1
|
|
||||||
// / \
|
|
||||||
// ~ ~
|
|
||||||
finalSeedHash = crypto.Tip5RehashTenCell(seed1Hash, crypto.Tip5ZeroZero)
|
|
||||||
finalSeedHash = crypto.Tip5RehashTenCell(crypto.Tip5Zero, finalSeedHash)
|
|
||||||
finalSeedHash = crypto.Tip5RehashTenCell(seed2Hash, finalSeedHash)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
feeHash := crypto.Tip5HashBelts([]crypto.Belt{{Value: 1}, {Value: spend.Fee}})
|
feeHash := crypto.Tip5HashBelts([]crypto.Belt{{Value: 1}, {Value: spend.Fee}})
|
||||||
seedHashFee := crypto.Tip5RehashTenCell(finalSeedHash, feeHash)
|
seedHashFee := crypto.Tip5RehashTenCell(finalSeedHash, feeHash)
|
||||||
return crypto.Tip5RehashTenCell(sigHash, seedHashFee), nil
|
return crypto.Tip5RehashTenCell(sigHash, seedHashFee), nil
|
||||||
@ -312,84 +280,33 @@ func HashSpend(spend *nockchain.NockchainSpend) ([5]uint64, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func HashMsg(spend *nockchain.NockchainSpend) ([5]uint64, error) {
|
func HashMsg(spend *nockchain.NockchainSpend) ([5]uint64, error) {
|
||||||
seedsCount := len(spend.Seeds)
|
seedsTree := NewZTree(
|
||||||
var finalSeedHash [5]uint64
|
func(i interface{}) [5]uint64 {
|
||||||
if seedsCount == 1 {
|
if seed, ok := i.(*nockchain.NockchainSeed); ok {
|
||||||
seedHash, err := HashSeed(spend.Seeds[0])
|
seedHash, err := HashSeedVarLen(seed)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return [5]uint64{}, err
|
return [5]uint64{}
|
||||||
}
|
}
|
||||||
|
return seedHash
|
||||||
finalSeedHash = crypto.Tip5RehashTenCell(seedHash, crypto.Tip5ZeroZero)
|
|
||||||
} else {
|
} else {
|
||||||
seed1HashVarLen, err := HashSeedVarLen(spend.Seeds[0])
|
return [5]uint64{}
|
||||||
if err != nil {
|
|
||||||
return [5]uint64{}, err
|
|
||||||
}
|
}
|
||||||
seed1Hash, err := HashSeed(spend.Seeds[0])
|
},
|
||||||
if err != nil {
|
func(i interface{}) ([5]uint64, error) {
|
||||||
return [5]uint64{}, err
|
if seed, ok := i.(*nockchain.NockchainSeed); ok {
|
||||||
}
|
hash, err := HashSeed(seed)
|
||||||
seed1DoubleHash := crypto.Tip5RehashTenCell(seed1HashVarLen, seed1HashVarLen)
|
return hash, err
|
||||||
seed2HashVarLen, err := HashSeedVarLen(spend.Seeds[1])
|
|
||||||
if err != nil {
|
|
||||||
return [5]uint64{}, err
|
|
||||||
}
|
|
||||||
seed2Hash, err := HashSeed(spend.Seeds[1])
|
|
||||||
if err != nil {
|
|
||||||
return [5]uint64{}, err
|
|
||||||
}
|
|
||||||
seed2DoubleHash := crypto.Tip5RehashTenCell(seed2HashVarLen, seed2HashVarLen)
|
|
||||||
|
|
||||||
seedHash1BigInt := new(big.Int).SetBytes(base58.Decode(crypto.Tip5HashToBase58(seed1HashVarLen)))
|
|
||||||
seed1DoubleHashBigInt := new(big.Int).SetBytes(base58.Decode(crypto.Tip5HashToBase58(seed1DoubleHash)))
|
|
||||||
seedHash2BigInt := new(big.Int).SetBytes(base58.Decode(crypto.Tip5HashToBase58(seed2HashVarLen)))
|
|
||||||
seed2DoubleHashBigInt := new(big.Int).SetBytes(base58.Decode(crypto.Tip5HashToBase58(seed2DoubleHash)))
|
|
||||||
if seedHash1BigInt.Cmp(seedHash2BigInt) == -1 {
|
|
||||||
// seed 1 < seed 2
|
|
||||||
|
|
||||||
if seed1DoubleHashBigInt.Cmp(seed2DoubleHashBigInt) == -1 {
|
|
||||||
// seed1
|
|
||||||
// / \
|
|
||||||
// ~ seed2
|
|
||||||
// / \
|
|
||||||
// ~ ~
|
|
||||||
finalSeedHash = crypto.Tip5RehashTenCell(seed2Hash, crypto.Tip5ZeroZero)
|
|
||||||
finalSeedHash = crypto.Tip5RehashTenCell(crypto.Tip5Zero, finalSeedHash)
|
|
||||||
finalSeedHash = crypto.Tip5RehashTenCell(seed1Hash, finalSeedHash)
|
|
||||||
} else {
|
} else {
|
||||||
// seed2
|
return [5]uint64{}, fmt.Errorf("invalid input type")
|
||||||
// / \
|
|
||||||
// seed1 ~
|
|
||||||
// / \
|
|
||||||
// ~ ~
|
|
||||||
finalSeedHash = crypto.Tip5RehashTenCell(seed1Hash, crypto.Tip5ZeroZero)
|
|
||||||
finalSeedHash = crypto.Tip5RehashTenCell(finalSeedHash, crypto.Tip5Zero)
|
|
||||||
finalSeedHash = crypto.Tip5RehashTenCell(seed2Hash, finalSeedHash)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// seed 1 > seed 2
|
|
||||||
|
|
||||||
if seed1DoubleHashBigInt.Cmp(seed2DoubleHashBigInt) == -1 {
|
|
||||||
// seed1
|
|
||||||
// / \
|
|
||||||
// seed2 ~
|
|
||||||
// / \
|
|
||||||
// ~ ~
|
|
||||||
finalSeedHash = crypto.Tip5RehashTenCell(seed2Hash, crypto.Tip5ZeroZero)
|
|
||||||
finalSeedHash = crypto.Tip5RehashTenCell(finalSeedHash, crypto.Tip5Zero)
|
|
||||||
finalSeedHash = crypto.Tip5RehashTenCell(seed1Hash, finalSeedHash)
|
|
||||||
} else {
|
|
||||||
// seed2
|
|
||||||
// / \
|
|
||||||
// ~ seed1
|
|
||||||
// / \
|
|
||||||
// ~ ~
|
|
||||||
finalSeedHash = crypto.Tip5RehashTenCell(seed1Hash, crypto.Tip5ZeroZero)
|
|
||||||
finalSeedHash = crypto.Tip5RehashTenCell(crypto.Tip5Zero, finalSeedHash)
|
|
||||||
finalSeedHash = crypto.Tip5RehashTenCell(seed2Hash, finalSeedHash)
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
for _, seed := range spend.Seeds {
|
||||||
|
seedsTree.Insert(seed, seed)
|
||||||
}
|
}
|
||||||
|
finalSeedHash, err := seedsTree.Hash()
|
||||||
|
if err != nil {
|
||||||
|
return [5]uint64{}, err
|
||||||
}
|
}
|
||||||
feeHash := crypto.Tip5HashBelts([]crypto.Belt{{Value: 1}, {Value: spend.Fee}})
|
feeHash := crypto.Tip5HashBelts([]crypto.Belt{{Value: 1}, {Value: spend.Fee}})
|
||||||
return crypto.Tip5RehashTenCell(finalSeedHash, feeHash), nil
|
return crypto.Tip5RehashTenCell(finalSeedHash, feeHash), nil
|
||||||
@ -417,7 +334,7 @@ func ComputeTxId(inputs []*nockchain.NockchainInput, timelockRange *nockchain.Ti
|
|||||||
inputTree := NewZTree(
|
inputTree := NewZTree(
|
||||||
func(i interface{}) [5]uint64 {
|
func(i interface{}) [5]uint64 {
|
||||||
if name, ok := i.(*nockchain.NockchainName); ok {
|
if name, ok := i.(*nockchain.NockchainName); ok {
|
||||||
return HashName(name)
|
return HashNameVarLen(name)
|
||||||
} else {
|
} else {
|
||||||
return [5]uint64{}
|
return [5]uint64{}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -54,8 +54,8 @@ func (h *GprcHandler) Keygen(ctx context.Context, req *nockchain.KeygenRequest)
|
|||||||
PrivateKey: base58.Encode(masterKey.PrivateKey),
|
PrivateKey: base58.Encode(masterKey.PrivateKey),
|
||||||
PublicKey: base58.Encode(masterKey.PublicKey),
|
PublicKey: base58.Encode(masterKey.PublicKey),
|
||||||
ChainCode: base58.Encode(masterKey.ChainCode),
|
ChainCode: base58.Encode(masterKey.ChainCode),
|
||||||
ImportPrivateKey: base58.Encode(crypto.SerializeExtend(masterKey.ChainCode, privBytes, crypto.PrivateKeyStart)),
|
ImportPrivateKey: base58.Encode(crypto.SerializeExtend(masterKey.ChainCode, privBytes, crypto.KeyType_PRIVATE)),
|
||||||
ImportPublicKey: base58.Encode(crypto.SerializeExtend(masterKey.ChainCode, masterKey.PublicKey, crypto.PublicKeyStart)),
|
ImportPublicKey: base58.Encode(crypto.SerializeExtend(masterKey.ChainCode, masterKey.PublicKey, crypto.KeyType_PUBLIC)),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,21 +68,21 @@ func (h *GprcHandler) ImportKeys(ctx context.Context, req *nockchain.ImportKeysR
|
|||||||
data := base58.Decode(req.Key)
|
data := base58.Decode(req.Key)
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(req.Key, "zprv"):
|
case strings.HasPrefix(req.Key, "zprv"):
|
||||||
if len(data) != 82 {
|
if len(data) != 83 {
|
||||||
return nil, fmt.Errorf("invalid extended private key length: %d (expected 82)", len(data))
|
return nil, fmt.Errorf("invalid extended private key length: %d (expected 83)", len(data))
|
||||||
}
|
}
|
||||||
if data[45] != 0x00 {
|
if data[46] != 0x00 {
|
||||||
return nil, fmt.Errorf("invalid private key prefix at byte 45: 0x%02x (expected 0x00)", data[45])
|
return nil, fmt.Errorf("invalid private key prefix at byte 46: 0x%02x (expected 0x00)", data[46])
|
||||||
}
|
}
|
||||||
hash := sha256.Sum256(data[:78])
|
hash := sha256.Sum256(data[:79])
|
||||||
hash = sha256.Sum256(hash[:])
|
hash = sha256.Sum256(hash[:])
|
||||||
if !slices.Equal(hash[:4], data[78:]) {
|
if !slices.Equal(hash[:4], data[79:]) {
|
||||||
return nil, fmt.Errorf("invalid checksum")
|
return nil, fmt.Errorf("invalid checksum")
|
||||||
}
|
}
|
||||||
chainCode := make([]byte, 32)
|
chainCode := make([]byte, 32)
|
||||||
copy(chainCode, data[13:45])
|
copy(chainCode, data[14:46])
|
||||||
privateKey := make([]byte, 32)
|
privateKey := make([]byte, 32)
|
||||||
copy(privateKey, data[46:78])
|
copy(privateKey, data[47:79])
|
||||||
masterKey, err := crypto.MasterKeyFromPrivKey(chainCode, privateKey)
|
masterKey, err := crypto.MasterKeyFromPrivKey(chainCode, privateKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -93,31 +93,31 @@ func (h *GprcHandler) ImportKeys(ctx context.Context, req *nockchain.ImportKeysR
|
|||||||
PrivateKey: base58.Encode(masterKey.PrivateKey),
|
PrivateKey: base58.Encode(masterKey.PrivateKey),
|
||||||
PublicKey: base58.Encode(masterKey.PublicKey),
|
PublicKey: base58.Encode(masterKey.PublicKey),
|
||||||
ChainCode: base58.Encode(masterKey.ChainCode),
|
ChainCode: base58.Encode(masterKey.ChainCode),
|
||||||
ImportPrivateKey: base58.Encode(crypto.SerializeExtend(masterKey.ChainCode, privBytes, crypto.PrivateKeyStart)),
|
ImportPrivateKey: base58.Encode(crypto.SerializeExtend(masterKey.ChainCode, privBytes, crypto.KeyType_PRIVATE)),
|
||||||
ImportPublicKey: base58.Encode(crypto.SerializeExtend(masterKey.ChainCode, masterKey.PublicKey, crypto.PublicKeyStart)),
|
ImportPublicKey: base58.Encode(crypto.SerializeExtend(masterKey.ChainCode, masterKey.PublicKey, crypto.KeyType_PUBLIC)),
|
||||||
}, nil
|
}, nil
|
||||||
case strings.HasPrefix(req.Key, "zpub"):
|
case strings.HasPrefix(req.Key, "zpub"):
|
||||||
if len(data) != 145 {
|
if len(data) != 147 {
|
||||||
return nil, fmt.Errorf("invalid extended public key length: %d (expected 145)", len(data))
|
return nil, fmt.Errorf("invalid extended public key length: %d (expected 147)", len(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
hash := sha256.Sum256(data[:141])
|
hash := sha256.Sum256(data[:143])
|
||||||
hash = sha256.Sum256(hash[:])
|
hash = sha256.Sum256(hash[:])
|
||||||
if !slices.Equal(hash[:4], data[141:]) {
|
if !slices.Equal(hash[:4], data[143:]) {
|
||||||
return nil, fmt.Errorf("invalid checksum")
|
return nil, fmt.Errorf("invalid checksum")
|
||||||
}
|
}
|
||||||
|
|
||||||
chainCode := make([]byte, 32)
|
chainCode := make([]byte, 32)
|
||||||
copy(chainCode, data[12:44])
|
copy(chainCode, data[14:46])
|
||||||
publicKey := make([]byte, 97)
|
publicKey := make([]byte, 97)
|
||||||
copy(publicKey, data[44:141])
|
copy(publicKey, data[46:143])
|
||||||
return &nockchain.ImportKeysResponse{
|
return &nockchain.ImportKeysResponse{
|
||||||
Seed: "",
|
Seed: "",
|
||||||
PrivateKey: "",
|
PrivateKey: "",
|
||||||
PublicKey: base58.Encode(publicKey),
|
PublicKey: base58.Encode(publicKey),
|
||||||
ChainCode: base58.Encode(chainCode),
|
ChainCode: base58.Encode(chainCode),
|
||||||
ImportPrivateKey: "",
|
ImportPrivateKey: "",
|
||||||
ImportPublicKey: base58.Encode(crypto.SerializeExtend(chainCode, publicKey, crypto.PublicKeyStart)),
|
ImportPublicKey: base58.Encode(crypto.SerializeExtend(chainCode, publicKey, crypto.KeyType_PUBLIC)),
|
||||||
}, nil
|
}, nil
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("invalid extended key")
|
return nil, fmt.Errorf("invalid extended key")
|
||||||
@ -145,8 +145,8 @@ func (h *GprcHandler) ImportKeys(ctx context.Context, req *nockchain.ImportKeysR
|
|||||||
PrivateKey: base58.Encode(masterKey.PrivateKey),
|
PrivateKey: base58.Encode(masterKey.PrivateKey),
|
||||||
PublicKey: base58.Encode(masterKey.PublicKey),
|
PublicKey: base58.Encode(masterKey.PublicKey),
|
||||||
ChainCode: base58.Encode(masterKey.ChainCode),
|
ChainCode: base58.Encode(masterKey.ChainCode),
|
||||||
ImportPrivateKey: base58.Encode(crypto.SerializeExtend(masterKey.ChainCode, privBytes, crypto.PrivateKeyStart)),
|
ImportPrivateKey: base58.Encode(crypto.SerializeExtend(masterKey.ChainCode, privBytes, crypto.KeyType_PRIVATE)),
|
||||||
ImportPublicKey: base58.Encode(crypto.SerializeExtend(masterKey.ChainCode, masterKey.PublicKey, crypto.PublicKeyStart)),
|
ImportPublicKey: base58.Encode(crypto.SerializeExtend(masterKey.ChainCode, masterKey.PublicKey, crypto.KeyType_PUBLIC)),
|
||||||
}, nil
|
}, nil
|
||||||
case nockchain.ImportType_SEEDPHRASE:
|
case nockchain.ImportType_SEEDPHRASE:
|
||||||
masterKey, err := crypto.MasterKeyFromSeed(req.Key)
|
masterKey, err := crypto.MasterKeyFromSeed(req.Key)
|
||||||
@ -159,8 +159,8 @@ func (h *GprcHandler) ImportKeys(ctx context.Context, req *nockchain.ImportKeysR
|
|||||||
PrivateKey: base58.Encode(masterKey.PrivateKey),
|
PrivateKey: base58.Encode(masterKey.PrivateKey),
|
||||||
PublicKey: base58.Encode(masterKey.PublicKey),
|
PublicKey: base58.Encode(masterKey.PublicKey),
|
||||||
ChainCode: base58.Encode(masterKey.ChainCode),
|
ChainCode: base58.Encode(masterKey.ChainCode),
|
||||||
ImportPrivateKey: base58.Encode(crypto.SerializeExtend(masterKey.ChainCode, privBytes, crypto.PrivateKeyStart)),
|
ImportPrivateKey: base58.Encode(crypto.SerializeExtend(masterKey.ChainCode, privBytes, crypto.KeyType_PRIVATE)),
|
||||||
ImportPublicKey: base58.Encode(crypto.SerializeExtend(masterKey.ChainCode, masterKey.PublicKey, crypto.PublicKeyStart)),
|
ImportPublicKey: base58.Encode(crypto.SerializeExtend(masterKey.ChainCode, masterKey.PublicKey, crypto.KeyType_PUBLIC)),
|
||||||
}, nil
|
}, nil
|
||||||
case nockchain.ImportType_WATCH_ONLY:
|
case nockchain.ImportType_WATCH_ONLY:
|
||||||
pubKey := base58.Decode(req.Key)
|
pubKey := base58.Decode(req.Key)
|
||||||
@ -188,21 +188,21 @@ func (h *GprcHandler) DeriveChild(ctx context.Context, req *nockchain.DeriveChil
|
|||||||
}
|
}
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(req.ImportedKey, "zprv"):
|
case strings.HasPrefix(req.ImportedKey, "zprv"):
|
||||||
if len(data) != 82 {
|
if len(data) != 83 {
|
||||||
return nil, fmt.Errorf("invalid extended private key length: %d (expected 82)", len(data))
|
return nil, fmt.Errorf("invalid extended private key length: %d (expected 83)", len(data))
|
||||||
}
|
}
|
||||||
if data[45] != 0x00 {
|
if data[46] != 0x00 {
|
||||||
return nil, fmt.Errorf("invalid private key prefix at byte 45: 0x%02x (expected 0x00)", data[45])
|
return nil, fmt.Errorf("invalid private key prefix at byte 46: 0x%02x (expected 0x00)", data[46])
|
||||||
}
|
}
|
||||||
hash := sha256.Sum256(data[:78])
|
hash := sha256.Sum256(data[:79])
|
||||||
hash = sha256.Sum256(hash[:])
|
hash = sha256.Sum256(hash[:])
|
||||||
if !slices.Equal(hash[:4], data[78:]) {
|
if !slices.Equal(hash[:4], data[79:]) {
|
||||||
return nil, fmt.Errorf("invalid checksum")
|
return nil, fmt.Errorf("invalid checksum")
|
||||||
}
|
}
|
||||||
chainCode := make([]byte, 32)
|
chainCode := make([]byte, 32)
|
||||||
copy(chainCode, data[13:45])
|
copy(chainCode, data[14:46])
|
||||||
privateKey := make([]byte, 32)
|
privateKey := make([]byte, 32)
|
||||||
copy(privateKey, data[46:78])
|
copy(privateKey, data[47:79])
|
||||||
masterKey, err := crypto.MasterKeyFromPrivKey(chainCode, privateKey)
|
masterKey, err := crypto.MasterKeyFromPrivKey(chainCode, privateKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -219,20 +219,20 @@ func (h *GprcHandler) DeriveChild(ctx context.Context, req *nockchain.DeriveChil
|
|||||||
}, nil
|
}, nil
|
||||||
|
|
||||||
case strings.HasPrefix(req.ImportedKey, "zpub"):
|
case strings.HasPrefix(req.ImportedKey, "zpub"):
|
||||||
if len(data) != 145 {
|
if len(data) != 147 {
|
||||||
return nil, fmt.Errorf("invalid extended public key length: %d (expected 145)", len(data))
|
return nil, fmt.Errorf("invalid extended public key length: %d (expected 145)", len(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
hash := sha256.Sum256(data[:141])
|
hash := sha256.Sum256(data[:143])
|
||||||
hash = sha256.Sum256(hash[:])
|
hash = sha256.Sum256(hash[:])
|
||||||
if !slices.Equal(hash[:4], data[141:]) {
|
if !slices.Equal(hash[:4], data[143:]) {
|
||||||
return nil, fmt.Errorf("invalid checksum")
|
return nil, fmt.Errorf("invalid checksum")
|
||||||
}
|
}
|
||||||
|
|
||||||
chainCode := make([]byte, 32)
|
chainCode := make([]byte, 32)
|
||||||
copy(chainCode, data[13:45])
|
copy(chainCode, data[14:46])
|
||||||
publicKey := make([]byte, 97)
|
publicKey := make([]byte, 97)
|
||||||
copy(publicKey, data[45:141])
|
copy(publicKey, data[46:143])
|
||||||
|
|
||||||
masterKey := crypto.MasterKey{
|
masterKey := crypto.MasterKey{
|
||||||
PublicKey: publicKey,
|
PublicKey: publicKey,
|
||||||
@ -287,7 +287,7 @@ func (h *GprcHandler) CreateTx(ctx context.Context, req *nockchain.CreateTxReque
|
|||||||
nameTree := NewZTree(
|
nameTree := NewZTree(
|
||||||
func(i interface{}) [5]uint64 {
|
func(i interface{}) [5]uint64 {
|
||||||
if name, ok := i.(*nockchain.NockchainName); ok {
|
if name, ok := i.(*nockchain.NockchainName); ok {
|
||||||
return HashName(name)
|
return HashNameVarLen(name)
|
||||||
} else {
|
} else {
|
||||||
return [5]uint64{}
|
return [5]uint64{}
|
||||||
}
|
}
|
||||||
@ -297,16 +297,18 @@ func (h *GprcHandler) CreateTx(ctx context.Context, req *nockchain.CreateTxReque
|
|||||||
for _, name := range nnames {
|
for _, name := range nnames {
|
||||||
nameTree.Insert(name, nil)
|
nameTree.Insert(name, nil)
|
||||||
}
|
}
|
||||||
nameLeftMost := nameTree.KeyLeftMost().(*nockchain.NockchainName)
|
nameList := nameTree.Tap()
|
||||||
idxLeftMost := -1
|
nameKeys := []string{}
|
||||||
|
for _, nameKey := range nameList {
|
||||||
|
name := nameKey.Key.(*nockchain.NockchainName)
|
||||||
|
nameKeys = append(nameKeys, name.First+" "+name.Last)
|
||||||
|
}
|
||||||
|
indices := make([]int, len(nnames))
|
||||||
for i, name := range nnames {
|
for i, name := range nnames {
|
||||||
if name.First == nameLeftMost.First && name.Last == nameLeftMost.Last {
|
if idx := slices.Index(nameKeys, name.First+" "+name.Last); idx != -1 {
|
||||||
idxLeftMost = i
|
indices[idx] = i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if idxLeftMost == -1 {
|
|
||||||
return nil, fmt.Errorf("unable to find left most node")
|
|
||||||
}
|
|
||||||
|
|
||||||
recipents := []*nockchain.NockchainLock{}
|
recipents := []*nockchain.NockchainLock{}
|
||||||
if strings.Contains(req.Recipients, "[") {
|
if strings.Contains(req.Recipients, "[") {
|
||||||
@ -411,40 +413,40 @@ func (h *GprcHandler) CreateTx(ctx context.Context, req *nockchain.CreateTxReque
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inputs := []*nockchain.NockchainInput{}
|
inputs := make([]*nockchain.NockchainInput, len(names))
|
||||||
for i := 0; i < len(names); i++ {
|
isSpent := false
|
||||||
if notes[i] == nil {
|
for i := 0; i < len(nameKeys); i++ {
|
||||||
return nil, fmt.Errorf("notes scanned is missing at name: %s", names[i])
|
idx := indices[i]
|
||||||
|
if notes[idx] == nil {
|
||||||
|
return nil, fmt.Errorf("notes scanned is missing at name: %s", names[idx])
|
||||||
}
|
}
|
||||||
|
|
||||||
if notes[i].Asset < gifts[i]+req.Fee {
|
if notes[idx].Asset < gifts[idx] {
|
||||||
return nil, fmt.Errorf("note not enough balance")
|
return nil, fmt.Errorf("note %s not enough balance", names[idx])
|
||||||
}
|
}
|
||||||
|
|
||||||
parentHash, err := HashNote(notes[i])
|
parentHash, err := HashNote(notes[idx])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
seeds := []*nockchain.NockchainSeed{
|
seeds := []*nockchain.NockchainSeed{
|
||||||
{
|
{
|
||||||
OutputSource: nil,
|
OutputSource: nil,
|
||||||
Recipient: recipents[i],
|
Recipient: recipents[idx],
|
||||||
TimelockIntent: req.TimelockIntent,
|
TimelockIntent: req.TimelockIntent,
|
||||||
Gift: gifts[i],
|
Gift: gifts[idx],
|
||||||
ParentHash: crypto.Tip5HashToBase58(parentHash),
|
ParentHash: crypto.Tip5HashToBase58(parentHash),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if notes[i].Asset < gifts[i] {
|
|
||||||
return nil, fmt.Errorf("insufficient funds for notes %s", names[i])
|
assetLeft := notes[idx].Asset - gifts[idx]
|
||||||
}
|
fee := uint64(0)
|
||||||
assetLeft := notes[i].Asset - gifts[i]
|
if !isSpent && assetLeft >= req.Fee {
|
||||||
if i == idxLeftMost {
|
isSpent = true
|
||||||
if assetLeft > req.Fee {
|
fee = req.Fee
|
||||||
assetLeft -= req.Fee
|
assetLeft -= req.Fee
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("insufficient funds for notes %s", names[i])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if assetLeft != 0 {
|
if assetLeft != 0 {
|
||||||
seeds = append(seeds, &nockchain.NockchainSeed{
|
seeds = append(seeds, &nockchain.NockchainSeed{
|
||||||
OutputSource: nil,
|
OutputSource: nil,
|
||||||
@ -457,19 +459,10 @@ func (h *GprcHandler) CreateTx(ctx context.Context, req *nockchain.CreateTxReque
|
|||||||
ParentHash: crypto.Tip5HashToBase58(parentHash),
|
ParentHash: crypto.Tip5HashToBase58(parentHash),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
var spend nockchain.NockchainSpend
|
spend := nockchain.NockchainSpend{
|
||||||
if i == idxLeftMost {
|
|
||||||
spend = nockchain.NockchainSpend{
|
|
||||||
Signatures: nil,
|
Signatures: nil,
|
||||||
Seeds: seeds,
|
Seeds: seeds,
|
||||||
Fee: req.Fee,
|
Fee: fee,
|
||||||
}
|
|
||||||
} else {
|
|
||||||
spend = nockchain.NockchainSpend{
|
|
||||||
Signatures: nil,
|
|
||||||
Seeds: seeds,
|
|
||||||
Fee: 0,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
msg, err := HashMsg(&spend)
|
msg, err := HashMsg(&spend)
|
||||||
@ -492,13 +485,16 @@ func (h *GprcHandler) CreateTx(ctx context.Context, req *nockchain.CreateTxReque
|
|||||||
}
|
}
|
||||||
|
|
||||||
input := nockchain.NockchainInput{
|
input := nockchain.NockchainInput{
|
||||||
Name: nnames[i],
|
Name: nnames[idx],
|
||||||
Note: notes[i],
|
Note: notes[idx],
|
||||||
Spend: &spend,
|
Spend: &spend,
|
||||||
}
|
}
|
||||||
inputs = append(inputs, &input)
|
inputs[idx] = &input
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !isSpent {
|
||||||
|
return nil, fmt.Errorf("insufficient funds")
|
||||||
|
}
|
||||||
var timelockRange *nockchain.TimelockRange
|
var timelockRange *nockchain.TimelockRange
|
||||||
if req.TimelockIntent == nil {
|
if req.TimelockIntent == nil {
|
||||||
timelockRange = &nockchain.TimelockRange{
|
timelockRange = &nockchain.TimelockRange{
|
||||||
|
|||||||
@ -17,13 +17,13 @@ import (
|
|||||||
|
|
||||||
// The entropy, salt and result is taken from "nockchain-wallet keygen" command
|
// The entropy, salt and result is taken from "nockchain-wallet keygen" command
|
||||||
func TestKeyGen(t *testing.T) {
|
func TestKeyGen(t *testing.T) {
|
||||||
entropyBigInt, isOk := new(big.Int).SetString("29615235796517918707367078072007441124337225858809749976291970867443501879006", 10)
|
entropyBigInt, isOk := new(big.Int).SetString("37133536588676344913489312523941366110857274548479981512263368615793750653450", 10)
|
||||||
assert.True(t, isOk)
|
assert.True(t, isOk)
|
||||||
|
|
||||||
entropy := entropyBigInt.Bytes()
|
entropy := entropyBigInt.Bytes()
|
||||||
assert.Len(t, entropy, 32)
|
assert.Len(t, entropy, 32)
|
||||||
|
|
||||||
saltBigInt, isOk := new(big.Int).SetString("212311808188922973323281316240858086116", 10)
|
saltBigInt, isOk := new(big.Int).SetString("251632902249061493058993135304695174381", 10)
|
||||||
assert.True(t, isOk)
|
assert.True(t, isOk)
|
||||||
|
|
||||||
salt := saltBigInt.Bytes()
|
salt := saltBigInt.Bytes()
|
||||||
@ -33,38 +33,38 @@ func TestKeyGen(t *testing.T) {
|
|||||||
slices.Reverse(argonBytes)
|
slices.Reverse(argonBytes)
|
||||||
mnemonic, err := bip39.NewMnemonic(argonBytes)
|
mnemonic, err := bip39.NewMnemonic(argonBytes)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, mnemonic, "brass vacuum stairs hurt brisk govern describe enforce fly exact rescue capable belt flavor lottery sauce easy frame orange legal injury border obey novel")
|
assert.Equal(t, mnemonic, "include lounge salad chicken trumpet embrace grace mercy pulp submit alter weapon plastic welcome funny female palm satoshi open file knock sun fade match")
|
||||||
|
|
||||||
masterKey, err := crypto.MasterKeyFromSeed(mnemonic)
|
masterKey, err := crypto.MasterKeyFromSeed(mnemonic)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t,
|
assert.Equal(t,
|
||||||
base58.Encode(masterKey.PublicKey),
|
base58.Encode(masterKey.PublicKey),
|
||||||
"39DL6YA1kSRCKMjzpFEtC8rmnxVuseUrP2LnViwY7YEhZYZkX2HmnAZ63Uwy1DwuXstmF1VeJDucg719xw49j9CKL3bsKq3A6SZN918CowcgQroHsgohj7dYgpGRWk41s42F",
|
"34VqjU7ojQXWiFZz7kvXe1xfxhbdimmqqUAgp21XGESLqJSXxHkqrcquWirFcCPPj1pmSL4pRc8GndZoiiK8ijeYjgcJ3QR7fb2s4b2WdJhDao4Dx7gw3NRSt4RjXawqUQw6",
|
||||||
)
|
)
|
||||||
assert.Equal(t,
|
assert.Equal(t,
|
||||||
base58.Encode(masterKey.PrivateKey),
|
base58.Encode(masterKey.PrivateKey),
|
||||||
"4SyUrsbGKPRknzvGakWmFbYefzHzb1r4LUmJpQD8WPcR",
|
"3B8Q5ZTHH63h9DT6WSwNZhea5zvtueuKpxk3qwZJEjsg",
|
||||||
)
|
)
|
||||||
assert.Equal(t,
|
assert.Equal(t,
|
||||||
base58.Encode(masterKey.ChainCode),
|
base58.Encode(masterKey.ChainCode),
|
||||||
"58SARPmADHvUcpq7XfBoCgwzy5QC8Kb3JrezpHqA85x2",
|
"2ztGPxS8xYzMXoAHf3HMbMuyh4siew8X4Kz4KuTXvqX8",
|
||||||
)
|
)
|
||||||
|
|
||||||
// assert import priv/pubkey
|
// assert import priv/pubkey
|
||||||
privBytes := append([]byte{0x00}, masterKey.PrivateKey...)
|
privBytes := append([]byte{0x00}, masterKey.PrivateKey...)
|
||||||
importPrivKey := crypto.SerializeExtend(masterKey.ChainCode, privBytes, crypto.PrivateKeyStart)
|
importPrivKey := crypto.SerializeExtend(masterKey.ChainCode, privBytes, crypto.KeyType_PRIVATE)
|
||||||
assert.Len(t, importPrivKey, 82)
|
assert.Len(t, importPrivKey, 83)
|
||||||
importPubKey := crypto.SerializeExtend(masterKey.ChainCode, masterKey.PublicKey, crypto.PublicKeyStart)
|
importPubKey := crypto.SerializeExtend(masterKey.ChainCode, masterKey.PublicKey, crypto.KeyType_PUBLIC)
|
||||||
assert.Len(t, importPubKey, 145)
|
assert.Len(t, importPubKey, 147)
|
||||||
|
|
||||||
assert.Equal(t,
|
assert.Equal(t,
|
||||||
base58.Encode(importPrivKey),
|
base58.Encode(importPrivKey),
|
||||||
"zprv2CyrSHEkzQzu4HCtJRFiP4t2rVMauZwLfJDFrNbqS8Pz3nsmXy5bAUx2HYUykaMuU4MiQTHsDcKYjLCjrPfpceNT9XBHgx1pUjKzBrF6Wdo",
|
"zprvLpf3WSvYWmHRd3jj5oR8UXr7bi88pGnfJXj1dM9RnwJwu1MLo6fZzcKauqpxL2W7dk2fmjYKAUzavbyaGpnvqY5QndcrUzsBrPQAHXNnhiXx",
|
||||||
)
|
)
|
||||||
assert.Equal(t,
|
assert.Equal(t,
|
||||||
base58.Encode(importPubKey),
|
base58.Encode(importPubKey),
|
||||||
"zpubUQwNTNE3hsCkMpBnD37W5QirkyVryokAVPLnPin1c6M13RRsq3yEJbwp5ies6qXF6DvJq5Woxw6ygT53PSVrmrsQgtHhbfMEixKNFm7qb4mELhpyoovpFEV1YPHFZx4xQGYBNF6qvXU6AHNh4TLrUdkYAdXKS2J5rPiSVPrXKGo8fLG6ZBCGBjJfPcwDb2VEJC",
|
"zpub2jgndknkQprVYB4X4mqREyn7ZTUE5zp9qkSugdpiqhC5NSeNBceafoz6jGSLEpzJhaLryvY8MF6TokwZN627UXhsg5zd2U12woEL82UtZaLHRL8PZi8YiQnE41BiNJwkfpWjzAbq8mwbAHV3nXUEFgJW2BKCz4GmfoMCkCUDhJppYh5KFsMUkN41DzsfFZHuFrzo",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,32 +76,32 @@ func TestImportKey(t *testing.T) {
|
|||||||
errStr string
|
errStr string
|
||||||
}
|
}
|
||||||
|
|
||||||
correctImportPrivKey := base58.Decode("zprv2CyrSHEkzQzu4HCtJRFiP4t2rVMauZwLfJDFrNbqS8Pz3nsmXy5bAUx2HYUykaMuU4MiQTHsDcKYjLCjrPfpceNT9XBHgx1pUjKzBrF6Wdo")
|
correctImportPrivKey := base58.Decode("zprvLpf3WSvYWmHRd3jj5oR8UXr7bi88pGnfJXj1dM9RnwJwu1MLo6fZzcKauqpxL2W7dk2fmjYKAUzavbyaGpnvqY5QndcrUzsBrPQAHXNnhiXx")
|
||||||
invalidImportPrivKeyPrefix := make([]byte, 82)
|
invalidImportPrivKeyPrefix := make([]byte, 83)
|
||||||
copy(invalidImportPrivKeyPrefix[:], correctImportPrivKey)
|
copy(invalidImportPrivKeyPrefix[:], correctImportPrivKey)
|
||||||
invalidImportPrivKeyPrefix[45] = 0x01
|
invalidImportPrivKeyPrefix[46] = 0x01
|
||||||
|
|
||||||
invalidImportPrivKeyChecksum := make([]byte, 82)
|
invalidImportPrivKeyChecksum := make([]byte, 83)
|
||||||
copy(invalidImportPrivKeyChecksum[:], correctImportPrivKey)
|
copy(invalidImportPrivKeyChecksum[:], correctImportPrivKey)
|
||||||
copy(invalidImportPrivKeyChecksum[78:], []byte{1, 2, 3, 4})
|
copy(invalidImportPrivKeyChecksum[79:], []byte{1, 2, 3, 4})
|
||||||
|
|
||||||
correctImportPubkey := base58.Decode("zpubUQwNTNE3hsCkMpBnD37W5QirkyVryokAVPLnPin1c6M13RRsq3yEJbwp5ies6qXF6DvJq5Woxw6ygT53PSVrmrsQgtHhbfMEixKNFm7qb4mELhpyoovpFEV1YPHFZx4xQGYBNF6qvXU6AHNh4TLrUdkYAdXKS2J5rPiSVPrXKGo8fLG6ZBCGBjJfPcwDb2VEJC")
|
correctImportPubkey := base58.Decode("zpub2jgndknkQprVYB4X4mqREyn7ZTUE5zp9qkSugdpiqhC5NSeNBceafoz6jGSLEpzJhaLryvY8MF6TokwZN627UXhsg5zd2U12woEL82UtZaLHRL8PZi8YiQnE41BiNJwkfpWjzAbq8mwbAHV3nXUEFgJW2BKCz4GmfoMCkCUDhJppYh5KFsMUkN41DzsfFZHuFrzo")
|
||||||
invalidImportPubkeyChecksum := make([]byte, 145)
|
invalidImportPubkeyChecksum := make([]byte, 147)
|
||||||
copy(invalidImportPubkeyChecksum[:], correctImportPubkey)
|
copy(invalidImportPubkeyChecksum[:], correctImportPubkey)
|
||||||
copy(invalidImportPubkeyChecksum[141:], []byte{1, 2, 3, 4})
|
copy(invalidImportPubkeyChecksum[143:], []byte{1, 2, 3, 4})
|
||||||
|
|
||||||
response := &nockchain.ImportKeysResponse{
|
response := &nockchain.ImportKeysResponse{
|
||||||
PublicKey: "39DL6YA1kSRCKMjzpFEtC8rmnxVuseUrP2LnViwY7YEhZYZkX2HmnAZ63Uwy1DwuXstmF1VeJDucg719xw49j9CKL3bsKq3A6SZN918CowcgQroHsgohj7dYgpGRWk41s42F",
|
PublicKey: "34VqjU7ojQXWiFZz7kvXe1xfxhbdimmqqUAgp21XGESLqJSXxHkqrcquWirFcCPPj1pmSL4pRc8GndZoiiK8ijeYjgcJ3QR7fb2s4b2WdJhDao4Dx7gw3NRSt4RjXawqUQw6",
|
||||||
PrivateKey: "4SyUrsbGKPRknzvGakWmFbYefzHzb1r4LUmJpQD8WPcR",
|
PrivateKey: "3B8Q5ZTHH63h9DT6WSwNZhea5zvtueuKpxk3qwZJEjsg",
|
||||||
ChainCode: "58SARPmADHvUcpq7XfBoCgwzy5QC8Kb3JrezpHqA85x2",
|
ChainCode: "2ztGPxS8xYzMXoAHf3HMbMuyh4siew8X4Kz4KuTXvqX8",
|
||||||
ImportPrivateKey: base58.Encode(correctImportPrivKey),
|
ImportPrivateKey: base58.Encode(correctImportPrivKey),
|
||||||
ImportPublicKey: base58.Encode(correctImportPubkey),
|
ImportPublicKey: base58.Encode(correctImportPubkey),
|
||||||
}
|
}
|
||||||
|
|
||||||
responseReadOnly := &nockchain.ImportKeysResponse{
|
responseReadOnly := &nockchain.ImportKeysResponse{
|
||||||
PublicKey: "39DL6YA1kSRCKMjzpFEtC8rmnxVuseUrP2LnViwY7YEhZYZkX2HmnAZ63Uwy1DwuXstmF1VeJDucg719xw49j9CKL3bsKq3A6SZN918CowcgQroHsgohj7dYgpGRWk41s42F",
|
PublicKey: "34VqjU7ojQXWiFZz7kvXe1xfxhbdimmqqUAgp21XGESLqJSXxHkqrcquWirFcCPPj1pmSL4pRc8GndZoiiK8ijeYjgcJ3QR7fb2s4b2WdJhDao4Dx7gw3NRSt4RjXawqUQw6",
|
||||||
PrivateKey: "",
|
PrivateKey: "",
|
||||||
ChainCode: "58SARPmADHvUcpq7XfBoCgwzy5QC8Kb3JrezpHqA85x2",
|
ChainCode: "2ztGPxS8xYzMXoAHf3HMbMuyh4siew8X4Kz4KuTXvqX8",
|
||||||
ImportPrivateKey: "",
|
ImportPrivateKey: "",
|
||||||
ImportPublicKey: base58.Encode(correctImportPubkey),
|
ImportPublicKey: base58.Encode(correctImportPubkey),
|
||||||
}
|
}
|
||||||
@ -142,7 +142,7 @@ func TestImportKey(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expectResp: nil,
|
expectResp: nil,
|
||||||
isErr: true,
|
isErr: true,
|
||||||
errStr: "invalid private key prefix at byte 45",
|
errStr: "invalid private key prefix at byte 46",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
req: &nockchain.ImportKeysRequest{
|
req: &nockchain.ImportKeysRequest{
|
||||||
@ -195,7 +195,7 @@ func TestImportKey(t *testing.T) {
|
|||||||
// case missing chaincode when import master privkey
|
// case missing chaincode when import master privkey
|
||||||
{
|
{
|
||||||
req: &nockchain.ImportKeysRequest{
|
req: &nockchain.ImportKeysRequest{
|
||||||
Key: "4SyUrsbGKPRknzvGakWmFbYefzHzb1r4LUmJpQD8WPcR",
|
Key: "3B8Q5ZTHH63h9DT6WSwNZhea5zvtueuKpxk3qwZJEjsg",
|
||||||
ImportType: nockchain.ImportType_MASTER_PRIVKEY,
|
ImportType: nockchain.ImportType_MASTER_PRIVKEY,
|
||||||
},
|
},
|
||||||
expectResp: nil,
|
expectResp: nil,
|
||||||
@ -205,7 +205,7 @@ func TestImportKey(t *testing.T) {
|
|||||||
// case invalid length
|
// case invalid length
|
||||||
{
|
{
|
||||||
req: &nockchain.ImportKeysRequest{
|
req: &nockchain.ImportKeysRequest{
|
||||||
Key: "abcdxyz,4SyUrsbGKPRknzvGakWmFbYefzHzb1r4LUmJpQD8WPcR",
|
Key: "abcdxyz,3B8Q5ZTHH63h9DT6WSwNZhea5zvtueuKpxk3qwZJEjsg",
|
||||||
ImportType: nockchain.ImportType_MASTER_PRIVKEY,
|
ImportType: nockchain.ImportType_MASTER_PRIVKEY,
|
||||||
},
|
},
|
||||||
expectResp: nil,
|
expectResp: nil,
|
||||||
@ -214,7 +214,7 @@ func TestImportKey(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
req: &nockchain.ImportKeysRequest{
|
req: &nockchain.ImportKeysRequest{
|
||||||
Key: "58SARPmADHvUcpq7XfBoCgwzy5QC8Kb3JrezpHqA85x2,abcdxyz",
|
Key: "2ztGPxS8xYzMXoAHf3HMbMuyh4siew8X4Kz4KuTXvqX8,abcdxyz",
|
||||||
ImportType: nockchain.ImportType_MASTER_PRIVKEY,
|
ImportType: nockchain.ImportType_MASTER_PRIVKEY,
|
||||||
},
|
},
|
||||||
expectResp: nil,
|
expectResp: nil,
|
||||||
@ -224,7 +224,7 @@ func TestImportKey(t *testing.T) {
|
|||||||
// case success import master privkey
|
// case success import master privkey
|
||||||
{
|
{
|
||||||
req: &nockchain.ImportKeysRequest{
|
req: &nockchain.ImportKeysRequest{
|
||||||
Key: "58SARPmADHvUcpq7XfBoCgwzy5QC8Kb3JrezpHqA85x2,4SyUrsbGKPRknzvGakWmFbYefzHzb1r4LUmJpQD8WPcR",
|
Key: "2ztGPxS8xYzMXoAHf3HMbMuyh4siew8X4Kz4KuTXvqX8,3B8Q5ZTHH63h9DT6WSwNZhea5zvtueuKpxk3qwZJEjsg",
|
||||||
ImportType: nockchain.ImportType_MASTER_PRIVKEY,
|
ImportType: nockchain.ImportType_MASTER_PRIVKEY,
|
||||||
},
|
},
|
||||||
expectResp: response,
|
expectResp: response,
|
||||||
@ -234,7 +234,7 @@ func TestImportKey(t *testing.T) {
|
|||||||
// case success import seed
|
// case success import seed
|
||||||
{
|
{
|
||||||
req: &nockchain.ImportKeysRequest{
|
req: &nockchain.ImportKeysRequest{
|
||||||
Key: "brass vacuum stairs hurt brisk govern describe enforce fly exact rescue capable belt flavor lottery sauce easy frame orange legal injury border obey novel",
|
Key: "include lounge salad chicken trumpet embrace grace mercy pulp submit alter weapon plastic welcome funny female palm satoshi open file knock sun fade match",
|
||||||
ImportType: nockchain.ImportType_SEEDPHRASE,
|
ImportType: nockchain.ImportType_SEEDPHRASE,
|
||||||
},
|
},
|
||||||
expectResp: response,
|
expectResp: response,
|
||||||
@ -244,11 +244,11 @@ func TestImportKey(t *testing.T) {
|
|||||||
// case sucess import pubkey
|
// case sucess import pubkey
|
||||||
{
|
{
|
||||||
req: &nockchain.ImportKeysRequest{
|
req: &nockchain.ImportKeysRequest{
|
||||||
Key: "39DL6YA1kSRCKMjzpFEtC8rmnxVuseUrP2LnViwY7YEhZYZkX2HmnAZ63Uwy1DwuXstmF1VeJDucg719xw49j9CKL3bsKq3A6SZN918CowcgQroHsgohj7dYgpGRWk41s42F",
|
Key: "34VqjU7ojQXWiFZz7kvXe1xfxhbdimmqqUAgp21XGESLqJSXxHkqrcquWirFcCPPj1pmSL4pRc8GndZoiiK8ijeYjgcJ3QR7fb2s4b2WdJhDao4Dx7gw3NRSt4RjXawqUQw6",
|
||||||
ImportType: nockchain.ImportType_WATCH_ONLY,
|
ImportType: nockchain.ImportType_WATCH_ONLY,
|
||||||
},
|
},
|
||||||
expectResp: &nockchain.ImportKeysResponse{
|
expectResp: &nockchain.ImportKeysResponse{
|
||||||
PublicKey: "39DL6YA1kSRCKMjzpFEtC8rmnxVuseUrP2LnViwY7YEhZYZkX2HmnAZ63Uwy1DwuXstmF1VeJDucg719xw49j9CKL3bsKq3A6SZN918CowcgQroHsgohj7dYgpGRWk41s42F",
|
PublicKey: "34VqjU7ojQXWiFZz7kvXe1xfxhbdimmqqUAgp21XGESLqJSXxHkqrcquWirFcCPPj1pmSL4pRc8GndZoiiK8ijeYjgcJ3QR7fb2s4b2WdJhDao4Dx7gw3NRSt4RjXawqUQw6",
|
||||||
},
|
},
|
||||||
isErr: false,
|
isErr: false,
|
||||||
errStr: "",
|
errStr: "",
|
||||||
@ -472,21 +472,5 @@ func TestCreateTx(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// the result is taken from create-tx scripts
|
// the result is taken from create-tx scripts
|
||||||
//assert.Equal(t, res.RawTx.TxId, "8gjTa6H1MrKXPWgNJCF9fsYE7PUfqhYzxetUoTftz7zyHCv24ZYHDM3")
|
assert.Equal(t, res.RawTx.TxId, "A8vSeRde61B4sZccSnNPEnkQgTe15EssoFwyhQXbkhtk4UNm5hyGSid")
|
||||||
|
|
||||||
_, err = nc.WalletSendTransaction(res.RawTx)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
txAccepted, err := nc.TxAccepted(res.RawTx.TxId)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("tx pass: ", res.RawTx.TxId)
|
|
||||||
if !txAccepted.GetAccepted() {
|
|
||||||
panic("tx not accepted")
|
|
||||||
}
|
|
||||||
// expected: A8vSeRde61B4sZccSnNPEnkQgTe15EssoFwyhQXbkhtk4UNm5hyGSid
|
|
||||||
}
|
}
|
||||||
|
|||||||
165
wallet/ztree.go
165
wallet/ztree.go
@ -1,8 +1,10 @@
|
|||||||
package wallet
|
package wallet
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math/big"
|
||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/btcutil/base58"
|
||||||
"github.com/phamminh0811/private-grpc/crypto"
|
"github.com/phamminh0811/private-grpc/crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -20,6 +22,11 @@ type ZTree struct {
|
|||||||
HashValueFunc func(interface{}) ([5]uint64, error)
|
HashValueFunc func(interface{}) ([5]uint64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ZPair struct {
|
||||||
|
Key interface{}
|
||||||
|
Value interface{}
|
||||||
|
}
|
||||||
|
|
||||||
func NewZTree(hashKeyFunc func(interface{}) [5]uint64, hashValueFunc func(interface{}) ([5]uint64, error)) *ZTree {
|
func NewZTree(hashKeyFunc func(interface{}) [5]uint64, hashValueFunc func(interface{}) ([5]uint64, error)) *ZTree {
|
||||||
return &ZTree{
|
return &ZTree{
|
||||||
Root: nil,
|
Root: nil,
|
||||||
@ -36,13 +43,13 @@ func (z *ZTree) Hash() ([5]uint64, error) {
|
|||||||
return z.Root.HashNode(z.HashValueFunc)
|
return z.Root.HashNode(z.HashValueFunc)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (z *ZTree) KeyLeftMost() interface{} {
|
func (t *ZTree) Tap() []ZPair {
|
||||||
node := z.Root
|
tap := []ZPair{}
|
||||||
for node.Left != nil {
|
TapNode(t.Root, &tap)
|
||||||
node = node.Left
|
slices.Reverse(tap)
|
||||||
}
|
return tap
|
||||||
return node.Key
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (node *ZNode) InsertNode(hashFunc func(interface{}) [5]uint64, key, value interface{}) *ZNode {
|
func (node *ZNode) InsertNode(hashFunc func(interface{}) [5]uint64, key, value interface{}) *ZNode {
|
||||||
if node == nil {
|
if node == nil {
|
||||||
node = &ZNode{
|
node = &ZNode{
|
||||||
@ -53,42 +60,18 @@ func (node *ZNode) InsertNode(hashFunc func(interface{}) [5]uint64, key, value i
|
|||||||
}
|
}
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
keyHash := hashFunc(key)
|
keyHash := hashFunc(key)
|
||||||
keyDoubleHash := crypto.Tip5RehashTenCell(keyHash, keyHash)
|
keyDoubleHash := crypto.Tip5RehashTenCell(keyHash, keyHash)
|
||||||
nodeKeyHash := hashFunc(node.Key)
|
nodeKeyHash := hashFunc(node.Key)
|
||||||
nodeKeyDoubleHash := crypto.Tip5RehashTenCell(nodeKeyHash, nodeKeyHash)
|
nodeKeyDoubleHash := crypto.Tip5RehashTenCell(nodeKeyHash, nodeKeyHash)
|
||||||
|
|
||||||
if slices.Compare(keyHash[:], nodeKeyHash[:]) == -1 {
|
keyHashBigInt := new(big.Int).SetBytes(base58.Decode(crypto.Tip5HashToBase58(keyHash)))
|
||||||
|
nodeKeyHashBigInt := new(big.Int).SetBytes(base58.Decode(crypto.Tip5HashToBase58(nodeKeyHash)))
|
||||||
|
keyDoubleHashBigInt := new(big.Int).SetBytes(base58.Decode(crypto.Tip5HashToBase58(keyDoubleHash)))
|
||||||
|
nodeKeyDoubleHashBigInt := new(big.Int).SetBytes(base58.Decode(crypto.Tip5HashToBase58(nodeKeyDoubleHash)))
|
||||||
|
if keyHashBigInt.Cmp(nodeKeyHashBigInt) == 1 {
|
||||||
// key < node key
|
// key < node key
|
||||||
if slices.Compare(keyDoubleHash[:], nodeKeyDoubleHash[:]) == -1 {
|
if keyDoubleHashBigInt.Cmp(nodeKeyDoubleHashBigInt) == 1 {
|
||||||
// reinsert in left
|
|
||||||
node.Left = node.Left.InsertNode(hashFunc, key, value)
|
|
||||||
} else {
|
|
||||||
// new key
|
|
||||||
// / \
|
|
||||||
// ~ old key
|
|
||||||
// / \
|
|
||||||
// ... ...
|
|
||||||
nodeKey := node.Key
|
|
||||||
nodeValue := node.Value
|
|
||||||
leftNode := node.Left
|
|
||||||
rightNode := node.Right
|
|
||||||
node = &ZNode{
|
|
||||||
Key: key,
|
|
||||||
Value: value,
|
|
||||||
Right: nil,
|
|
||||||
Left: &ZNode{
|
|
||||||
Key: nodeKey,
|
|
||||||
Value: nodeValue,
|
|
||||||
Left: leftNode,
|
|
||||||
Right: rightNode,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// key > node key
|
|
||||||
if slices.Compare(keyDoubleHash[:], nodeKeyDoubleHash[:]) == -1 {
|
|
||||||
// reinsert in right
|
// reinsert in right
|
||||||
node.Right = node.Right.InsertNode(hashFunc, key, value)
|
node.Right = node.Right.InsertNode(hashFunc, key, value)
|
||||||
} else {
|
} else {
|
||||||
@ -101,6 +84,92 @@ func (node *ZNode) InsertNode(hashFunc func(interface{}) [5]uint64, key, value i
|
|||||||
nodeValue := node.Value
|
nodeValue := node.Value
|
||||||
leftNode := node.Left
|
leftNode := node.Left
|
||||||
rightNode := node.Right
|
rightNode := node.Right
|
||||||
|
if rightNode == nil {
|
||||||
|
node = &ZNode{
|
||||||
|
Key: key,
|
||||||
|
Value: value,
|
||||||
|
Right: nil,
|
||||||
|
Left: &ZNode{
|
||||||
|
Key: nodeKey,
|
||||||
|
Value: nodeValue,
|
||||||
|
Left: leftNode,
|
||||||
|
Right: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rightNodeKeyHash := hashFunc(rightNode.Key)
|
||||||
|
rightNodeKeyHashBigInt := new(big.Int).SetBytes(base58.Decode(crypto.Tip5HashToBase58(rightNodeKeyHash)))
|
||||||
|
if rightNodeKeyHashBigInt.Cmp(keyHashBigInt) == -1 {
|
||||||
|
node = &ZNode{
|
||||||
|
Key: key,
|
||||||
|
Value: value,
|
||||||
|
Right: nil,
|
||||||
|
Left: &ZNode{
|
||||||
|
Key: nodeKey,
|
||||||
|
Value: nodeValue,
|
||||||
|
Left: leftNode,
|
||||||
|
Right: rightNode,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
node = &ZNode{
|
||||||
|
Key: key,
|
||||||
|
Value: value,
|
||||||
|
Right: rightNode,
|
||||||
|
Left: &ZNode{
|
||||||
|
Key: nodeKey,
|
||||||
|
Value: nodeValue,
|
||||||
|
Left: leftNode,
|
||||||
|
Right: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// key > node key
|
||||||
|
if keyDoubleHashBigInt.Cmp(nodeKeyDoubleHashBigInt) == 1 {
|
||||||
|
// reinsert in left
|
||||||
|
node.Left = node.Left.InsertNode(hashFunc, key, value)
|
||||||
|
} else {
|
||||||
|
// new key
|
||||||
|
// / \
|
||||||
|
// ~ old key
|
||||||
|
// / \
|
||||||
|
// ... ...
|
||||||
|
nodeKey := node.Key
|
||||||
|
nodeValue := node.Value
|
||||||
|
leftNode := node.Left
|
||||||
|
rightNode := node.Right
|
||||||
|
if leftNode == nil {
|
||||||
|
node = &ZNode{
|
||||||
|
Key: key,
|
||||||
|
Value: value,
|
||||||
|
Right: &ZNode{
|
||||||
|
Key: nodeKey,
|
||||||
|
Value: nodeValue,
|
||||||
|
Left: nil,
|
||||||
|
Right: rightNode,
|
||||||
|
},
|
||||||
|
Left: nil,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
leftNodeKeyHash := hashFunc(leftNode.Key)
|
||||||
|
leftNodeKeyHashBigInt := new(big.Int).SetBytes(base58.Decode(crypto.Tip5HashToBase58(leftNodeKeyHash)))
|
||||||
|
if leftNodeKeyHashBigInt.Cmp(keyHashBigInt) == -1 {
|
||||||
|
node = &ZNode{
|
||||||
|
Key: key,
|
||||||
|
Value: value,
|
||||||
|
Right: &ZNode{
|
||||||
|
Key: nodeKey,
|
||||||
|
Value: nodeValue,
|
||||||
|
Left: nil,
|
||||||
|
Right: rightNode,
|
||||||
|
},
|
||||||
|
Left: leftNode,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
node = &ZNode{
|
node = &ZNode{
|
||||||
Key: key,
|
Key: key,
|
||||||
Value: value,
|
Value: value,
|
||||||
@ -114,6 +183,8 @@ func (node *ZNode) InsertNode(hashFunc func(interface{}) [5]uint64, key, value i
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,3 +209,25 @@ func (node *ZNode) HashNode(hashFunc func(interface{}) ([5]uint64, error)) ([5]u
|
|||||||
}
|
}
|
||||||
return crypto.Tip5RehashTenCell(valHash, hashLeftRight), nil
|
return crypto.Tip5RehashTenCell(valHash, hashLeftRight), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TapNode(node *ZNode, acc *[]ZPair) {
|
||||||
|
if node == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
stored := false
|
||||||
|
if node.Right != nil {
|
||||||
|
TapNode(node.Right, acc)
|
||||||
|
} else {
|
||||||
|
*acc = append(*acc, ZPair{Key: node.Key, Value: node.Value})
|
||||||
|
stored = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if node.Left != nil {
|
||||||
|
TapNode(node.Left, acc)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !stored {
|
||||||
|
*acc = append(*acc, ZPair{Key: node.Key, Value: node.Value})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user