nockchain-grpc/wallet/nockhash.go

318 lines
10 KiB
Go

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 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)
finalSeedHash, err := HashSeedWithoutSource(spend.Seeds[seedsCount-1])
if err != nil {
return [5]uint64{}, err
}
finalSeedHash = crypto.Tip5RehashTenCell(finalSeedHash, crypto.Tip5ZeroZero)
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}})
seedHashFee := crypto.Tip5RehashTenCell(finalSeedHash, feeHash)
return crypto.Tip5RehashTenCell(sigHash, seedHashFee), nil
}
func HashMsg(spend *nockchain.NockchainSpend) ([5]uint64, error) {
seedsCount := len(spend.Seeds)
finalSeedHash, err := HashSeed(spend.Seeds[seedsCount-1])
if err != nil {
return [5]uint64{}, err
}
finalSeedHash = crypto.Tip5RehashTenCell(finalSeedHash, crypto.Tip5ZeroZero)
if seedsCount != 1 {
finalSeedHash = crypto.Tip5RehashTenCell(crypto.Tip5Zero, 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}})
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)
}