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) return crypto.Tip5RehashTenCell(sigHash, crypto.Tip5ZeroZero), nil } func HashNoteData(lock *nockchain.NockchainLock) [5]uint64 { keysRequiredHash := crypto.Tip5HashLeaf(lock.KeysRequired) // TODO: handle multisig finalHash := crypto.Base58ToTip5Hash(lock.Pubkeys[lock.KeysRequired-1]) hash := crypto.Tip5RehashTenCell(crypto.Tip5HashLeaf(finalHash[3]), crypto.Tip5HashLeaf(finalHash[4])) hash = crypto.Tip5RehashTenCell(crypto.Tip5HashLeaf(finalHash[2]), hash) hash = crypto.Tip5RehashTenCell(crypto.Tip5HashLeaf(finalHash[1]), hash) hash = crypto.Tip5RehashTenCell(crypto.Tip5HashLeaf(finalHash[0]), hash) hash = crypto.Tip5RehashTenCell(hash, crypto.Tip5ZeroZero) pkh := crypto.Tip5RehashTenCell(keysRequiredHash, hash) pkhWithPkhTag := crypto.Tip5RehashTenCell(crypto.PkhTagTip5Hash, pkh) pkhWithPkhTag = crypto.Tip5RehashTenCell(pkhWithPkhTag, crypto.Tip5Zero) pkhWithPkhTag = crypto.Tip5RehashTenCell(crypto.Tip5Zero, pkhWithPkhTag) pkhWithPkhLockTag := crypto.Tip5RehashTenCell(crypto.LockTagTip5Hash, pkhWithPkhTag) return crypto.Tip5RehashTenCell(pkhWithPkhLockTag, crypto.Tip5ZeroZero) } 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 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 HashNoteV0(note *nockchain.NockchainNoteV0) ([5]uint64, error) { versionHash := crypto.Tip5HashBelts([]crypto.Belt{{Value: 1}, {Value: uint64(note.Version)}}) blockHash := crypto.Tip5HashBelts([]crypto.Belt{{Value: 1}, {Value: note.OriginPage}}) hashBlockTimeLock := crypto.Tip5RehashTenCell(blockHash, crypto.Tip5Zero) p := crypto.Tip5RehashTenCell(versionHash, hashBlockTimeLock) nameHash := HashName(note.Name) lockHash, err := HashLockV0(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 HashLockV0(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 HashLock(lock *nockchain.NockchainLock) [5]uint64 { keysRequiredHash := crypto.Tip5HashBelts([]crypto.Belt{{Value: 1}, {Value: lock.KeysRequired}}) pkh := crypto.Base58ToTip5Hash(lock.Pubkeys[lock.KeysRequired-1]) finalHash := crypto.Tip5RehashTenCell(pkh, crypto.Tip5ZeroZero) if lock.KeysRequired != 1 { for i := uint64(1); i < lock.KeysRequired; i++ { pkh := crypto.Base58ToTip5Hash(lock.Pubkeys[lock.KeysRequired-1-i]) finalHash = crypto.Tip5RehashTenCell(pkh, finalHash) } } lockHash := crypto.Tip5RehashTenCell(keysRequiredHash, finalHash) lockHashWithTag := crypto.Tip5RehashTenCell(crypto.PkhTagTip5Hash, lockHash) return crypto.Tip5RehashTenCell(lockHashWithTag, crypto.Tip5Zero) } 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.NockchainSeedV0) [5]uint64 { lockRoot := crypto.Base58ToTip5Hash(seed.LockRoot) fmt.Println("lockRoot:", lockRoot) assetHash := crypto.Tip5HashBelts([]crypto.Belt{{Value: 1}, {Value: seed.Gift}}) parentHash := crypto.Base58ToTip5Hash(seed.ParentHash) fmt.Println("parentHash:", parentHash) assetHashparentHash := crypto.Tip5RehashTenCell(assetHash, parentHash) noteDataHash := HashNoteData(seed.NoteData) fmt.Println("noteDataHash:", noteDataHash) noteDataHashAssetParentHash := crypto.Tip5RehashTenCell(noteDataHash, assetHashparentHash) seedHash := crypto.Tip5RehashTenCell(lockRoot, noteDataHashAssetParentHash) return seedHash } func HashSeed(seed *nockchain.NockchainSeedV0) [5]uint64 { seedHash := HashSeedWithoutSource(seed) sourceHash := HashSource(seed.OutputSource) return crypto.Tip5RehashTenCell(sourceHash, seedHash) } func HashSeedVarLen(seed *nockchain.NockchainSeedV0) [5]uint64 { belts := []crypto.Belt{{Value: 0}} lockRoot := crypto.Base58ToTip5Hash(seed.LockRoot) for _, i := range lockRoot { belts = append(belts, crypto.Belt{Value: i}) } // %lock and %pkh tag belts = append(belts, []crypto.Belt{{Value: 1801678700}, {Value: 0}, {Value: 6843248}}...) belts = append(belts, crypto.Belt{Value: seed.NoteData.KeysRequired}) for _, pk := range seed.NoteData.Pubkeys { pkHash := crypto.Base58ToTip5Hash(pk) for _, i := range pkHash { belts = append(belts, crypto.Belt{Value: i}) } } belts = append(belts, []crypto.Belt{{Value: 0}, {Value: 0}, {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) } func HashNonce(pkPoint crypto.CheetahPoint, message [5]uint64, privKey []byte) ([]crypto.Belt, [5]uint64) { privKeyBigInt := new(big.Int).SetBytes(privKey) privKeyT8 := crypto.BigIntToT8(*privKeyBigInt) 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) for _, belt := range privKeyT8 { belts = append(belts, crypto.Belt{Value: belt}) } return resBelts, crypto.Tip5HashBelts(belts) } func HashSpendV0(spend *nockchain.NockchainSpendV0) ([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 } seedsTree := NewZTree( func(i interface{}) [5]uint64 { if seed, ok := i.(*nockchain.NockchainSeedV0); ok { return HashSeedVarLen(seed) } else { return [5]uint64{} } }, func(i interface{}) ([5]uint64, error) { if seed, ok := i.(*nockchain.NockchainSeedV0); ok { return HashSeedWithoutSource(seed), nil } else { return [5]uint64{}, fmt.Errorf("invalid input type") } }, ) 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}}) seedHashFee := crypto.Tip5RehashTenCell(finalSeedHash, feeHash) return crypto.Tip5RehashTenCell(sigHash, seedHashFee), nil } func HashMsg(spend *nockchain.NockchainSpendV0) ([5]uint64, error) { seedsTree := NewZTree( func(i interface{}) [5]uint64 { if seed, ok := i.(*nockchain.NockchainSeedV0); ok { return HashSeedVarLen(seed) } else { return [5]uint64{} } }, func(i interface{}) ([5]uint64, error) { if seed, ok := i.(*nockchain.NockchainSeedV0); ok { return HashSeed(seed), nil } else { return [5]uint64{}, fmt.Errorf("invalid input type") } }, ) 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}}) 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) // return crypto.Tip5RehashTenCell(nameHash, hashNoteSpend), nil // } func ComputeTxId(spends []*nockchain.NockchainNamedSpend, version uint64) ([5]uint64, error) { var spendHash [5]uint64 var err error switch version { case 0: spendTree := NewZTree( func(i interface{}) [5]uint64 { if name, ok := i.(*nockchain.NockchainName); ok { return HashNameVarLen(name) } else { return [5]uint64{} } }, func(i interface{}) ([5]uint64, error) { if spendEntry, ok := i.(*nockchain.NockchainNamedSpend); ok { nameHash := HashName(spendEntry.Name) spendV0Hash, err := HashSpendV0(spendEntry.GetLegacy()) if err != nil { return [5]uint64{}, fmt.Errorf("error hashing spend: %v", err) } zeroHashspend := crypto.Tip5RehashTenCell(crypto.Tip5Zero, spendV0Hash) return crypto.Tip5RehashTenCell(nameHash, zeroHashspend), nil } else { return [5]uint64{}, fmt.Errorf("invalid input type") } }, ) for _, spend := range spends { spendTree.Insert(spend.Name, spend) } spendHash, err = spendTree.Hash() if err != nil { return [5]uint64{}, fmt.Errorf("error hashing spends: %v", err) } fmt.Println("spendHash:", spendHash) default: return [5]uint64{}, fmt.Errorf("unsupported version %d", version) } return crypto.Tip5RehashTenCell(crypto.Tip5One, spendHash), 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, m.PrivateKey) 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) }