public ServerCommitment[] SignHashes(SignaturesRequest sigRequest) { if (sigRequest == null) { throw new ArgumentNullException(nameof(sigRequest)); } if (sigRequest.Hashes.Length != Parameters.GetTotalTransactionsCount()) { throw new ArgumentException("Incorrect number of hashes, expected " + sigRequest.Hashes.Length); } AssertState(PromiseServerStates.WaitingHashes); List <ServerCommitment> promises = new List <ServerCommitment>(); List <EncryptedSignature> encryptedSignatures = new List <EncryptedSignature>(); foreach (var hash in sigRequest.Hashes) { var ecdsa = InternalState.EscrowKey.Sign(hash); var ecdsaDER = ecdsa.ToDER(); var key = new XORKey(Parameters.ServerKey); var promise = key.XOR(ecdsaDER); PuzzleSolution solution = new PuzzleSolution(key.ToBytes()); var puzzle = Parameters.ServerKey.GeneratePuzzle(ref solution); promises.Add(new ServerCommitment(puzzle.PuzzleValue, promise)); encryptedSignatures.Add(new EncryptedSignature(ecdsa, hash, solution)); } InternalState.Status = PromiseServerStates.WaitingRevelation; InternalState.EncryptedSignatures = encryptedSignatures.ToArray(); InternalState.FakeIndexesHash = sigRequest.FakeIndexesHash; return(promises.ToArray()); }
public SignaturesRequest CreateSignatureRequest(Script cashoutDestination, FeeRate feeRate) { if (cashoutDestination == null) { throw new ArgumentNullException(nameof(cashoutDestination)); } if (feeRate == null) { throw new ArgumentNullException(nameof(feeRate)); } AssertState(PromiseClientStates.WaitingSignatureRequest); Transaction cashout = new Transaction(); cashout.AddInput(new TxIn(InternalState.EscrowedCoin.Outpoint, Script.Empty)); cashout.AddOutput(new TxOut(Money.Zero, cashoutDestination)); var fee = feeRate.GetFee(cashout.GetVirtualSize()); cashout.Outputs[0].Value = InternalState.EscrowedCoin.Amount - fee; List <HashBase> hashes = new List <HashBase>(); LockTime lockTime = new LockTime(0); for (int i = 0; i < Parameters.RealTransactionCount; i++) { RealHash h = new RealHash(cashout, InternalState.EscrowedCoin); h.LockTime = lockTime; lockTime++; hashes.Add(h); } for (int i = 0; i < Parameters.FakeTransactionCount; i++) { FakeHash h = new FakeHash(Parameters); h.Salt = new uint256(RandomUtils.GetBytes(32)); hashes.Add(h); } _Hashes = hashes.ToArray(); NBitcoin.Utils.Shuffle(_Hashes, RandomUtils.GetInt32()); for (int i = 0; i < _Hashes.Length; i++) { _Hashes[i].Index = i; } var fakeIndices = _Hashes.OfType <FakeHash>().Select(h => h.Index).ToArray(); uint256 indexSalt = null; var request = new SignaturesRequest { Hashes = _Hashes.Select(h => h.GetHash()).ToArray(), FakeIndexesHash = PromiseUtils.HashIndexes(ref indexSalt, fakeIndices), }; InternalState.IndexSalt = indexSalt; InternalState.Cashout = cashout.Clone(); InternalState.Status = PromiseClientStates.WaitingCommitments; InternalState.FakeIndexes = fakeIndices; return(request); }
public ServerCommitment[][] SignHashes(SignaturesRequest sigRequest) { // Almost done, just need to confirm a typo and a function. // Step 5 if (sigRequest == null) { throw new ArgumentNullException(nameof(sigRequest)); } var hashesCount = sigRequest.Hashes.Select(a => a.Length).Sum(); if (hashesCount != Parameters.GetTotalTransactionsCount()) { throw new ArgumentException($"Incorrect number of hashes, expected {Parameters.GetTotalTransactionsCount()}"); } AssertState(PromiseServerStates.WaitingHashes); // 2D array of pairs of puzzles and promises (z_i, c_i). var promises = new ServerCommitment[Parameters.PaymentsCount][]; // 2D array of encrypted signatures with their solutions. var encryptedSignatures = new EncryptedSignature[Parameters.PaymentsCount][]; // 1-D array is used to store the epsilons for each column to be used when Hashing. var previousSolutions = new byte[Parameters.GetTotalTransactionsCountPerLevel()][]; previousSolutions = previousSolutions.Select(a => new byte[0]).ToArray(); // Initialize to empty array of bytes for (int i = 0; i < Parameters.PaymentsCount; i++) { promises[i] = new ServerCommitment[sigRequest.Hashes[i].Length]; // Initialization encryptedSignatures[i] = new EncryptedSignature[promises[i].Length]; // Initialization for (int j = 0; j < promises[i].Length; j++) { var hash = sigRequest.Hashes[i][j]; // Sign the hash value var ecdsa = InternalState.EscrowKey.Sign(hash); // Convert Signature to Bytes. var ecdsaDER = ecdsa.ToDER(); // This can be replaced by "Utils.GenerateEncryptableInteger(Key)" if padding when XORing is not important. var key = (new XORKey(Parameters.ServerKey)).ToBytes(); // This just generates a random epsilon. // Append the new epsilon to the list of epsilons we have for that column to create "epsilon_{i-1,j}|| . . . , epsilon_{0,j}". previousSolutions[j] = Utils.Combine(key, previousSolutions[j]); // Create the padded solution with the following format "i||j||epsilon_{i,j}||epsilon_{i-1,j}|| . . . , epsilon_{0,j}" var paddedSolutions = new PuzzleSolution(Utils.Combine(NBitcoin.Utils.ToBytes((uint)i, true), NBitcoin.Utils.ToBytes((uint)j, true), previousSolutions[j])); // Hash and XOR the padded solution with the signature we have. var promise = XORKey.XOR(paddedSolutions._Value.ToByteArrayUnsigned(), ecdsaDER); // This function needs to be approved "XOR". PuzzleSolution solution = new PuzzleSolution(key); // Epsilon // Encrypt the epsilon value using RSA var puzzle = Parameters.ServerKey.GeneratePuzzle(ref solution); promises[i][j] = new ServerCommitment(puzzle.PuzzleValue, promise); encryptedSignatures[i][j] = new EncryptedSignature(ecdsa, hash, solution); } } InternalState.Status = PromiseServerStates.WaitingRevelation; InternalState.EncryptedSignatures = encryptedSignatures; InternalState.FakeIndexesHash = sigRequest.FakeIndexesHash; return(promises); }
public SignaturesRequest CreateSignatureRequest(Script cashoutDestination, FeeRate feeRate) { if (cashoutDestination == null) { throw new ArgumentNullException(nameof(cashoutDestination)); } if (feeRate == null) { throw new ArgumentNullException(nameof(feeRate)); } AssertState(PromiseClientStates.WaitingSignatureRequest); Transaction cashout = new Transaction(); cashout.AddInput(new TxIn(InternalState.EscrowedCoin.Outpoint)); cashout.Inputs[0].ScriptSig = new Script( Op.GetPushOp(TrustedBroadcastRequest.PlaceholderSignature), Op.GetPushOp(TrustedBroadcastRequest.PlaceholderSignature), Op.GetPushOp(InternalState.EscrowedCoin.Redeem.ToBytes()) ); cashout.Inputs[0].Witnessify(); cashout.AddOutput(new TxOut(InternalState.EscrowedCoin.Amount, cashoutDestination)); cashout.Outputs[0].Value -= feeRate.GetFee(cashout.GetVirtualSize()); List <HashBase> hashes = new List <HashBase>(); for (int i = 0; i < Parameters.RealTransactionCount; i++) { RealHash h = new RealHash(cashout, InternalState.EscrowedCoin); h.FeeVariation = Money.Satoshis(i); hashes.Add(h); } for (int i = 0; i < Parameters.FakeTransactionCount; i++) { FakeHash h = new FakeHash(Parameters); h.Salt = new uint256(RandomUtils.GetBytes(32)); hashes.Add(h); } _Hashes = hashes.ToArray(); NBitcoin.Utils.Shuffle(_Hashes, RandomUtils.GetInt32()); for (int i = 0; i < _Hashes.Length; i++) { _Hashes[i].Index = i; } var fakeIndices = _Hashes.OfType <FakeHash>().Select(h => h.Index).ToArray(); uint256 indexSalt = null; var request = new SignaturesRequest { Hashes = _Hashes.Select(h => h.GetHash()).ToArray(), FakeIndexesHash = PromiseUtils.HashIndexes(ref indexSalt, fakeIndices), }; InternalState.IndexSalt = indexSalt; InternalState.Cashout = cashout.Clone(); InternalState.Status = PromiseClientStates.WaitingCommitments; InternalState.FakeIndexes = fakeIndices; return(request); }
public SignaturesRequest CreateSignatureRequest(Script cashoutDestination, FeeRate feeRate) { // Steps 2-4 // Almost done, just need to figure out the Transaction CashOut things. if (cashoutDestination == null) { throw new ArgumentNullException(nameof(cashoutDestination)); } if (feeRate == null) { throw new ArgumentNullException(nameof(feeRate)); } AssertState(PromiseClientStates.WaitingSignatureRequest); Transaction cashout = new Transaction(); // TODO: Figure out the cashout format to give j Bitcoins to Bob and Q-J to the Tumbler cashout.AddInput(new TxIn(InternalState.EscrowedCoin.Outpoint)); cashout.Inputs[0].ScriptSig = new Script( Op.GetPushOp(TrustedBroadcastRequest.PlaceholderSignature), Op.GetPushOp(TrustedBroadcastRequest.PlaceholderSignature), Op.GetPushOp(InternalState.EscrowedCoin.Redeem.ToBytes()) ); cashout.AddOutput(new TxOut(InternalState.EscrowedCoin.Amount, cashoutDestination)); cashout.Outputs[0].Value -= feeRate.GetFee(cashout.GetVirtualSize()); // If each payment level requires a different cashOut, then this // should be moved to the first loop. HashBase[][] hashes = new HashBase[_Parameters.PaymentsCount][]; //2D for (int i = 0; i < _Parameters.PaymentsCount; i++) { hashes[i] = new HashBase[_Parameters.GetTotalTransactionsCountPerLevel()]; for (int j = 0; j < Parameters.RealTransactionCountPerLevel; j++) { RealHash h = new RealHash(cashout, InternalState.EscrowedCoin) { FeeVariation = Money.Satoshis(i) }; hashes[i][j] = h; } for (int j = Parameters.RealTransactionCountPerLevel; j < hashes[i].Length; j++) { FakeHash h = new FakeHash(Parameters) { Salt = new uint256(RandomUtils.GetBytes(32)) }; hashes[i][j] = h; } } _Hashes = hashes; // Under the assumption that given the same seed the Shuffle will be deterministic. // TODO: Verify this in Debugging or a unit test. var shuffleSeed = RandomUtils.GetInt32(); for (int i = 0; i < _Parameters.PaymentsCount; i++) { NBitcoin.Utils.Shuffle(_Hashes[i], shuffleSeed); } for (int i = 0; i < _Parameters.PaymentsCount; i++) { for (int j = 0; j < _Hashes[i].Length; j++) { _Hashes[i][j].Index = j; } } var fakeIndices = _Hashes.First().OfType <FakeHash>().Select(h => h.Index).ToArray(); uint256 indexSalt = null; var request = new SignaturesRequest { // This looks cool, but double check the use of Select in debugging. Hashes = _Hashes.Select(h => (h.Select(k => k.GetHash()).ToArray())).ToArray(), FakeIndexesHash = PromiseUtils.HashIndexes(ref indexSalt, fakeIndices), }; InternalState.IndexSalt = indexSalt; InternalState.Cashout = cashout.Clone(); InternalState.Status = PromiseClientStates.WaitingCommitments; InternalState.FakeColumns = fakeIndices; return(request); }