package wallet import ( "fmt" "math/big" "github.com/btcsuite/btcd/btcutil/base58" "github.com/phamminh0811/private-grpc/crypto" "github.com/phamminh0811/private-grpc/nockchain" ) var LastName = [5]uint64{9541855607561054508, 12383849149342406623, 11220017934615522559, 678840671137489369, 8985908938884028381} func HashPubkey(pkPoint crypto.CheetahPoint) [5]uint64 { belts := []crypto.Belt{{Value: 13}} belts = append(belts, pkPoint.X[:]...) belts = append(belts, pkPoint.Y[:]...) belts = append(belts, crypto.BELT_ONE) for _, i := range crypto.MagicDyckForPoint { belts = append(belts, crypto.Belt{Value: i}) } return crypto.Tip5HashBelts(belts) } func HashSignature(signature *nockchain.NockchainSignature) ([5]uint64, error) { belts := []crypto.Belt{{Value: 16}} for _, i := range signature.Chal { belts = append(belts, crypto.Belt{Value: i}) } for _, i := range signature.Sig { belts = append(belts, crypto.Belt{Value: i}) } for _, i := range crypto.MagicDyckForT8 { belts = append(belts, crypto.Belt{Value: i}) } sigHash := crypto.Tip5HashBelts(belts) pkPoint, err := crypto.CheetaPointFromBytes(base58.Decode(signature.Pubkey)) if err != nil { return [5]uint64{}, err } pkHash := HashPubkey(pkPoint) sigHash = crypto.Tip5RehashTenCell(pkHash, sigHash) sigHash = crypto.Tip5RehashTenCell(sigHash, crypto.Tip5ZeroZero) return crypto.Tip5RehashTenCell(crypto.Tip5Zero, sigHash), nil } func HashOwner(pkPoint crypto.CheetahPoint) [5]uint64 { pkHashedBelts := HashPubkey(pkPoint) pkHashedZeroZero := crypto.Tip5RehashTenCell(pkHashedBelts, crypto.Tip5ZeroZero) return crypto.Tip5RehashTenCell(crypto.Tip5One, pkHashedZeroZero) } func NockName(ownerHash [5]uint64) ([5]uint64, [5]uint64) { firstName := first(ownerHash) return firstName, LastName } func HashName(name *nockchain.NockchainName) [5]uint64 { firstNameHash := crypto.Base58ToTip5Hash(name.First) lastNameHash := crypto.Base58ToTip5Hash(name.Last) return crypto.Tip5RehashTenCell(firstNameHash, crypto.Tip5RehashTenCell(lastNameHash, crypto.Tip5Zero)) } func HashNote(note *nockchain.NockchainNote) ([5]uint64, error) { versionHash := crypto.Tip5HashBelts([]crypto.Belt{{Value: 1}, {Value: uint64(note.Version)}}) blockHash := crypto.Tip5HashBelts([]crypto.Belt{{Value: 1}, {Value: note.BlockHeight}}) timelockHash := HashTimelockIntent(note.Timelock) hashBlockTimeLock := crypto.Tip5RehashTenCell(blockHash, timelockHash) p := crypto.Tip5RehashTenCell(versionHash, hashBlockTimeLock) nameHash := HashName(note.Name) lockHash, err := HashLock(note.Lock) if err != nil { return [5]uint64{}, err } sourceHash := HashSource(note.Source) assetHash := crypto.Tip5HashBelts([]crypto.Belt{{Value: 1}, {Value: uint64(note.Asset)}}) hashSourceAsset := crypto.Tip5RehashTenCell(sourceHash, assetHash) q := crypto.Tip5RehashTenCell(nameHash, crypto.Tip5RehashTenCell(lockHash, hashSourceAsset)) return crypto.Tip5RehashTenCell(p, q), nil } func HashTimelockIntent(timelock *nockchain.TimelockIntent) [5]uint64 { if timelock == nil { return crypto.Tip5Zero } if timelock.Absolute == nil && timelock.Relative == nil { return crypto.Tip5ZeroZero } absoluteHash := HashTimelockRange(timelock.Absolute) relativeHash := HashTimelockRange(timelock.Relative) return crypto.Tip5RehashTenCell(absoluteHash, relativeHash) } func HashTimelockRange(timelockRange *nockchain.TimelockRange) [5]uint64 { hash := crypto.Tip5Zero if timelockRange != nil { minHash := crypto.Tip5Zero if timelockRange.Min != nil { minHash = crypto.Tip5HashBelts([]crypto.Belt{{Value: 1}, {Value: timelockRange.Min.Value}}) } maxHash := crypto.Tip5Zero if timelockRange.Max != nil { maxHash = crypto.Tip5HashBelts([]crypto.Belt{{Value: 1}, {Value: timelockRange.Max.Value}}) } hash = crypto.Tip5RehashTenCell(minHash, maxHash) } return hash } func HashLock(lock *nockchain.NockchainLock) ([5]uint64, error) { keysRequiredHash := crypto.Tip5HashBelts([]crypto.Belt{{Value: 1}, {Value: lock.KeysRequired}}) finalPk := base58.Decode(lock.Pubkeys[lock.KeysRequired-1]) finalPkPoint, err := crypto.CheetaPointFromBytes(finalPk) if err != nil { return [5]uint64{}, err } finalPkHash := HashPubkey(finalPkPoint) finalHash := crypto.Tip5RehashTenCell(finalPkHash, crypto.Tip5ZeroZero) if lock.KeysRequired != 1 { for i := uint64(1); i < lock.KeysRequired; i++ { pk := base58.Decode(lock.Pubkeys[lock.KeysRequired-1-i]) pkPoint, err := crypto.CheetaPointFromBytes(pk) if err != nil { return [5]uint64{}, err } pkHash := HashPubkey(pkPoint) finalHash = crypto.Tip5RehashTenCell(pkHash, finalHash) } } return crypto.Tip5RehashTenCell(keysRequiredHash, finalHash), nil } func HashSource(source *nockchain.NockchainSource) [5]uint64 { if source == nil { return crypto.Tip5Zero } sourceHash := crypto.Base58ToTip5Hash(source.Source) return crypto.Tip5RehashTenCell(sourceHash, crypto.Tip5One) } func HashSeedWithoutSource(seed *nockchain.NockchainSeed) ([5]uint64, error) { lockHash, err := HashLock(seed.Recipient) if err != nil { return [5]uint64{}, nil } timelockIntentHash := HashTimelockIntent(seed.TimelockIntent) assetHash := crypto.Tip5HashBelts([]crypto.Belt{{Value: 1}, {Value: seed.Gift}}) parentHash := crypto.Base58ToTip5Hash(seed.ParentHash) assetHashparentHash := crypto.Tip5RehashTenCell(assetHash, parentHash) timelockHashAssetParentHash := crypto.Tip5RehashTenCell(timelockIntentHash, assetHashparentHash) seedHash := crypto.Tip5RehashTenCell(lockHash, timelockHashAssetParentHash) return seedHash, nil } func HashSeed(seed *nockchain.NockchainSeed) ([5]uint64, error) { seedHash, err := HashSeedWithoutSource(seed) if err != nil { return [5]uint64{}, nil } sourceHash := HashSource(seed.OutputSource) 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 { belts = append(belts, belt) } for _, belt := range pkPoint.Y { belts = append(belts, belt) } for _, belt := range message { belts = append(belts, crypto.Belt{Value: belt}) } resBelts := make([]crypto.Belt, len(belts)) copy(resBelts, belts) return resBelts, crypto.Tip5HashBelts(belts) } func HashSpend(spend *nockchain.NockchainSpend) ([5]uint64, error) { // TODO: handle multiple sig if len(spend.Signatures) == 0 { return [5]uint64{}, fmt.Errorf("signatures can not be empty") } sigHash, err := HashSignature(spend.Signatures[0]) if err != nil { 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) } 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) } } } feeHash := crypto.Tip5HashBelts([]crypto.Belt{{Value: 1}, {Value: spend.Fee}}) seedHashFee := crypto.Tip5RehashTenCell(finalSeedHash, feeHash) return crypto.Tip5RehashTenCell(sigHash, seedHashFee), nil } func HashMsg(spend *nockchain.NockchainSpend) ([5]uint64, error) { seedsCount := len(spend.Seeds) var finalSeedHash [5]uint64 if seedsCount == 1 { seedHash, err := HashSeed(spend.Seeds[0]) if err != nil { return [5]uint64{}, err } 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) 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) } } } feeHash := crypto.Tip5HashBelts([]crypto.Belt{{Value: 1}, {Value: spend.Fee}}) return crypto.Tip5RehashTenCell(finalSeedHash, feeHash), nil } func HashInput(input *nockchain.NockchainInput) ([5]uint64, error) { nameHash := HashName(input.Name) noteHash, err := HashNote(input.Note) if err != nil { return [5]uint64{}, err } spendHash, err := HashSpend(input.Spend) if err != nil { return [5]uint64{}, err } hashNoteSpend := crypto.Tip5RehashTenCell(noteHash, spendHash) inputHash := crypto.Tip5RehashTenCell(nameHash, hashNoteSpend) return crypto.Tip5RehashTenCell(inputHash, crypto.Tip5ZeroZero), nil } func ComputeTxId(inputs []*nockchain.NockchainInput, timelockRange *nockchain.TimelockRange, totalFees uint64) ([5]uint64, error) { // TODO: do it with multiple intputs input := inputs[0] inputHash, err := HashInput(input) if err != nil { return [5]uint64{}, err } timelockHash := HashTimelockRange(timelockRange) totalFeesHash := crypto.Tip5HashBelts([]crypto.Belt{{Value: 1}, {Value: totalFees}}) q := crypto.Tip5RehashTenCell(timelockHash, totalFeesHash) return crypto.Tip5RehashTenCell(inputHash, q), nil } func ComputeSig(m crypto.MasterKey, msg [5]uint64) ([8]uint64, [8]uint64, error) { pkPoint, err := crypto.CheetaPointFromBytes(m.PublicKey) if err != nil { return [8]uint64{}, [8]uint64{}, err } belts, nonce := HashNonce(pkPoint, msg) nonceBigInt := crypto.TruncGOrder(nonce) scalar := crypto.CheetahScaleBig(crypto.A_GEN, *nonceBigInt) scalarBelts := []crypto.Belt{} scalarBelts = append(scalarBelts, scalar.X[:]...) scalarBelts = append(scalarBelts, scalar.Y[:]...) belts = append(scalarBelts, belts...) chal := crypto.Tip5HashBelts(belts) chalBigInt := crypto.TruncGOrder(chal) skBigInt := new(big.Int).SetBytes(m.PrivateKey) sig := new(big.Int).Mul(chalBigInt, skBigInt) sig.Add(sig, nonceBigInt) sig.Mod(sig, crypto.G_ORDER) chalT8 := crypto.BigIntToT8(*chalBigInt) sigT8 := crypto.BigIntToT8(*sig) return chalT8, sigT8, nil } func first(ownerHash [5]uint64) [5]uint64 { ownerHashZero := crypto.Tip5RehashTenCell(ownerHash, crypto.Tip5Zero) ownerHashZeroOne := crypto.Tip5RehashTenCell(crypto.Tip5One, ownerHashZero) return crypto.Tip5RehashTenCell(crypto.Tip5Zero, ownerHashZeroOne) }