-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
MimbleWimble: started implementing MW tx creation
Add NBitcoin.Secp256k1 library because it has Schnorr signature. As the library requires .netstandard 2.1, make NLitecoin target it instead of 2.0.
- Loading branch information
1 parent
5f5e144
commit 9b278b1
Showing
4 changed files
with
400 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
module NLitecoin.MimbleWimble.Pedersen | ||
|
||
open Org.BouncyCastle.Crypto.Digests | ||
open Org.BouncyCastle.Crypto.Parameters | ||
open Org.BouncyCastle.Asn1.X9 | ||
open Org.BouncyCastle.Math | ||
open NBitcoin | ||
|
||
|
||
let curve = ECNamedCurveTable.GetByName("secp256k1") | ||
let domainParams = new ECDomainParameters(curve.Curve, curve.G, curve.N, curve.H, curve.GetSeed()) | ||
|
||
let generatorG = curve.G | ||
let generatorH = | ||
curve.Curve.CreatePoint | ||
(BigInteger | ||
[| 0x50uy; 0x92uy; 0x9buy; 0x74uy; 0xc1uy; 0xa0uy; 0x49uy; 0x54uy; 0xb7uy; 0x8buy; 0x4buy; 0x60uy; 0x35uy; 0xe9uy; 0x7auy; 0x5euy; | ||
0x07uy; 0x8auy; 0x5auy; 0x0fuy; 0x28uy; 0xecuy; 0x96uy; 0xd5uy; 0x47uy; 0xbfuy; 0xeeuy; 0x9auy; 0xceuy; 0x80uy; 0x3auy; 0xc0uy; |], | ||
BigInteger | ||
[| 0x31uy; 0xd3uy; 0xc6uy; 0x86uy; 0x39uy; 0x73uy; 0x92uy; 0x6euy; 0x04uy; 0x9euy; 0x63uy; 0x7cuy; 0xb1uy; 0xb5uy; 0xf4uy; 0x0auy; | ||
0x36uy; 0xdauy; 0xc2uy; 0x8auy; 0xf1uy; 0x76uy; 0x69uy; 0x68uy; 0xc3uy; 0x0cuy; 0x23uy; 0x13uy; 0xf3uy; 0xa3uy; 0x89uy; 0x04uy; |]) | ||
|
||
let generatorJPub = | ||
curve.Curve.CreatePoint | ||
(BigInteger | ||
[| | ||
0x5fuy; 0x15uy; 0x21uy; 0x36uy; 0x93uy; 0x93uy; 0x01uy; 0x2auy; 0x8duy; 0x8buy; 0x39uy; 0x7euy; 0x9buy; 0xf4uy; 0x54uy; 0x29uy; | ||
0x2fuy; 0x5auy; 0x1buy; 0x3duy; 0x38uy; 0x85uy; 0x16uy; 0xc2uy; 0xf3uy; 0x03uy; 0xfcuy; 0x95uy; 0x67uy; 0xf5uy; 0x60uy; 0xb8uy; |], | ||
BigInteger | ||
[| | ||
0x3auy; 0xc4uy; 0xc5uy; 0xa6uy; 0xdcuy; 0xa2uy; 0x01uy; 0x59uy; 0xfcuy; 0x56uy; 0xcfuy; 0x74uy; 0x9auy; 0xa6uy; 0xa5uy; 0x65uy; | ||
0x31uy; 0x6auy; 0xa5uy; 0x03uy; 0x74uy; 0x42uy; 0x3fuy; 0x42uy; 0x53uy; 0x8fuy; 0xaauy; 0x2cuy; 0xd3uy; 0x09uy; 0x3fuy; 0xa4uy; |]) | ||
|
||
/// Calculates the blinding factor x' = x + SHA256(xG+vH | xJ), used in the switch commitment x'G+vH. | ||
let BlindSwitch (blindingFactor: BlindingFactor) (amount: CAmount) : BlindingFactor = | ||
let hasher = Sha256Digest() | ||
|
||
let x = blindingFactor.ToUInt256().ToBytes() |> BigInteger | ||
let v = amount.ToString() |> BigInteger | ||
/// xG + vH | ||
let commitSerialized = generatorG.Multiply(x).Add(generatorH.Multiply(v)).GetEncoded() | ||
hasher.BlockUpdate(commitSerialized, 0, commitSerialized.Length) | ||
|
||
// xJ | ||
let xJ = generatorJPub.Multiply x | ||
let xJSerialized = xJ.GetEncoded true | ||
hasher.BlockUpdate(xJSerialized, 0, xJSerialized.Length) | ||
|
||
let hash = Array.zeroCreate<byte> 32 | ||
hasher.DoFinal(hash, 0) |> ignore | ||
|
||
let result = x.Add(BigInteger hash) | ||
|
||
result.ToByteArrayUnsigned() | ||
|> uint256 | ||
|> BlindingFactor.BlindingFactor | ||
|
||
/// Generates a pedersen commitment: *commit = blind * G + value * H. The blinding factor is 32 bytes. | ||
let Commit (value: CAmount) (blind: BlindingFactor) : PedersenCommitment = | ||
let result = | ||
generatorG.Multiply(blind.ToUInt256().ToBytes() |> BigInteger) | ||
.Add(generatorH.Multiply(BigInteger.ValueOf value)) | ||
let bytes = result.GetEncoded() | ||
assert(bytes.Length = PedersenCommitment.NumBytes) | ||
PedersenCommitment(BigInt bytes) | ||
|
||
let AddBlindingFactors (positive: array<BlindingFactor>) (negative: array<BlindingFactor>) : BlindingFactor = | ||
let sum (factors: array<BlindingFactor>) = | ||
factors | ||
|> Array.map (fun blind -> blind.ToUInt256().ToBytes() |> BigInteger) | ||
|> Array.fold (fun (a : BigInteger) b -> a.Add(b)) BigInteger.Zero | ||
|
||
let result = (sum positive).Subtract(sum negative) | ||
|
||
result.ToByteArrayUnsigned() | ||
|> uint256 | ||
|> BlindingFactor.BlindingFactor |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
module NLitecoin.MimbleWimble.TransactionBuilder | ||
|
||
open System | ||
|
||
open NBitcoin | ||
open Org.BouncyCastle.Math | ||
|
||
type private Inputs = | ||
{ | ||
TotalBlind: BlindingFactor | ||
TotalKey: uint256 | ||
Inputs: array<Input> | ||
} | ||
|
||
type private Outputs = | ||
{ | ||
TotalBlind: BlindingFactor | ||
TotalKey: uint256 | ||
Outputs: array<Output> | ||
Coins: array<NLitecoin.MimbleWimble.Coin> | ||
} | ||
|
||
/// Creates a standard input with a stealth key (feature bit = 1)// Creates a standard input with a stealth key (feature bit = 1) | ||
let private CreateInput (outputId: Hash) (commitment: PedersenCommitment) (inputKey: uint256) (outputKey: uint256) = | ||
let features = InputFeatures.STEALTH_KEY_FEATURE_BIT | ||
|
||
let inputPubKey = PublicKey(inputKey.ToBytes() |> BigInt) | ||
let outputPubKey = PublicKey(outputKey.ToBytes() |> BigInt) | ||
|
||
// Hash keys (K_i||K_o) | ||
let keyHasher = Hasher() | ||
keyHasher.Append inputPubKey | ||
keyHasher.Append outputPubKey | ||
let keyHash = keyHasher.Hash().ToBytes() | ||
|
||
// Calculate aggregated key k_agg = k_i + HASH(K_i||K_o) * k_o | ||
let sigKey = | ||
BigInteger(outputKey.ToBytes()) | ||
.Multiply(BigInteger keyHash) | ||
.Add(BigInteger(inputKey.ToBytes())) | ||
|
||
let msgHasher = Hasher() | ||
//msgHasher.Append features | ||
msgHasher.Append outputId | ||
let msgHash = msgHasher.Hash().ToBytes() | ||
|
||
let schnorrSignature = | ||
// is this the right one? | ||
NBitcoin.Secp256k1.ECPrivKey.Create(sigKey.ToByteArrayUnsigned()).SignBIP340(msgHash) | ||
|
||
{ | ||
Features = features | ||
OutputID = outputId | ||
Commitment = commitment | ||
InputPublicKey = Some inputPubKey | ||
OutputPublicKey = outputPubKey | ||
Signature = Signature(schnorrSignature.ToBytes() |> BigInt) | ||
ExtraData = Array.empty | ||
} | ||
|
||
let private CreateInputs (inputCoins: seq<NLitecoin.MimbleWimble.Coin>) : Inputs = | ||
let blinds, keys, inputs = | ||
[| for inputCoin in inputCoins do | ||
let blind = Pedersen.BlindSwitch inputCoin.Blind.Value inputCoin.Amount | ||
let ephemeralKey = NBitcoin.RandomUtils.GetUInt256() | ||
let input = | ||
CreateInput | ||
inputCoin.OutputId | ||
(Pedersen.Commit inputCoin.Amount blind) | ||
ephemeralKey | ||
inputCoin.SpendKey.Value | ||
yield blind, (BlindingFactor ephemeralKey, BlindingFactor inputCoin.SpendKey.Value), input |] | ||
|> Array.unzip3 | ||
|
||
let positiveKeys, negativeKeys = Array.unzip keys | ||
|
||
{ | ||
TotalBlind = Pedersen.AddBlindingFactors blinds Array.empty | ||
TotalKey = (Pedersen.AddBlindingFactors positiveKeys negativeKeys).ToUInt256() | ||
Inputs = inputs | ||
} | ||
|
||
let private CreateOutput (senderPrivKey: uint256) (receiverAddr: StealthAddress) (value: uint64) : Output * BlindingFactor = | ||
let features = OutputFeatures.STANDARD_FIELDS_FEATURE_BIT | ||
|
||
// Generate 128-bit secret nonce 'n' = Hash128(T_nonce, sender_privkey) | ||
let n = | ||
let hasher = Hasher(HashTags.NONCE) | ||
hasher.Write(senderPrivKey.ToBytes()) | ||
hasher.Hash().ToBytes() | ||
|> Array.take 16 | ||
|> BigInt | ||
|
||
// Calculate unique sending key 's' = H(T_send, A, B, v, n) | ||
let s = | ||
let hasher = Hasher(HashTags.SEND_KEY) | ||
hasher.Append receiverAddr.ScanPubKey | ||
hasher.Append receiverAddr.SpendPubKey | ||
hasher.Write (BitConverter.GetBytes value) | ||
hasher.Append n | ||
hasher.Hash().ToBytes() | ||
|> BigInteger | ||
|
||
let A = | ||
match receiverAddr.ScanPubKey with | ||
| PublicKey pubKey -> BigInteger pubKey.Data | ||
|
||
let B = | ||
match receiverAddr.SpendPubKey with | ||
| PublicKey pubKey -> BigInteger pubKey.Data | ||
|
||
// Derive shared secret 't' = H(T_derive, s*A) | ||
let sA = A.Multiply s | ||
let t = | ||
let hasher = Hasher(HashTags.DERIVE) | ||
hasher.Write(sA.ToByteArrayUnsigned()) | ||
hasher.Hash() | ||
|
||
// Construct one-time public key for receiver 'Ko' = H(T_outkey, t)*B | ||
let Ko = | ||
let hasher = Hasher(HashTags.OUT_KEY) | ||
hasher.Append t | ||
B.Multiply(hasher.Hash().ToBytes() |> BigInteger); | ||
|
||
// Key exchange public key 'Ke' = s*B | ||
let Ke = B.Multiply s | ||
|
||
// Calc blinding factor and mask nonce and amount. | ||
let mask = OutputMask.FromShared(t.ToUInt256()) | ||
let blind = Pedersen.BlindSwitch mask.PreBlind (int64 value) | ||
let mv = mask.MaskValue value | ||
let mn = mask.MaskNonce n | ||
|
||
// Commitment 'C' = r*G + v*H | ||
let outputCommit = Pedersen.Commit (int64 value) blind | ||
|
||
// Calculate the ephemeral send pubkey 'Ks' = ks*G | ||
let Ks = Secp256k1.ECPubKey.Create(senderPrivKey.ToBytes()) | ||
|
||
// Derive view tag as first byte of H(T_tag, sA) | ||
let viewTag = | ||
let hasher = Hasher(HashTags.TAG) | ||
hasher.Write(sA.ToByteArrayUnsigned()) | ||
hasher.Hash().ToBytes().[0] | ||
|
||
let message = | ||
{ | ||
Features = features | ||
StandardFields = | ||
Some { | ||
KeyExchangePubkey = PublicKey(Ke.ToByteArrayUnsigned() |> BigInt) | ||
ViewTag = viewTag | ||
MaskedValue = mv | ||
MaskedNonce = mn | ||
} | ||
ExtraData = Array.empty | ||
} | ||
|
||
failwith "not implemented" | ||
|
||
let private CreateOutputs (recipients: seq<Recipient>) : Outputs = | ||
let outputBlinds, outputs, coins = | ||
[| for recipient in recipients do | ||
let ephemeralKey = NBitcoin.RandomUtils.GetUInt256() | ||
let output, rawBlind = CreateOutput ephemeralKey recipient.Address (uint64 recipient.Amount) | ||
let outputBlind = Pedersen.BlindSwitch rawBlind recipient.Amount | ||
let coin = | ||
{ Coin.Empty with | ||
Blind = Some rawBlind | ||
Amount = recipient.Amount | ||
OutputId = output.GetOutputID() | ||
SenderKey = Some ephemeralKey | ||
Address = Some recipient.Address | ||
} | ||
yield outputBlind, output, coin | ||
|] | ||
|> Array.unzip3 | ||
|
||
let outputKeys = | ||
coins | ||
|> Array.choose (fun coin -> coin.SenderKey) | ||
|> Array.map BlindingFactor | ||
|
||
{ | ||
TotalBlind = Pedersen.AddBlindingFactors outputBlinds Array.empty | ||
TotalKey = (Pedersen.AddBlindingFactors outputKeys Array.empty).ToUInt256() | ||
Outputs = outputs | ||
Coins = coins | ||
} |
Oops, something went wrong.