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 ServerCommitmentsProof CheckRevelation(ClientRevelation revelation) { if (revelation == null) { throw new ArgumentNullException(nameof(revelation)); } if (revelation.Salts.Length != Parameters.FakeTransactionCount || revelation.FakeIndexes.Length != Parameters.FakeTransactionCount) { throw new ArgumentNullException("The revelation should contains " + Parameters.FakeTransactionCount + " indexes and salts"); } AssertState(PromiseServerStates.WaitingRevelation); var indexSalt = revelation.IndexesSalt; if (InternalState.FakeIndexesHash != PromiseUtils.HashIndexes(ref indexSalt, revelation.FakeIndexes)) { throw new PuzzleException("Invalid index salt"); } List <PuzzleSolution> solutions = new List <PuzzleSolution>(); for (int i = 0; i < Parameters.FakeTransactionCount; i++) { var salt = revelation.Salts[i]; var encrypted = InternalState.EncryptedSignatures[revelation.FakeIndexes[i]]; var actualSignedHash = Parameters.CreateFakeHash(salt); if (actualSignedHash != encrypted.SignedHash) { throw new PuzzleException("Incorrect salt provided"); } solutions.Add(encrypted.PuzzleSolution); } // We can throw away the fake puzzles InternalState.EncryptedSignatures = InternalState.EncryptedSignatures .Where((e, i) => !revelation.FakeIndexes.Contains(i)).ToArray(); Quotient[] quotients = new Quotient[Parameters.RealTransactionCount - 1]; for (int i = 0; i < InternalState.EncryptedSignatures.Length - 1; i++) { var a = InternalState.EncryptedSignatures[i].PuzzleSolution._Value; var b = InternalState.EncryptedSignatures[i + 1].PuzzleSolution._Value; quotients[i] = new Quotient(b.Multiply(a.ModInverse(Parameters.ServerKey._Key.Modulus)) .Mod(Parameters.ServerKey._Key.Modulus)); } InternalState.FakeIndexesHash = null; InternalState.Status = PromiseServerStates.Completed; return(new ServerCommitmentsProof(solutions.ToArray(), quotients)); }
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); }
public ServerCommitmentsProof CheckRevelation(ClientRevelation revelation, Script cashoutDestination, FeeRate feeRate) { /* * Steps 7, 9 * Almost ready, just need to figure out: * - The CashOutFormat for the validation of RealSet. * - How to get the cashoutDestination and the feeRate, * for now I pass them in like in "CreateSignatureRequest" * from ClientSession. */ if (revelation == null) { throw new ArgumentNullException(nameof(revelation)); } var saltCount = revelation.Salts.Select(a => a.Length).Sum(); if (saltCount != Parameters.GetTotalFakeTransactionsCount() || revelation.FakeIndexes.Length != Parameters.FakeTransactionCountPerLevel) { throw new ArgumentNullException($"The revelation should contains {Parameters.GetTotalFakeTransactionsCount()} salts and {Parameters.FakeTransactionCountPerLevel} indices"); } var variationCount = revelation.FeeVariations.Select(a => a.Length).Sum(); if (variationCount != Parameters.GetTotalRealTransactionsCount()) { throw new ArgumentNullException($"The revelation should contains {Parameters.GetTotalRealTransactionsCount()} fee variations"); } AssertState(PromiseServerStates.WaitingRevelation); var indexSalt = revelation.IndexesSalt; if (InternalState.FakeIndexesHash != PromiseUtils.HashIndexes(ref indexSalt, revelation.FakeIndexes)) { throw new PuzzleException("Invalid index salt"); } Transaction cashout = new Transaction(); // TODO: Figure out the cashout format for j Bitcoins 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()); var solutions = new PuzzleSolution[Parameters.PaymentsCount][]; var RealIndexes = Enumerable.Range(0, Parameters.GetTotalTransactionsCountPerLevel()).Where(a => !revelation.FakeIndexes.Contains(a)).ToArray(); for (int i = 0; i < solutions.Length; i++) { // Checking valid Transactions for (int j = 0; j < Parameters.RealTransactionCountPerLevel; j++) { var feeVariation = revelation.FeeVariations[i][j]; var encrypted = InternalState.EncryptedSignatures[i][RealIndexes[j]]; // Check if this function approved! var actualSignedHash = Parameters.CreateRealHash(cashout, InternalState.EscrowedCoin, feeVariation); if (actualSignedHash != encrypted.SignedHash) { throw new PuzzleException("Incorrect feeVariation provided"); } } // Checking Fake Transactions solutions[i] = new PuzzleSolution[Parameters.FakeTransactionCountPerLevel]; // Initialization for (int j = 0; j < solutions[i].Length; j++) { var salt = revelation.Salts[i][j]; var encrypted = InternalState.EncryptedSignatures[i][revelation.FakeIndexes[j]]; var actualSignedHash = Parameters.CreateFakeHash(salt); if (actualSignedHash != encrypted.SignedHash) { throw new PuzzleException("Incorrect salt provided"); } solutions[i][j] = encrypted.PuzzleSolution; } } // We can throw away the fake puzzles InternalState.EncryptedSignatures = InternalState.EncryptedSignatures.Select(a => a.Where((e, i) => !revelation.FakeIndexes.Contains(i)).ToArray()).ToArray(); // Step 9 var quotients = new Quotient[Parameters.PaymentsCount][]; for (int i = 0; i < quotients.Length; i++) { quotients[i] = new Quotient[Parameters.RealTransactionCountPerLevel - 1]; for (int j = 0; j < quotients[i].Length; j++) { var a = InternalState.EncryptedSignatures[i][j].PuzzleSolution._Value; var b = InternalState.EncryptedSignatures[i][j + 1].PuzzleSolution._Value; quotients[i][j] = new Quotient(b.Multiply(a.ModInverse(Parameters.ServerKey._Key.Modulus)).Mod(Parameters.ServerKey._Key.Modulus)); } } InternalState.FakeIndexesHash = null; InternalState.Status = PromiseServerStates.Completed; return(new ServerCommitmentsProof(solutions.ToArray(), quotients)); }