From 552a9e9d9bb95b9bc24ef0c4bfb1aa37dacc8688 Mon Sep 17 00:00:00 2001 From: Anh Minh <1phamminh0811@gmail.com> Date: Wed, 15 Oct 2025 08:00:31 +0700 Subject: [PATCH] fix: send tx with 2 seeds --- crypto/master_key.go | 1 + wallet/nockhash.go | 194 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 171 insertions(+), 24 deletions(-) diff --git a/crypto/master_key.go b/crypto/master_key.go index 2265634..06fd139 100644 --- a/crypto/master_key.go +++ b/crypto/master_key.go @@ -17,6 +17,7 @@ var ( PublicKeyStart = []byte{234, 230, 92} 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} + 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} Tip5Zero = [5]uint64{1730770831742798981, 2676322185709933211, 8329210750824781744, 16756092452590401876, 3547445316740171466} Tip5One = [5]uint64{6727110957294540849, 15606243244732609007, 11887284596344881785, 10646863421881571398, 8146872807338919620} Tip5ZeroZero = [5]uint64{4372149332062030091, 17876920912185183887, 13348798570422431948, 8872865212694716527, 3385176510443841516} diff --git a/wallet/nockhash.go b/wallet/nockhash.go index e88ad62..e08dd81 100644 --- a/wallet/nockhash.go +++ b/wallet/nockhash.go @@ -174,6 +174,29 @@ func HashSeed(seed *nockchain.NockchainSeed) ([5]uint64, error) { return crypto.Tip5RehashTenCell(sourceHash, seedHash), nil } +func HashSeedVarLen(seed *nockchain.NockchainSeed) ([5]uint64, error) { + belts := []crypto.Belt{{Value: 0}} + belts = append(belts, crypto.Belt{Value: seed.Recipient.KeysRequired}) + for _, pk := range seed.Recipient.Pubkeys { + pkPoint, err := crypto.CheetaPointFromBytes(base58.Decode(pk)) + if err != nil { + return [5]uint64{}, err + } + belts = append(belts, pkPoint.X[:]...) + belts = append(belts, pkPoint.Y[:]...) + } + belts = append(belts, []crypto.Belt{{Value: 1}, {Value: 0}, {Value: 0}, {Value: 0}, {Value: seed.Gift}}...) + parentHash := crypto.Base58ToTip5Hash(seed.ParentHash) + for _, i := range parentHash { + belts = append(belts, crypto.Belt{Value: i}) + } + size := len(belts) + belts = append([]crypto.Belt{{Value: uint64(size)}}, belts...) + for _, i := range crypto.MagicDyckForSeed { + belts = append(belts, crypto.Belt{Value: i}) + } + return crypto.Tip5HashBelts(belts), nil +} func HashNonce(pkPoint crypto.CheetahPoint, message [5]uint64) ([]crypto.Belt, [5]uint64) { belts := []crypto.Belt{} for _, belt := range pkPoint.X { @@ -203,21 +226,83 @@ func HashSpend(spend *nockchain.NockchainSpend) ([5]uint64, error) { return [5]uint64{}, err } seedsCount := len(spend.Seeds) - finalSeedHash, err := HashSeedWithoutSource(spend.Seeds[seedsCount-1]) + var finalSeedHash [5]uint64 + if seedsCount == 1 { + seedHash, err := HashSeedWithoutSource(spend.Seeds[0]) + if err != nil { + return [5]uint64{}, err + } - if err != nil { - return [5]uint64{}, err - } - finalSeedHash = crypto.Tip5RehashTenCell(finalSeedHash, crypto.Tip5ZeroZero) + finalSeedHash = crypto.Tip5RehashTenCell(seedHash, crypto.Tip5ZeroZero) + } else { + seed1Hash, err := HashSeedWithoutSource(spend.Seeds[0]) + if err != nil { + return [5]uint64{}, err + } + seed1HashVarLen, err := HashSeedVarLen(spend.Seeds[0]) + if err != nil { + return [5]uint64{}, err + } + seed1DoubleHash := crypto.Tip5RehashTenCell(seed1HashVarLen, seed1HashVarLen) + 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 { + // seed2 + // / \ + // 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) - if seedsCount != 1 { - finalSeedHash = crypto.Tip5RehashTenCell(crypto.Tip5Zero, finalSeedHash) - for i := 1; i < seedsCount; i++ { - seedHash, err := HashSeedWithoutSource(spend.Seeds[seedsCount-1-i]) - if err != nil { - return [5]uint64{}, err } - finalSeedHash = crypto.Tip5RehashTenCell(seedHash, finalSeedHash) } } feeHash := crypto.Tip5HashBelts([]crypto.Belt{{Value: 1}, {Value: spend.Fee}}) @@ -228,22 +313,83 @@ func HashSpend(spend *nockchain.NockchainSpend) ([5]uint64, error) { func HashMsg(spend *nockchain.NockchainSpend) ([5]uint64, error) { seedsCount := len(spend.Seeds) - finalSeedHash, err := HashSeed(spend.Seeds[seedsCount-1]) + var finalSeedHash [5]uint64 + if seedsCount == 1 { + seedHash, err := HashSeed(spend.Seeds[0]) + if err != nil { + return [5]uint64{}, err + } - if err != nil { - return [5]uint64{}, err - } - finalSeedHash = crypto.Tip5RehashTenCell(finalSeedHash, crypto.Tip5ZeroZero) + finalSeedHash = crypto.Tip5RehashTenCell(seedHash, crypto.Tip5ZeroZero) + } else { + seed1HashVarLen, err := HashSeedVarLen(spend.Seeds[0]) + if err != nil { + return [5]uint64{}, err + } + seed1Hash, err := HashSeed(spend.Seeds[0]) + if err != nil { + return [5]uint64{}, err + } + seed1DoubleHash := crypto.Tip5RehashTenCell(seed1HashVarLen, seed1HashVarLen) + 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) - if seedsCount != 1 { - finalSeedHash = crypto.Tip5RehashTenCell(crypto.Tip5Zero, finalSeedHash) + 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 { + // seed2 + // / \ + // 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 i := 1; i < seedsCount; i++ { - seedHash, err := HashSeed(spend.Seeds[seedsCount-1-i]) - if err != nil { - return [5]uint64{}, err } - finalSeedHash = crypto.Tip5RehashTenCell(seedHash, finalSeedHash) } } feeHash := crypto.Tip5HashBelts([]crypto.Belt{{Value: 1}, {Value: spend.Fee}})