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

updated NTumbleBit code changes

parent 065a6810
......@@ -71,8 +71,9 @@ namespace NTumbleBit
tx.Inputs.Add(new TxIn(coin.Outpoint));
tx.Inputs[0].Sequence = 0;
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());
var vSize = tx.GetVirtualSize() + 80; // Size without signature + the signature size
tx.Outputs[0].Value -= feeRate.GetFee(vSize);
var redeemTransaction = new TrustedBroadcastRequest
......
using NBitcoin.RPC;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
......@@ -16,5 +17,33 @@ namespace NTumbleBit
Params = parameters
}, 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
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());
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;
......
......@@ -318,6 +318,15 @@ namespace NTumbleBit.PuzzleSolver
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()
{
Script offer = CreateOfferScript();
......@@ -333,15 +342,17 @@ namespace NTumbleBit.PuzzleSolver
{
var coin = CreateUnsignedOfferTransaction().Outputs.AsCoins().First().ToScriptCoin(CreateOfferScript());
var unknownOutpoints = new OutPoint(uint256.Zero, 0);
Transaction tx = new Transaction();
tx.LockTime = CreateOfferScriptParameters().Expiration;
tx.Inputs.Add(new TxIn(coin.Outpoint));
tx.Inputs.Add(new TxIn(unknownOutpoints));
tx.Inputs[0].Sequence = 0;
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());
var vSize = tx.GetVirtualSize() + 80; // Size without signature + the signature size
tx.Outputs[0].Value -= feeRate.GetFee(vSize);
var redeemTransaction = new TrustedBroadcastRequest
{
Key = InternalState.RedeemKey,
......@@ -349,7 +360,7 @@ namespace NTumbleBit.PuzzleSolver
Transaction = tx
};
//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;
}
......
......@@ -20,6 +20,7 @@ namespace NTumbleBit.PuzzleSolver
WaitingRevelation,
WaitingBlindFactor,
WaitingFulfillment,
WaitingEscape,
Completed
}
public class SolverServerSession : EscrowReceiver
......@@ -289,7 +290,7 @@ namespace NTumbleBit.PuzzleSolver
public TrustedBroadcastRequest GetSignedOfferTransaction()
{
AssertState(SolverServerStates.Completed);
AssertState(SolverServerStates.WaitingEscape);
var offerTransaction = GetUnsignedOfferTransaction();
TransactionBuilder txBuilder = new TransactionBuilder();
txBuilder.Extensions.Add(new EscrowBuilderExtension());
......@@ -307,7 +308,7 @@ namespace NTumbleBit.PuzzleSolver
public SolutionKey[] GetSolutionKeys()
{
AssertState(SolverServerStates.Completed);
AssertState(SolverServerStates.WaitingEscape);
return InternalState.SolvedPuzzles.Select(s => s.SolutionKey).ToArray();
}
......@@ -330,30 +331,36 @@ namespace NTumbleBit.PuzzleSolver
Script offerScript = GetOfferScript();
var offer = GetUnsignedOfferTransaction();
PubKey clientKey = AssertValidSignature(clientSignature, offer);
TransactionBuilder builder = new TransactionBuilder();
builder.StandardTransactionPolicy.CheckFee = false;
builder.Extensions.Add(new EscrowBuilderExtension());
builder.AddCoins(InternalState.EscrowedCoin);
builder.AddKeys(InternalState.EscrowKey);
var clientKey = InternalState.GetClientEscrowPubKey();
builder.AddKnownSignature(clientKey, clientSignature);
builder.SignTransactionInPlace(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 solutions = InternalState.SolvedPuzzles.Select(s => s.SolutionKey).ToArray();
Transaction fulfill = new Transaction();
fulfill.Inputs.Add(new TxIn(offerCoin.Outpoint));
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 fulfillScript = SolverScriptBuilder.CreateFulfillScript(signature, solutions);
fulfillScript = SolverScriptBuilder.CreateFulfillScript(signature, solutions);
fulfill.Inputs[0].ScriptSig = fulfillScript + Op.GetPushOp(offerCoin.Redeem.ToBytes());
InternalState.OfferClientSignature = clientSignature;
InternalState.Status = SolverServerStates.Completed;
InternalState.Status = SolverServerStates.WaitingEscape;
return new TrustedBroadcastRequest
{
Key = InternalState.FulfillKey,
......@@ -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()
{
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