Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
B
Breeze
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
3
Issues
3
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
DeStream-public
Breeze
Commits
398ec79f
Commit
398ec79f
authored
Jun 14, 2017
by
Jeremy Bokobza
Committed by
GitHub
Jun 14, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #98 from bokobza/master
TumbleBit integration work
parents
065a6810
91e9a8a9
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
603 additions
and
109 deletions
+603
-109
TumbleBitController.cs
...reeze.TumbleBit.Client/Controllers/TumbleBitController.cs
+1
-1
ExternalServices.cs
Breeze/src/Breeze.TumbleBit.Client/ExternalServices.cs
+39
-0
IStateMachine.cs
Breeze/src/Breeze.TumbleBit.Client/IStateMachine.cs
+2
-2
ITumbleBitManager.cs
Breeze/src/Breeze.TumbleBit.Client/ITumbleBitManager.cs
+1
-1
ITumblerService.cs
Breeze/src/Breeze.TumbleBit.Client/ITumblerService.cs
+2
-0
RequestModels.cs
Breeze/src/Breeze.TumbleBit.Client/Models/RequestModels.cs
+4
-1
PaymentStateMachine.cs
Breeze/src/Breeze.TumbleBit.Client/PaymentStateMachine.cs
+290
-0
TransactionType.cs
Breeze/src/Breeze.TumbleBit.Client/TransactionType.cs
+22
-0
TumbleBitManager.cs
Breeze/src/Breeze.TumbleBit.Client/TumbleBitManager.cs
+22
-32
TumblerService.cs
Breeze/src/Breeze.TumbleBit.Client/TumblerService.cs
+6
-0
TumblingState.cs
Breeze/src/Breeze.TumbleBit.Client/TumblingState.cs
+113
-58
EscrowInitiator.cs
Breeze/src/NTumbleBit/EscrowInitiator.cs
+2
-1
Extensions.cs
Breeze/src/NTumbleBit/Extensions.cs
+29
-0
PromiseClientSession.cs
Breeze/src/NTumbleBit/PuzzlePromise/PromiseClientSession.cs
+6
-1
SolverClientSession.cs
Breeze/src/NTumbleBit/PuzzleSolver/SolverClientSession.cs
+15
-4
SolverServerSession.cs
Breeze/src/NTumbleBit/PuzzleSolver/SolverServerSession.cs
+49
-8
No files found.
Breeze/src/Breeze.TumbleBit.Client/Controllers/TumbleBitController.cs
View file @
398ec79f
...
...
@@ -63,7 +63,7 @@ namespace Breeze.TumbleBit.Controllers
try
{
await
this
.
tumbleBitManager
.
TumbleAsync
(
request
.
DestinationWalletName
);
await
this
.
tumbleBitManager
.
TumbleAsync
(
request
.
OriginWalletName
,
request
.
DestinationWalletName
);
return
this
.
Ok
();
}
catch
(
Exception
e
)
...
...
Breeze/src/Breeze.TumbleBit.Client/ExternalServices.cs
0 → 100644
View file @
398ec79f
using
System
;
using
System.Collections.Generic
;
using
System.Text
;
using
NBitcoin
;
using
NTumbleBit
;
namespace
Breeze.TumbleBit.Client
{
public
class
ExternalServices
{
public
Transaction
FundTransaction
(
TxOut
txOut
,
FeeRate
feeRate
)
{
return
null
;
}
public
void
Track
(
Script
scriptPubkey
)
{
}
public
bool
Broadcast
(
Transaction
tx
)
{
return
true
;
}
public
void
TrustedBroadcast
(
int
cycleStart
,
TransactionType
transactionType
,
uint
correlation
,
TrustedBroadcastRequest
broadcast
)
{
}
public
TransactionInformation
[]
GetTransactions
(
Script
scriptPubKey
,
bool
withProof
)
{
return
null
;
}
public
FeeRate
GetFeeRate
()
{
return
null
;
}
}
}
Breeze/src/Breeze.TumbleBit.Client/IStateMachine.cs
View file @
398ec79f
...
...
@@ -8,10 +8,10 @@
void
Save
();
/// <summary>
/// Loads the s
tate of the current tumbling session
.
/// Loads the s
aved state of the tumbling execution to the file system
.
/// </summary>
/// <returns></returns>
IStateMachine
Load
();
void
LoadStateFromMemory
();
/// <summary>
/// Deletes the state of the current tumbling session..
...
...
Breeze/src/Breeze.TumbleBit.Client/ITumbleBitManager.cs
View file @
398ec79f
...
...
@@ -17,7 +17,7 @@ namespace Breeze.TumbleBit.Client
/// <returns></returns>
Task
<
ClassicTumblerParameters
>
ConnectToTumblerAsync
(
Uri
serverAddress
);
Task
TumbleAsync
(
string
destinationWalletName
);
Task
TumbleAsync
(
string
originWalletName
,
string
destinationWalletName
);
/// <summary>
/// Processes a block received from the network.
...
...
Breeze/src/Breeze.TumbleBit.Client/ITumblerService.cs
View file @
398ec79f
...
...
@@ -40,5 +40,7 @@ namespace Breeze.TumbleBit.Client
Task
<
PuzzleSolver
.
ServerCommitment
[
]>
SolvePuzzlesAsync
(
int
cycleId
,
string
channelId
,
PuzzleValue
[]
puzzles
);
Task
<
SolutionKey
[
]>
FulfillOfferAsync
(
int
cycleId
,
string
channelId
,
TransactionSignature
clientSignature
);
Task
GiveEscapeKeyAsync
(
int
cycleId
,
string
channelId
,
TransactionSignature
signature
);
}
}
Breeze/src/Breeze.TumbleBit.Client/Models/RequestModels.cs
View file @
398ec79f
...
...
@@ -28,7 +28,10 @@ namespace Breeze.TumbleBit.Models
public
class
TumbleRequest
{
[
Required
(
ErrorMessage
=
"A wallet name is required."
)]
[
Required
(
ErrorMessage
=
"The name of the origin wallet is required."
)]
public
string
OriginWalletName
{
get
;
set
;
}
[
Required
(
ErrorMessage
=
"The name of the destination wallet is required."
)]
public
string
DestinationWalletName
{
get
;
set
;
}
}
}
Breeze/src/Breeze.TumbleBit.Client/PaymentStateMachine.cs
0 → 100644
View file @
398ec79f
This diff is collapsed.
Click to expand it.
Breeze/src/Breeze.TumbleBit.Client/TransactionType.cs
0 → 100644
View file @
398ec79f
namespace
Breeze.TumbleBit.Client
{
public
enum
TransactionType
:
int
{
TumblerEscrow
,
TumblerRedeem
,
/// <summary>
/// The transaction that cashout tumbler's escrow (go to client)
/// </summary>
TumblerCashout
,
ClientEscrow
,
ClientRedeem
,
ClientOffer
,
ClientEscape
,
/// <summary>
/// The transaction that cashout client's escrow (go to tumbler)
/// </summary>
ClientFulfill
,
ClientOfferRedeem
}
}
Breeze/src/Breeze.TumbleBit.Client/TumbleBitManager.cs
View file @
398ec79f
...
...
@@ -23,21 +23,18 @@ namespace Breeze.TumbleBit.Client
private
readonly
Network
network
;
private
TumblingState
tumblingState
;
private
IDisposable
blockReceiver
;
int
lastCycleStarted
;
private
ClassicTumblerParameters
TumblerParameters
{
get
;
set
;
}
public
TumbleBitManager
(
ILoggerFactory
loggerFactory
,
IWalletManager
walletManager
,
ConcurrentChain
chain
,
Network
network
,
Signals
signals
)
{
this
.
lastCycleStarted
=
0
;
this
.
walletManager
=
walletManager
;
this
.
chain
=
chain
;
this
.
signals
=
signals
;
this
.
network
=
network
;
this
.
logger
=
loggerFactory
.
CreateLogger
(
this
.
GetType
().
FullName
);
// load the persisted tumbling state
this
.
tumblingState
=
TumblingState
.
LoadState
();
this
.
tumblingState
=
new
TumblingState
(
loggerFactory
,
this
.
chain
,
this
.
walletManager
,
this
.
network
);
}
/// <inheritdoc />
...
...
@@ -50,21 +47,21 @@ namespace Breeze.TumbleBit.Client
{
throw
new
Exception
(
$"The tumbler is on network
{
this
.
TumblerParameters
.
Network
}
while the wallet is on network
{
this
.
network
}
."
);
}
if
(
this
.
tumblingState
==
null
)
{
this
.
tumblingState
=
new
TumblingState
();
}
// load the current tumbling state fromt he file system
this
.
tumblingState
.
LoadStateFromMemory
();
// update and save the state
this
.
tumblingState
.
TumblerUri
=
serverAddress
;
this
.
tumblingState
.
TumblerParameters
=
this
.
TumblerParameters
;
this
.
tumblingState
.
SetClients
(
this
.
tumblerService
);
this
.
tumblingState
.
Save
();
return
this
.
TumblerParameters
;
}
/// <inheritdoc />
public
Task
TumbleAsync
(
string
destinationWalletName
)
public
Task
TumbleAsync
(
string
originWalletName
,
string
destinationWalletName
)
{
// make sure the tumbler service is initialized
if
(
this
.
TumblerParameters
==
null
||
this
.
tumblerService
==
null
)
...
...
@@ -81,11 +78,21 @@ namespace Breeze.TumbleBit.Client
Wallet
destinationWallet
=
this
.
walletManager
.
GetWallet
(
destinationWalletName
);
if
(
destinationWallet
==
null
)
{
throw
new
Exception
(
$"Destination not found. Have you created a wallet with name
{
destinationWalletName
}
?"
);
throw
new
Exception
(
$"Destination wallet not found. Have you created a wallet with name
{
destinationWalletName
}
?"
);
}
Wallet
originWallet
=
this
.
walletManager
.
GetWallet
(
originWalletName
);
if
(
originWallet
==
null
)
{
throw
new
Exception
(
$"Origin wallet not found. Have you created a wallet with name
{
originWalletName
}
?"
);
}
// update the state and save
this
.
tumblingState
.
DestinationWallet
=
destinationWallet
;
this
.
tumblingState
.
DestinationWalletName
=
destinationWalletName
;
this
.
tumblingState
.
OriginWallet
=
originWallet
;
this
.
tumblingState
.
OriginWalletName
=
originWalletName
;
this
.
tumblingState
.
Save
();
// subscribe to receiving blocks
...
...
@@ -113,30 +120,13 @@ namespace Breeze.TumbleBit.Client
/// <inheritdoc />
public
void
ProcessBlock
(
int
height
,
Block
block
)
{
// TODO start the state machine
this
.
logger
.
LogDebug
(
$"Receive block with height
{
height
}
"
);
{
this
.
logger
.
LogDebug
(
$"Received block with height
{
height
}
during tumbling session."
);
// update the block height in the tumbling state
this
.
tumblingState
.
LastBlockReceivedHeight
=
height
;
this
.
tumblingState
.
Save
();
// get the next cycle to be started
var
cycle
=
this
.
TumblerParameters
.
CycleGenerator
.
GetRegistratingCycle
(
height
);
// check if we need to start a new session starting from the registration cycle
if
(
this
.
lastCycleStarted
!=
cycle
.
Start
)
{
this
.
lastCycleStarted
=
cycle
.
Start
;
this
.
logger
.
LogDebug
(
$"new registration cycle at
{
cycle
.
Start
}
"
);
if
(
this
.
tumblingState
.
Sessions
.
SingleOrDefault
(
s
=>
s
.
StartCycle
==
cycle
.
Start
)
==
null
)
{
this
.
tumblingState
.
CreateNewSession
(
cycle
.
Start
);
this
.
logger
.
LogDebug
(
$"new session created at
{
cycle
.
Start
}
"
);
}
}
// update the state of the tumbling session in this new block
this
.
tumblingState
.
Update
();
}
...
...
Breeze/src/Breeze.TumbleBit.Client/TumblerService.cs
View file @
398ec79f
...
...
@@ -110,5 +110,11 @@ namespace Breeze.TumbleBit.Client
SolutionKey
[]
result
=
await
this
.
serverAddress
.
AppendPathSegment
(
$"api/v1/tumblers/0/clientchannels/
{
cycleId
}
/
{
channelId
}
/offer"
).
PostJsonAsync
(
clientSignature
).
ReceiveJson
<
SolutionKey
[
]>
();
return
result
;
}
/// <inheritdoc />
public
async
Task
GiveEscapeKeyAsync
(
int
cycleId
,
string
channelId
,
TransactionSignature
signature
)
{
await
this
.
serverAddress
.
AppendPathSegment
(
$"api/v1/tumblers/0/clientchannels/
{
cycleId
}
/
{
channelId
}
/escape"
).
PostJsonAsync
(
signature
);
}
}
}
\ No newline at end of file
Breeze/src/Breeze.TumbleBit.Client/TumblingState.cs
View file @
398ec79f
...
...
@@ -4,34 +4,74 @@ using System.IO;
using
System.Linq
;
using
System.Runtime.InteropServices
;
using
Microsoft.Extensions.Logging
;
using
NBitcoin
;
using
Newtonsoft.Json
;
using
NTumbleBit.ClassicTumbler
;
using
NTumbleBit.PuzzlePromise
;
using
NTumbleBit.PuzzleSolver
;
using
Stratis.Bitcoin.Wallet
;
namespace
Breeze.TumbleBit.Client
{
public
class
TumblingState
:
IStateMachine
public
partial
class
TumblingState
:
IStateMachine
{
private
const
string
StateFileName
=
"tumblebit_state.json"
;
private
readonly
ILogger
logger
;
private
readonly
ConcurrentChain
chain
;
private
readonly
IWalletManager
walletManager
;
private
readonly
CoinType
coinType
;
[
JsonProperty
(
"tumblerParameters"
)]
public
ClassicTumblerParameters
TumblerParameters
{
get
;
set
;
}
[
JsonProperty
(
"tumblerUri"
)]
public
Uri
TumblerUri
{
get
;
set
;
}
[
JsonProperty
(
"lastBlockReceivedHeight"
)]
[
JsonProperty
(
"lastBlockReceivedHeight"
,
DefaultValueHandling
=
DefaultValueHandling
.
Ignore
)]
public
int
LastBlockReceivedHeight
{
get
;
set
;
}
[
JsonProperty
(
"destinationWalletName"
)]
[
JsonProperty
(
"originWalletName"
,
NullValueHandling
=
NullValueHandling
.
Ignore
)]
public
string
OriginWalletName
{
get
;
set
;
}
[
JsonProperty
(
"destinationWalletName"
,
NullValueHandling
=
NullValueHandling
.
Ignore
)]
public
string
DestinationWalletName
{
get
;
set
;
}
[
JsonProperty
(
"sessions"
,
NullValueHandling
=
NullValueHandling
.
Ignore
)]
public
IList
<
Session
>
Sessions
{
get
;
set
;
}
[
JsonIgnore
]
public
Wallet
OriginWallet
{
get
;
set
;
}
[
JsonIgnore
]
public
Wallet
DestinationWallet
{
get
;
set
;
}
[
JsonIgnore
]
public
ITumblerService
AliceClient
{
get
;
set
;
}
[
JsonIgnore
]
public
ITumblerService
BobClient
{
get
;
set
;
}
[
JsonConstructor
]
public
TumblingState
()
{
this
.
Sessions
=
new
List
<
Session
>();
}
public
TumblingState
(
ILoggerFactory
loggerFactory
,
ConcurrentChain
chain
,
IWalletManager
walletManager
,
Network
network
)
{
this
.
logger
=
loggerFactory
.
CreateLogger
(
this
.
GetType
().
FullName
);
this
.
chain
=
chain
;
this
.
walletManager
=
walletManager
;
this
.
coinType
=
(
CoinType
)
network
.
Consensus
.
CoinType
;
}
public
void
SetClients
(
ITumblerService
tumblerService
)
{
this
.
AliceClient
=
tumblerService
;
this
.
BobClient
=
tumblerService
;
}
/// <inheritdoc />
...
...
@@ -41,9 +81,23 @@ namespace Breeze.TumbleBit.Client
}
/// <inheritdoc />
public
IStateMachine
Load
()
public
void
LoadStateFromMemory
()
{
return
LoadState
();
var
stateFilePath
=
GetStateFilePath
();
if
(!
File
.
Exists
(
stateFilePath
))
{
return
;
}
// load the file from the local system
var
savedState
=
JsonConvert
.
DeserializeObject
<
TumblingState
>(
File
.
ReadAllText
(
stateFilePath
));
this
.
Sessions
=
savedState
.
Sessions
??
new
List
<
Session
>();
this
.
OriginWalletName
=
savedState
.
OriginWalletName
;
this
.
DestinationWalletName
=
savedState
.
DestinationWalletName
;
this
.
LastBlockReceivedHeight
=
savedState
.
LastBlockReceivedHeight
;
this
.
TumblerParameters
=
savedState
.
TumblerParameters
;
this
.
TumblerUri
=
savedState
.
TumblerUri
;
}
/// <inheritdoc />
...
...
@@ -56,67 +110,70 @@ namespace Breeze.TumbleBit.Client
/// <inheritdoc />
public
void
Update
()
{
// get the next cycle to be started
var
cycle
=
this
.
TumblerParameters
.
CycleGenerator
.
GetRegistratingCycle
(
this
.
LastBlockReceivedHeight
);
// create a new session if allowed
if
(
this
.
Sessions
.
Count
==
0
)
{
this
.
CreateNewSession
(
cycle
.
Start
);
}
else
{
// TODO remove the limitation to have only 1 session
//var lastCycleStarted = this.Sessions.Max(s => s.StartCycle);
//// check if we need to start a new session starting from the registration cycle
//if (lastCycleStarted != cycle.Start)
//{
// if (this.Sessions.SingleOrDefault(s => s.StartCycle == cycle.Start) == null)
// {
// this.CreateNewSession(cycle.Start);
// }
//}
}
// get a list of cycles we expect to have at this height
var
cycles
=
this
.
TumblerParameters
.
CycleGenerator
.
GetCycles
(
this
.
LastBlockReceivedHeight
);
var
states
=
cycles
.
SelectMany
(
c
=>
this
.
Sessions
.
Where
(
s
=>
s
.
StartCycle
==
c
.
Start
)).
ToList
();
foreach
(
var
state
in
states
)
var
existingSessions
=
cycles
.
SelectMany
(
c
=>
this
.
Sessions
.
Where
(
s
=>
s
.
StartCycle
==
c
.
Start
)).
ToList
();
foreach
(
var
existingSession
in
existingSessions
)
{
try
// create a new session to be updated
var
session
=
new
Session
();
if
(
existingSession
.
NegotiationClientState
!=
null
)
{
// create a new session to be updated
var
session
=
new
Session
();
if
(
state
.
NegotiationClientState
!=
null
)
{
session
.
StartCycle
=
state
.
NegotiationClientState
.
CycleStart
;
session
.
ClientChannelNegotiation
=
new
ClientChannelNegotiation
(
this
.
TumblerParameters
,
state
.
NegotiationClientState
);
}
if
(
state
.
PromiseClientState
!=
null
)
session
.
PromiseClientSession
=
new
PromiseClientSession
(
this
.
TumblerParameters
.
CreatePromiseParamaters
(),
state
.
PromiseClientState
);
if
(
state
.
SolverClientState
!=
null
)
session
.
SolverClientSession
=
new
SolverClientSession
(
this
.
TumblerParameters
.
CreateSolverParamaters
(),
state
.
SolverClientState
);
// update the session
session
.
Update
();
// replace the updated session in the list of existing sessions
int
index
=
this
.
Sessions
.
IndexOf
(
state
);
if
(
index
!=
-
1
)
{
this
.
Sessions
[
index
]
=
session
;
}
this
.
Save
();
session
.
StartCycle
=
existingSession
.
NegotiationClientState
.
CycleStart
;
session
.
ClientChannelNegotiation
=
new
ClientChannelNegotiation
(
this
.
TumblerParameters
,
existingSession
.
NegotiationClientState
);
}
catch
(
Exception
)
if
(
existingSession
.
PromiseClientState
!=
null
)
session
.
PromiseClientSession
=
new
PromiseClientSession
(
this
.
TumblerParameters
.
CreatePromiseParamaters
(),
existingSession
.
PromiseClientState
);
if
(
existingSession
.
SolverClientState
!=
null
)
session
.
SolverClientSession
=
new
SolverClientSession
(
this
.
TumblerParameters
.
CreateSolverParamaters
(),
existingSession
.
SolverClientState
);
// update the session
this
.
MoveToNextPhase
(
session
);
// replace the updated session in the list of existing sessions
int
index
=
this
.
Sessions
.
IndexOf
(
existingSession
);
if
(
index
!=
-
1
)
{
th
row
;
th
is
.
Sessions
[
index
]
=
session
;
}
this
.
Save
();
}
}
public
void
MoveToNextPhase
(
Session
session
)
{
this
.
logger
.
LogInformation
(
$"Entering next phase for cycle
{
session
.
StartCycle
}
."
);
}
public
void
CreateNewSession
(
int
start
)
{
this
.
Sessions
.
Add
(
new
Session
{
StartCycle
=
start
});
this
.
Save
();
}
/// <summary>
/// Loads the saved state of the tumbling execution to the file system.
/// </summary>
/// <returns></returns>
public
static
TumblingState
LoadState
()
{
var
stateFilePath
=
GetStateFilePath
();
if
(!
File
.
Exists
(
stateFilePath
))
{
return
null
;
}
// load the file from the local system
return
JsonConvert
.
DeserializeObject
<
TumblingState
>(
File
.
ReadAllText
(
stateFilePath
));
}
/// <summary>
/// Gets the file path of the file containing the state of the tumbling execution.
...
...
@@ -152,15 +209,13 @@ namespace Breeze.TumbleBit.Client
public
SolverClientSession
.
State
SolverClientState
{
get
;
set
;
}
[
JsonIgnore
]
public
ClientChannelNegotiation
ClientChannelNegotiation
{
get
;
set
;
}
[
JsonIgnore
]
public
SolverClientSession
SolverClientSession
{
get
;
set
;
}
[
JsonIgnore
]
public
PromiseClientSession
PromiseClientSession
{
get
;
set
;
}
public
void
Update
()
{
}
}
}
Breeze/src/NTumbleBit/EscrowInitiator.cs
View file @
398ec79f
...
...
@@ -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
...
...
Breeze/src/NTumbleBit/Extensions.cs
View file @
398ec79f
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
;
}
}
}
Breeze/src/NTumbleBit/PuzzlePromise/PromiseClientSession.cs
View file @
398ec79f
...
...
@@ -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
;
...
...
Breeze/src/NTumbleBit/PuzzleSolver/SolverClientSession.cs
View file @
398ec79f
...
...
@@ -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
;
}
...
...
Breeze/src/NTumbleBit/PuzzleSolver/SolverServerSession.cs
View file @
398ec79f
...
...
@@ -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
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment