Commit 3a1e91e7 authored by Jeremy Bokobza's avatar Jeremy Bokobza

updated NTumbleBit code changes

parent 065a6810
...@@ -71,8 +71,9 @@ namespace NTumbleBit ...@@ -71,8 +71,9 @@ namespace NTumbleBit
tx.Inputs.Add(new TxIn(coin.Outpoint)); tx.Inputs.Add(new TxIn(coin.Outpoint));
tx.Inputs[0].Sequence = 0; tx.Inputs[0].Sequence = 0;
tx.Outputs.Add(new TxOut(coin.Amount, redeemDestination)); tx.Outputs.Add(new TxOut(coin.Amount, redeemDestination));
var vSize = tx.GetVirtualSize() + 80;
tx.Inputs[0].ScriptSig = EscrowScriptBuilder.GenerateScriptSig(new TransactionSignature[] { null }) + Op.GetPushOp(coin.Redeem.ToBytes()); tx.Inputs[0].ScriptSig = EscrowScriptBuilder.GenerateScriptSig(new TransactionSignature[] { null }) + Op.GetPushOp(coin.Redeem.ToBytes());
var vSize = tx.GetVirtualSize() + 80; // Size without signature + the signature size
tx.Outputs[0].Value -= feeRate.GetFee(vSize); tx.Outputs[0].Value -= feeRate.GetFee(vSize);
var redeemTransaction = new TrustedBroadcastRequest var redeemTransaction = new TrustedBroadcastRequest
......
using NBitcoin.RPC; using NBitcoin.RPC;
using Newtonsoft.Json.Linq;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
...@@ -16,5 +17,33 @@ namespace NTumbleBit ...@@ -16,5 +17,33 @@ namespace NTumbleBit
Params = parameters Params = parameters
}, throwIfRPCError: false); }, throwIfRPCError: false);
} }
public static JArray ListTransactions(this RPCClient rpcClient)
{
JArray array = new JArray();
int count = 100;
int skip = 0;
int highestConfirmation = 0;
while(true)
{
var result = rpcClient.SendCommandNoThrows("listtransactions", "*", count, skip, true);
skip += count;
if(result.Error != null)
return null;
var transactions = (JArray)result.Result;
foreach(var obj in transactions)
{
array.Add(obj);
if(obj["confirmations"] != null)
{
highestConfirmation = Math.Max(highestConfirmation, (int)obj["confirmations"]);
}
}
if(transactions.Count < count || highestConfirmation >= 1400)
break;
}
return array;
}
} }
} }
...@@ -245,7 +245,12 @@ namespace NTumbleBit.PuzzlePromise ...@@ -245,7 +245,12 @@ namespace NTumbleBit.PuzzlePromise
Transaction cashout = new Transaction(); Transaction cashout = new Transaction();
cashout.AddInput(new TxIn(InternalState.EscrowedCoin.Outpoint, Script.Empty)); cashout.AddInput(new TxIn(InternalState.EscrowedCoin.Outpoint, Script.Empty));
cashout.AddOutput(new TxOut(Money.Zero, cashoutDestination)); cashout.AddOutput(new TxOut(Money.Zero, cashoutDestination));
var fee = feeRate.GetFee(cashout.GetVirtualSize());
var tb = new TransactionBuilder();
tb.Extensions.Add(new EscrowBuilderExtension());
tb.AddCoins(InternalState.EscrowedCoin);
var size = tb.EstimateSize(cashout, true);
var fee = feeRate.GetFee(size);
cashout.Outputs[0].Value = InternalState.EscrowedCoin.Amount - fee; cashout.Outputs[0].Value = InternalState.EscrowedCoin.Amount - fee;
......
...@@ -318,6 +318,15 @@ namespace NTumbleBit.PuzzleSolver ...@@ -318,6 +318,15 @@ namespace NTumbleBit.PuzzleSolver
return signature; return signature;
} }
public TransactionSignature SignEscape()
{
AssertState(SolverClientStates.Completed);
var dummy = new Transaction();
dummy.Inputs.Add(new TxIn(InternalState.EscrowedCoin.Outpoint));
dummy.Outputs.Add(new TxOut());
return dummy.SignInput(InternalState.EscrowKey, InternalState.EscrowedCoin, SigHash.None | SigHash.AnyoneCanPay);
}
private Transaction CreateUnsignedOfferTransaction() private Transaction CreateUnsignedOfferTransaction()
{ {
Script offer = CreateOfferScript(); Script offer = CreateOfferScript();
...@@ -333,15 +342,17 @@ namespace NTumbleBit.PuzzleSolver ...@@ -333,15 +342,17 @@ namespace NTumbleBit.PuzzleSolver
{ {
var coin = CreateUnsignedOfferTransaction().Outputs.AsCoins().First().ToScriptCoin(CreateOfferScript()); var coin = CreateUnsignedOfferTransaction().Outputs.AsCoins().First().ToScriptCoin(CreateOfferScript());
var unknownOutpoints = new OutPoint(uint256.Zero, 0);
Transaction tx = new Transaction(); Transaction tx = new Transaction();
tx.LockTime = CreateOfferScriptParameters().Expiration; tx.LockTime = CreateOfferScriptParameters().Expiration;
tx.Inputs.Add(new TxIn(coin.Outpoint)); tx.Inputs.Add(new TxIn(unknownOutpoints));
tx.Inputs[0].Sequence = 0; tx.Inputs[0].Sequence = 0;
tx.Outputs.Add(new TxOut(coin.Amount, redeemDestination)); tx.Outputs.Add(new TxOut(coin.Amount, redeemDestination));
var vSize = tx.GetVirtualSize() + 80;
tx.Outputs[0].Value -= feeRate.GetFee(vSize);
tx.Inputs[0].ScriptSig = new Script(OpcodeType.OP_0) + Op.GetPushOp(coin.Redeem.ToBytes()); tx.Inputs[0].ScriptSig = new Script(OpcodeType.OP_0) + Op.GetPushOp(coin.Redeem.ToBytes());
var vSize = tx.GetVirtualSize() + 80; // Size without signature + the signature size
tx.Outputs[0].Value -= feeRate.GetFee(vSize);
var redeemTransaction = new TrustedBroadcastRequest var redeemTransaction = new TrustedBroadcastRequest
{ {
Key = InternalState.RedeemKey, Key = InternalState.RedeemKey,
...@@ -349,7 +360,7 @@ namespace NTumbleBit.PuzzleSolver ...@@ -349,7 +360,7 @@ namespace NTumbleBit.PuzzleSolver
Transaction = tx Transaction = tx
}; };
//Strip redeem script information so we check if TrustedBroadcastRequest can sign correctly //Strip redeem script information so we check if TrustedBroadcastRequest can sign correctly
redeemTransaction.Transaction = redeemTransaction.ReSign(new Coin(coin.Outpoint, coin.TxOut)); redeemTransaction.Transaction = redeemTransaction.ReSign(new Coin(unknownOutpoints, coin.TxOut));
return redeemTransaction; return redeemTransaction;
} }
......
...@@ -20,6 +20,7 @@ namespace NTumbleBit.PuzzleSolver ...@@ -20,6 +20,7 @@ namespace NTumbleBit.PuzzleSolver
WaitingRevelation, WaitingRevelation,
WaitingBlindFactor, WaitingBlindFactor,
WaitingFulfillment, WaitingFulfillment,
WaitingEscape,
Completed Completed
} }
public class SolverServerSession : EscrowReceiver public class SolverServerSession : EscrowReceiver
...@@ -289,7 +290,7 @@ namespace NTumbleBit.PuzzleSolver ...@@ -289,7 +290,7 @@ namespace NTumbleBit.PuzzleSolver
public TrustedBroadcastRequest GetSignedOfferTransaction() public TrustedBroadcastRequest GetSignedOfferTransaction()
{ {
AssertState(SolverServerStates.Completed); AssertState(SolverServerStates.WaitingEscape);
var offerTransaction = GetUnsignedOfferTransaction(); var offerTransaction = GetUnsignedOfferTransaction();
TransactionBuilder txBuilder = new TransactionBuilder(); TransactionBuilder txBuilder = new TransactionBuilder();
txBuilder.Extensions.Add(new EscrowBuilderExtension()); txBuilder.Extensions.Add(new EscrowBuilderExtension());
...@@ -307,7 +308,7 @@ namespace NTumbleBit.PuzzleSolver ...@@ -307,7 +308,7 @@ namespace NTumbleBit.PuzzleSolver
public SolutionKey[] GetSolutionKeys() public SolutionKey[] GetSolutionKeys()
{ {
AssertState(SolverServerStates.Completed); AssertState(SolverServerStates.WaitingEscape);
return InternalState.SolvedPuzzles.Select(s => s.SolutionKey).ToArray(); return InternalState.SolvedPuzzles.Select(s => s.SolutionKey).ToArray();
} }
...@@ -330,30 +331,36 @@ namespace NTumbleBit.PuzzleSolver ...@@ -330,30 +331,36 @@ namespace NTumbleBit.PuzzleSolver
Script offerScript = GetOfferScript(); Script offerScript = GetOfferScript();
var offer = GetUnsignedOfferTransaction(); var offer = GetUnsignedOfferTransaction();
PubKey clientKey = AssertValidSignature(clientSignature, offer);
TransactionBuilder builder = new TransactionBuilder(); TransactionBuilder builder = new TransactionBuilder();
builder.StandardTransactionPolicy.CheckFee = false;
builder.Extensions.Add(new EscrowBuilderExtension()); builder.Extensions.Add(new EscrowBuilderExtension());
builder.AddCoins(InternalState.EscrowedCoin); builder.AddCoins(InternalState.EscrowedCoin);
builder.AddKeys(InternalState.EscrowKey); builder.AddKeys(InternalState.EscrowKey);
var clientKey = InternalState.GetClientEscrowPubKey();
builder.AddKnownSignature(clientKey, clientSignature); builder.AddKnownSignature(clientKey, clientSignature);
builder.SignTransactionInPlace(offer); builder.SignTransactionInPlace(offer);
if(!builder.Verify(offer)) if(!builder.Verify(offer))
throw new PuzzleException("invalid-signature"); throw new PuzzleException("invalid-tumbler-signature");
var offerCoin = offer.Outputs.AsCoins().First().ToScriptCoin(offerScript); var offerCoin = offer.Outputs.AsCoins().First().ToScriptCoin(offerScript);
var solutions = InternalState.SolvedPuzzles.Select(s => s.SolutionKey).ToArray(); var solutions = InternalState.SolvedPuzzles.Select(s => s.SolutionKey).ToArray();
Transaction fulfill = new Transaction(); Transaction fulfill = new Transaction();
fulfill.Inputs.Add(new TxIn(offerCoin.Outpoint)); fulfill.Inputs.Add(new TxIn(offerCoin.Outpoint));
fulfill.Outputs.Add(new TxOut(offerCoin.Amount, cashout)); fulfill.Outputs.Add(new TxOut(offerCoin.Amount, cashout));
var size = new OfferBuilderExtension().EstimateScriptSigSize(offerCoin.Redeem);
fulfill.Outputs[0].Value -= feeRate.GetFee(size); var fulfillScript = SolverScriptBuilder.CreateFulfillScript(NBitcoin.BuilderExtensions.BuilderExtension.DummySignature, solutions);
fulfill.Inputs[0].ScriptSig = fulfillScript + Op.GetPushOp(offerCoin.Redeem.ToBytes());
fulfill.Outputs[0].Value -= feeRate.GetFee(fulfill.GetVirtualSize());
var signature = fulfill.Inputs.AsIndexedInputs().First().Sign(InternalState.FulfillKey, offerCoin, SigHash.All); var signature = fulfill.Inputs.AsIndexedInputs().First().Sign(InternalState.FulfillKey, offerCoin, SigHash.All);
var fulfillScript = SolverScriptBuilder.CreateFulfillScript(signature, solutions); fulfillScript = SolverScriptBuilder.CreateFulfillScript(signature, solutions);
fulfill.Inputs[0].ScriptSig = fulfillScript + Op.GetPushOp(offerCoin.Redeem.ToBytes()); fulfill.Inputs[0].ScriptSig = fulfillScript + Op.GetPushOp(offerCoin.Redeem.ToBytes());
InternalState.OfferClientSignature = clientSignature; InternalState.OfferClientSignature = clientSignature;
InternalState.Status = SolverServerStates.Completed; InternalState.Status = SolverServerStates.WaitingEscape;
return new TrustedBroadcastRequest return new TrustedBroadcastRequest
{ {
Key = InternalState.FulfillKey, Key = InternalState.FulfillKey,
...@@ -362,6 +369,40 @@ namespace NTumbleBit.PuzzleSolver ...@@ -362,6 +369,40 @@ namespace NTumbleBit.PuzzleSolver
}; };
} }
private PubKey AssertValidSignature(TransactionSignature clientSignature, Transaction offer)
{
var signedHash = offer.Inputs.AsIndexedInputs().First().GetSignatureHash(InternalState.EscrowedCoin, clientSignature.SigHash);
var clientKey = InternalState.GetClientEscrowPubKey();
if(!clientKey.Verify(signedHash, clientSignature.Signature))
throw new PuzzleException("invalid-client-signature");
return clientKey;
}
public Transaction GetSignedEscapeTransaction(TransactionSignature clientSignature, Script cashout)
{
AssertState(SolverServerStates.WaitingEscape);
var escapeTx = GetUnsignedOfferTransaction();
escapeTx.Outputs[0].ScriptPubKey = cashout;
var clientKey = AssertValidSignature(clientSignature, escapeTx);
TransactionBuilder builder = new TransactionBuilder();
builder.StandardTransactionPolicy.CheckFee = false;
builder.Extensions.Add(new EscrowBuilderExtension());
builder.AddCoins(InternalState.EscrowedCoin);
builder.AddKnownSignature(clientKey, clientSignature);
//This add the known signature if correct SigHash
builder.SignTransactionInPlace(escapeTx, SigHash.None | SigHash.AnyoneCanPay);
//This sign SigHash.All
builder.AddKeys(InternalState.EscrowKey);
builder.SignTransactionInPlace(escapeTx);
if(!builder.Verify(escapeTx))
throw new PuzzleException("invalid-tumbler-signature");
return escapeTx;
}
private Script GetOfferScript() private Script GetOfferScript()
{ {
var escrow = EscrowScriptBuilder.ExtractEscrowScriptPubKeyParameters(InternalState.EscrowedCoin.Redeem); var escrow = EscrowScriptBuilder.ExtractEscrowScriptPubKeyParameters(InternalState.EscrowedCoin.Redeem);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment