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
c5fa0532
Commit
c5fa0532
authored
Apr 25, 2017
by
Jeremy Bokobza
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added creation of accounts for a wallet
parent
bbec71c8
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
330 additions
and
184 deletions
+330
-184
ControllersTests.cs
Breeze/src/Breeze.Api.Tests/ControllersTests.cs
+26
-26
WalletController.cs
Breeze/src/Breeze.Wallet/Controllers/WalletController.cs
+26
-0
IWalletManager.cs
Breeze/src/Breeze.Wallet/IWalletManager.cs
+16
-2
CreateAccountModel.cs
Breeze/src/Breeze.Wallet/Models/CreateAccountModel.cs
+22
-0
Wallet.cs
Breeze/src/Breeze.Wallet/Wallet.cs
+146
-4
WalletHierarchy.cs
Breeze/src/Breeze.Wallet/WalletHierarchy.cs
+0
-143
WalletManager.cs
Breeze/src/Breeze.Wallet/WalletManager.cs
+94
-9
No files found.
Breeze/src/Breeze.Api.Tests/ControllersTests.cs
View file @
c5fa0532
using
System.Collections.Generic
;
using
System
;
using
System.Collections.Generic
;
using
System.IO
;
using
System.IO
;
using
Microsoft.AspNetCore.Mvc
;
using
Microsoft.AspNetCore.Mvc
;
using
Xunit
;
using
Xunit
;
using
Moq
;
using
Moq
;
using
Breeze.Wallet.Wrappers
;
using
Breeze.Wallet
;
using
Breeze.Wallet
;
using
Breeze.Wallet.Controllers
;
using
Breeze.Wallet.Controllers
;
using
Breeze.Wallet.Errors
;
using
Breeze.Wallet.Errors
;
using
Breeze.Wallet.Helpers
;
using
Breeze.Wallet.Models
;
using
Breeze.Wallet.Models
;
using
NBitcoin
;
namespace
Breeze.Api.Tests
namespace
Breeze.Api.Tests
{
{
...
@@ -16,8 +18,9 @@ namespace Breeze.Api.Tests
...
@@ -16,8 +18,9 @@ namespace Breeze.Api.Tests
[
Fact
]
[
Fact
]
public
void
CreateWalletSuccessfullyReturnsMnemonic
()
public
void
CreateWalletSuccessfullyReturnsMnemonic
()
{
{
var
mockWalletCreate
=
new
Mock
<
IWalletWrapper
>();
Mnemonic
mnemonic
=
new
Mnemonic
(
Wordlist
.
English
,
WordCount
.
Twelve
);
mockWalletCreate
.
Setup
(
wallet
=>
wallet
.
Create
(
It
.
IsAny
<
string
>(),
It
.
IsAny
<
string
>(),
It
.
IsAny
<
string
>(),
It
.
IsAny
<
string
>())).
Returns
(
"mnemonic"
);
var
mockWalletCreate
=
new
Mock
<
IWalletManager
>();
mockWalletCreate
.
Setup
(
wallet
=>
wallet
.
CreateWallet
(
It
.
IsAny
<
string
>(),
It
.
IsAny
<
string
>(),
It
.
IsAny
<
string
>(),
It
.
IsAny
<
string
>(),
null
,
CoinType
.
Bitcoin
)).
Returns
(
mnemonic
);
var
controller
=
new
WalletController
(
mockWalletCreate
.
Object
);
var
controller
=
new
WalletController
(
mockWalletCreate
.
Object
);
...
@@ -33,32 +36,31 @@ namespace Breeze.Api.Tests
...
@@ -33,32 +36,31 @@ namespace Breeze.Api.Tests
// Assert
// Assert
mockWalletCreate
.
VerifyAll
();
mockWalletCreate
.
VerifyAll
();
var
viewResult
=
Assert
.
IsType
<
JsonResult
>(
result
);
var
viewResult
=
Assert
.
IsType
<
JsonResult
>(
result
);
Assert
.
Equal
(
"mnemonic"
,
viewResult
.
Value
);
Assert
.
Equal
(
mnemonic
.
ToString
()
,
viewResult
.
Value
);
Assert
.
NotNull
(
result
);
Assert
.
NotNull
(
result
);
}
}
[
Fact
]
[
Fact
]
public
void
LoadWalletSuccessfullyReturnsWalletModel
()
public
void
LoadWalletSuccessfullyReturnsWalletModel
()
{
{
Wallet
Model
walletModel
=
new
WalletModel
Wallet
.
Wallet
wallet
=
new
Wallet
.
Wallet
{
{
FileName
=
"myWallet"
,
Name
=
"myWallet"
,
Network
=
"MainNet"
,
Network
=
WalletHelpers
.
GetNetwork
(
"mainnet"
)
Addresses
=
new
List
<
string
>
{
"address1"
,
"address2"
,
"address3"
,
"address4"
,
"address5"
}
};
};
var
mockWalletWrapper
=
new
Mock
<
IWalletWrapper
>();
mockWalletWrapper
.
Setup
(
wallet
=>
wallet
.
Recover
(
It
.
IsAny
<
string
>(),
It
.
IsAny
<
string
>(),
It
.
IsAny
<
string
>(),
It
.
IsAny
<
string
>(),
It
.
IsAny
<
string
>())).
Returns
(
walletModel
);
var
mockWalletWrapper
=
new
Mock
<
IWalletManager
>();
mockWalletWrapper
.
Setup
(
w
=>
w
.
RecoverWallet
(
It
.
IsAny
<
string
>(),
It
.
IsAny
<
string
>(),
It
.
IsAny
<
string
>(),
It
.
IsAny
<
string
>(),
It
.
IsAny
<
string
>(),
null
,
CoinType
.
Bitcoin
,
null
)).
Returns
(
wallet
);
var
controller
=
new
WalletController
(
mockWalletWrapper
.
Object
);
var
controller
=
new
WalletController
(
mockWalletWrapper
.
Object
);
// Act
// Act
var
result
=
controller
.
Recover
(
new
WalletRecoveryRequest
var
result
=
controller
.
Recover
(
new
WalletRecoveryRequest
{
{
Name
=
"my
Name
"
,
Name
=
"my
Wallet
"
,
FolderPath
=
""
,
FolderPath
=
""
,
Password
=
""
,
Password
=
""
,
Network
=
""
,
Network
=
"
MainNet
"
,
Mnemonic
=
"mnemonic"
Mnemonic
=
"mnemonic"
});
});
...
@@ -69,28 +71,26 @@ namespace Breeze.Api.Tests
...
@@ -69,28 +71,26 @@ namespace Breeze.Api.Tests
Assert
.
IsType
<
WalletModel
>(
viewResult
.
Value
);
Assert
.
IsType
<
WalletModel
>(
viewResult
.
Value
);
var
model
=
viewResult
.
Value
as
WalletModel
;
var
model
=
viewResult
.
Value
as
WalletModel
;
Assert
.
Equal
(
"
myWallet"
,
model
.
FileName
);
Assert
.
Equal
(
"
Main"
,
model
.
Network
);
}
}
[
Fact
]
[
Fact
]
public
void
RecoverWalletSuccessfullyReturnsWalletModel
()
public
void
RecoverWalletSuccessfullyReturnsWalletModel
()
{
{
Wallet
Model
walletModel
=
new
WalletModel
Wallet
.
Wallet
wallet
=
new
Wallet
.
Wallet
{
{
FileName
=
"myWallet"
,
Name
=
"myWallet"
,
Network
=
"MainNet"
,
Network
=
WalletHelpers
.
GetNetwork
(
"mainnet"
)
Addresses
=
new
List
<
string
>
{
"address1"
,
"address2"
,
"address3"
,
"address4"
,
"address5"
}
};
};
var
mockWalletWrapper
=
new
Mock
<
IWallet
Wrapp
er
>();
var
mockWalletWrapper
=
new
Mock
<
IWallet
Manag
er
>();
mockWalletWrapper
.
Setup
(
w
allet
=>
wallet
.
Load
(
It
.
IsAny
<
string
>(),
It
.
IsAny
<
string
>(),
It
.
IsAny
<
string
>())).
Returns
(
walletModel
);
mockWalletWrapper
.
Setup
(
w
=>
w
.
LoadWallet
(
It
.
IsAny
<
string
>(),
It
.
IsAny
<
string
>(),
It
.
IsAny
<
string
>())).
Returns
(
wallet
);
var
controller
=
new
WalletController
(
mockWalletWrapper
.
Object
);
var
controller
=
new
WalletController
(
mockWalletWrapper
.
Object
);
// Act
// Act
var
result
=
controller
.
Load
(
new
WalletLoadRequest
var
result
=
controller
.
Load
(
new
WalletLoadRequest
{
{
Name
=
"my
Name
"
,
Name
=
"my
Wallet
"
,
FolderPath
=
""
,
FolderPath
=
""
,
Password
=
""
Password
=
""
});
});
...
@@ -102,14 +102,14 @@ namespace Breeze.Api.Tests
...
@@ -102,14 +102,14 @@ namespace Breeze.Api.Tests
Assert
.
IsType
<
WalletModel
>(
viewResult
.
Value
);
Assert
.
IsType
<
WalletModel
>(
viewResult
.
Value
);
var
model
=
viewResult
.
Value
as
WalletModel
;
var
model
=
viewResult
.
Value
as
WalletModel
;
Assert
.
Equal
(
"
myWallet"
,
model
.
FileName
);
Assert
.
Equal
(
"
Main"
,
model
.
Network
);
}
}
[
Fact
]
[
Fact
]
public
void
FileNotFoundExceptionandReturns404
()
public
void
FileNotFoundExceptionandReturns404
()
{
{
var
mockWalletWrapper
=
new
Mock
<
IWallet
Wrapp
er
>();
var
mockWalletWrapper
=
new
Mock
<
IWallet
Manag
er
>();
mockWalletWrapper
.
Setup
(
wallet
=>
wallet
.
Load
(
It
.
IsAny
<
string
>(),
It
.
IsAny
<
string
>(),
It
.
IsAny
<
string
>())).
Throws
<
FileNotFoundException
>();
mockWalletWrapper
.
Setup
(
wallet
=>
wallet
.
Load
Wallet
(
It
.
IsAny
<
string
>(),
It
.
IsAny
<
string
>(),
It
.
IsAny
<
string
>())).
Throws
<
FileNotFoundException
>();
var
controller
=
new
WalletController
(
mockWalletWrapper
.
Object
);
var
controller
=
new
WalletController
(
mockWalletWrapper
.
Object
);
...
...
Breeze/src/Breeze.Wallet/Controllers/WalletController.cs
View file @
c5fa0532
...
@@ -311,6 +311,32 @@ namespace Breeze.Wallet.Controllers
...
@@ -311,6 +311,32 @@ namespace Breeze.Wallet.Controllers
}
}
}
}
/// <summary>
/// Creates a new account for a wallet.
/// </summary>
/// <returns>An account name.</returns>
[
Route
(
"account"
)]
[
HttpPost
]
public
IActionResult
CreateNewAccount
([
FromBody
]
CreateAccountModel
request
)
{
// checks the request is valid
if
(!
this
.
ModelState
.
IsValid
)
{
var
errors
=
this
.
ModelState
.
Values
.
SelectMany
(
e
=>
e
.
Errors
.
Select
(
m
=>
m
.
ErrorMessage
));
return
ErrorHelpers
.
BuildErrorResponse
(
HttpStatusCode
.
BadRequest
,
"Formatting error"
,
string
.
Join
(
Environment
.
NewLine
,
errors
));
}
try
{
var
result
=
this
.
walletManager
.
CreateNewAccount
(
request
.
WalletName
,
request
.
AccountName
);
return
this
.
Json
(
result
);
}
catch
(
Exception
e
)
{
return
ErrorHelpers
.
BuildErrorResponse
(
HttpStatusCode
.
BadRequest
,
e
.
Message
,
e
.
ToString
());
}
}
/// <summary>
/// <summary>
/// Gets a folder.
/// Gets a folder.
/// </summary>
/// </summary>
...
...
Breeze/src/Breeze.Wallet/IWalletManager.cs
View file @
c5fa0532
...
@@ -17,8 +17,9 @@ namespace Breeze.Wallet
...
@@ -17,8 +17,9 @@ namespace Breeze.Wallet
/// <param name="name">The name of the wallet.</param>
/// <param name="name">The name of the wallet.</param>
/// <param name="network">The network this wallet is for.</param>
/// <param name="network">The network this wallet is for.</param>
/// <param name="passphrase">The passphrase used in the seed.</param>
/// <param name="passphrase">The passphrase used in the seed.</param>
/// <param name="coinType">The type of coin this wallet will contain.</param>
/// <returns>A mnemonic defining the wallet's seed used to generate addresses.</returns>
/// <returns>A mnemonic defining the wallet's seed used to generate addresses.</returns>
Mnemonic
CreateWallet
(
string
password
,
string
folderPath
,
string
name
,
string
network
,
string
passphrase
=
null
);
Mnemonic
CreateWallet
(
string
password
,
string
folderPath
,
string
name
,
string
network
,
string
passphrase
=
null
,
CoinType
coinType
=
CoinType
.
Bitcoin
);
/// <summary>
/// <summary>
/// Loads a wallet from a file.
/// Loads a wallet from a file.
...
@@ -38,9 +39,10 @@ namespace Breeze.Wallet
...
@@ -38,9 +39,10 @@ namespace Breeze.Wallet
/// <param name="network">The network in which to creae this wallet</param>
/// <param name="network">The network in which to creae this wallet</param>
/// <param name="mnemonic">The user's mnemonic for the wallet.</param>
/// <param name="mnemonic">The user's mnemonic for the wallet.</param>
/// <param name="passphrase">The passphrase used in the seed.</param>
/// <param name="passphrase">The passphrase used in the seed.</param>
/// <param name="coinType">The type of coin this wallet will contain.</param>
/// <param name="creationTime">The time this wallet was created.</param>
/// <param name="creationTime">The time this wallet was created.</param>
/// <returns>The recovered wallet.</returns>
/// <returns>The recovered wallet.</returns>
Wallet
RecoverWallet
(
string
password
,
string
folderPath
,
string
name
,
string
network
,
string
mnemonic
,
string
passphrase
=
null
,
DateTimeOffset
?
creationTime
=
null
);
Wallet
RecoverWallet
(
string
password
,
string
folderPath
,
string
name
,
string
network
,
string
mnemonic
,
string
passphrase
=
null
,
CoinType
coinType
=
CoinType
.
Bitcoin
,
DateTimeOffset
?
creationTime
=
null
);
/// <summary>
/// <summary>
/// Deleted a wallet.
/// Deleted a wallet.
...
@@ -48,6 +50,18 @@ namespace Breeze.Wallet
...
@@ -48,6 +50,18 @@ namespace Breeze.Wallet
/// <param name="walletFilePath">The location of the wallet file.</param>
/// <param name="walletFilePath">The location of the wallet file.</param>
void
DeleteWallet
(
string
walletFilePath
);
void
DeleteWallet
(
string
walletFilePath
);
/// <summary>
/// Creates a new account.
/// </summary>
/// <param name="walletName">The name of the wallet in which this account will be created.</param>
/// <param name="accountName">The name by which this account will be identified.</param>
/// <remarks>
/// According to BIP44, an account at index (i) can only be created when the account
/// at index (i - 1) contains transactions.
/// </remarks>
/// <returns>The name of the new account.</returns>
string
CreateNewAccount
(
string
walletName
,
string
accountName
);
WalletGeneralInfoModel
GetGeneralInfo
(
string
walletName
);
WalletGeneralInfoModel
GetGeneralInfo
(
string
walletName
);
WalletBalanceModel
GetBalance
(
string
walletName
);
WalletBalanceModel
GetBalance
(
string
walletName
);
...
...
Breeze/src/Breeze.Wallet/Models/CreateAccountModel.cs
0 → 100644
View file @
c5fa0532
using
System
;
using
System.Collections.Generic
;
using
System.ComponentModel.DataAnnotations
;
using
System.Text
;
namespace
Breeze.Wallet.Models
{
public
class
CreateAccountModel
{
/// <summary>
/// The name of the wallet in which to create the account.
/// </summary>
[
Required
]
public
string
WalletName
{
get
;
set
;
}
/// <summary>
/// The name of the account.
/// </summary>
[
Required
]
public
string
AccountName
{
get
;
set
;
}
}
}
Breeze/src/Breeze.Wallet/Wallet.cs
View file @
c5fa0532
using
System
;
using
System
;
using
System.Collections.Generic
;
using
Breeze.Wallet.JsonConverters
;
using
Breeze.Wallet.JsonConverters
;
using
NBitcoin
;
using
NBitcoin
;
using
Newtonsoft.Json
;
using
Newtonsoft.Json
;
...
@@ -10,6 +11,11 @@ namespace Breeze.Wallet
...
@@ -10,6 +11,11 @@ namespace Breeze.Wallet
/// </summary>
/// </summary>
public
class
Wallet
public
class
Wallet
{
{
/// <summary>
/// The name of this wallet.
/// </summary>
[
JsonProperty
(
PropertyName
=
"name"
)]
public
string
Name
{
get
;
set
;
}
/// <summary>
/// <summary>
/// The seed for this wallet, password encrypted.
/// The seed for this wallet, password encrypted.
...
@@ -41,13 +47,149 @@ namespace Breeze.Wallet
...
@@ -41,13 +47,149 @@ namespace Breeze.Wallet
/// <summary>
/// <summary>
/// The location of the wallet file on the local system.
/// The location of the wallet file on the local system.
/// </summary>
/// </summary>
[
Json
Ignore
]
[
Json
Property
(
PropertyName
=
"walletFilePath"
)
]
public
string
WalletFilePath
{
get
;
set
;
}
public
string
WalletFilePath
{
get
;
set
;
}
/// <summary>
/// <summary>
/// The hierarchy of the wallet's accounts and addresses.
/// The type of coin, Bitcoin or Stratis.
/// </summary>
[
JsonProperty
(
PropertyName
=
"coinType"
)]
public
CoinType
CoinType
{
get
;
set
;
}
/// <summary>
/// The accounts used in the wallet.
/// </summary>
[
JsonProperty
(
PropertyName
=
"accounts"
)]
public
IEnumerable
<
HdAccount
>
Accounts
{
get
;
set
;
}
}
/// <summary>
/// The type of coin, as specified in BIP44.
/// </summary>
public
enum
CoinType
{
Bitcoin
=
0
,
Stratis
=
105
}
/// <summary>
/// An Hd account's details.
/// </summary>
public
class
HdAccount
{
/// <summary>
/// The index of the account.
/// </summary>
/// <remarks>
/// According to BIP44, an account at index (i) can only be created when the account
/// at index (i - 1) contains transactions.
/// </remarks>
[
JsonProperty
(
PropertyName
=
"index"
)]
public
int
Index
{
get
;
set
;
}
/// <summary>
/// The name of this account.
/// </summary>
/// </summary>
[
JsonProperty
(
PropertyName
=
"hierarchy"
)]
[
JsonProperty
(
PropertyName
=
"name"
)]
public
WalletHierarchy
Hierarchy
{
get
;
set
;
}
public
string
Name
{
get
;
set
;
}
/// <summary>
/// Gets or sets the creation time.
/// </summary>
[
JsonProperty
(
PropertyName
=
"creationTime"
)]
[
JsonConverter
(
typeof
(
DateTimeOffsetConverter
))]
public
DateTimeOffset
CreationTime
{
get
;
set
;
}
/// <summary>
/// The list of external addresses, typically used for receiving money.
/// </summary>
[
JsonProperty
(
PropertyName
=
"externalAddresses"
)]
public
IEnumerable
<
HdAddress
>
ExternalAddresses
{
get
;
set
;
}
/// <summary>
/// The list of internal addresses, typically used to receive change.
/// </summary>
[
JsonProperty
(
PropertyName
=
"internalAddresses"
)]
public
IEnumerable
<
HdAddress
>
InternalAddresses
{
get
;
set
;
}
}
/// <summary>
/// An Hd address.
/// </summary>
public
class
HdAddress
{
/// <summary>
/// Gets or sets the creation time.
/// </summary>
[
JsonProperty
(
PropertyName
=
"creationTime"
)]
[
JsonConverter
(
typeof
(
DateTimeOffsetConverter
))]
public
DateTimeOffset
CreationTime
{
get
;
set
;
}
/// <summary>
/// The script pub key for this address.
/// </summary>
[
JsonProperty
(
PropertyName
=
"scriptPubKey"
)]
public
Script
ScriptPubKey
{
get
;
set
;
}
/// <summary>
/// The Base58 representation of this address.
/// </summary>
[
JsonProperty
(
PropertyName
=
"address"
)]
public
BitcoinAddress
Address
{
get
;
set
;
}
/// <summary>
/// A path to the address as defined in BIP44.
/// </summary>
[
JsonProperty
(
PropertyName
=
"hdPath"
)]
public
string
HdPath
{
get
;
set
;
}
/// <summary>
/// A list detailing which blocks have been scanned for this address.
/// </summary>
[
JsonIgnore
]
public
SortedList
<
int
,
int
>
BlocksScanned
{
get
;
set
;
}
/// <summary>
/// A list of transactions involving this address.
/// </summary>
[
JsonProperty
(
PropertyName
=
"transactions"
)]
public
IEnumerable
<
TransactionData
>
Transactions
{
get
;
set
;
}
}
/// <summary>
/// An object containing transaction data.
/// </summary>
public
class
TransactionData
{
/// <summary>
/// Transaction id.
/// </summary>
[
JsonProperty
(
PropertyName
=
"id"
)]
public
string
Id
{
get
;
set
;
}
/// <summary>
/// The transaction amount.
/// </summary>
[
JsonProperty
(
PropertyName
=
"amount"
)]
public
Money
Amount
{
get
;
set
;
}
/// <summary>
/// The height of the block including this transaction.
/// </summary>
[
JsonProperty
(
PropertyName
=
"blockHeight"
)]
public
int
BlockHeight
{
get
;
set
;
}
/// <summary>
/// Whether this transaction has been confirmed or not.
/// </summary>
[
JsonProperty
(
PropertyName
=
"confirmed"
)]
public
bool
Confirmed
{
get
;
set
;
}
/// <summary>
/// Gets or sets the creation time.
/// </summary>
[
JsonProperty
(
PropertyName
=
"creationTime"
)]
[
JsonConverter
(
typeof
(
DateTimeOffsetConverter
))]
public
DateTimeOffset
CreationTime
{
get
;
set
;
}
}
}
}
}
\ No newline at end of file
Breeze/src/Breeze.Wallet/WalletHierarchy.cs
deleted
100644 → 0
View file @
bbec71c8
using
System
;
using
System.Collections.Generic
;
using
Breeze.Wallet.JsonConverters
;
using
NBitcoin
;
using
Newtonsoft.Json
;
namespace
Breeze.Wallet
{
/// <summary>
/// Represents the root of the user's wallet's addresses and transactions.
/// </summary>
public
class
WalletHierarchy
{
/// <summary>
/// The type of coin, Bitcoin or Stratis.
/// </summary>
[
JsonProperty
(
PropertyName
=
"coinType"
)]
public
CoinType
CoinType
{
get
;
set
;
}
/// <summary>
/// The accounts used in the wallet.
/// </summary>
[
JsonProperty
(
PropertyName
=
"accounts"
)]
public
IEnumerable
<
HdAccount
>
Accounts
{
get
;
set
;
}
}
/// <summary>
/// The type of coin, as specified in BIP44.
/// </summary>
public
enum
CoinType
{
Bitcoin
=
0
,
Stratis
=
105
}
/// <summary>
/// An Hd account's details.
/// </summary>
public
class
HdAccount
{
/// <summary>
/// The index of the account.
/// </summary>
/// <remarks>
/// According to BIP44, an account at index (i) can only be created when the account
/// at index (i - 1) contains transactions.
/// </remarks>
[
JsonProperty
(
PropertyName
=
"index"
)]
public
int
Index
{
get
;
set
;
}
/// <summary>
/// The list of external addresses, typically used for receiving money.
/// </summary>
[
JsonProperty
(
PropertyName
=
"externalAddresses"
)]
public
IEnumerable
<
HdAddress
>
ExternalAddresses
{
get
;
set
;
}
/// <summary>
/// The list of internal addresses, typically used to receive change.
/// </summary>
[
JsonProperty
(
PropertyName
=
"internalAddresses"
)]
public
IEnumerable
<
HdAddress
>
InternalAddresses
{
get
;
set
;
}
}
/// <summary>
/// An Hd address.
/// </summary>
public
class
HdAddress
{
/// <summary>
/// Gets or sets the creation time.
/// </summary>
[
JsonProperty
(
PropertyName
=
"creationTime"
)]
[
JsonConverter
(
typeof
(
DateTimeOffsetConverter
))]
public
DateTimeOffset
CreationTime
{
get
;
set
;
}
/// <summary>
/// The script pub key for this address.
/// </summary>
[
JsonProperty
(
PropertyName
=
"scriptPubKey"
)]
public
Script
ScriptPubKey
{
get
;
set
;
}
/// <summary>
/// The Base58 representation of this address.
/// </summary>
[
JsonProperty
(
PropertyName
=
"address"
)]
public
BitcoinAddress
Address
{
get
;
set
;
}
/// <summary>
/// A path to the address as defined in BIP44.
/// </summary>
[
JsonProperty
(
PropertyName
=
"hdPath"
)]
public
string
HdPath
{
get
;
set
;
}
/// <summary>
/// A list detailing which blocks have been scanned for this address.
/// </summary>
[
JsonIgnore
]
public
SortedList
<
int
,
int
>
BlocksScanned
{
get
;
set
;
}
/// <summary>
/// A list of transactions involving this address.
/// </summary>
[
JsonProperty
(
PropertyName
=
"transactions"
)]
public
IEnumerable
<
TransactionData
>
Transactions
{
get
;
set
;
}
}
/// <summary>
/// An object containing transaction data.
/// </summary>
public
class
TransactionData
{
/// <summary>
/// Transaction id.
/// </summary>
[
JsonProperty
(
PropertyName
=
"id"
)]
public
string
Id
{
get
;
set
;
}
/// <summary>
/// The transaction amount.
/// </summary>
[
JsonProperty
(
PropertyName
=
"amount"
)]
public
Money
Amount
{
get
;
set
;
}
/// <summary>
/// The height of the block including this transaction.
/// </summary>
[
JsonProperty
(
PropertyName
=
"blockHeight"
)]
public
int
BlockHeight
{
get
;
set
;
}
/// <summary>
/// Whether this transaction has been confirmed or not.
/// </summary>
[
JsonProperty
(
PropertyName
=
"confirmed"
)]
public
bool
Confirmed
{
get
;
set
;
}
/// <summary>
/// Gets or sets the creation time.
/// </summary>
[
JsonProperty
(
PropertyName
=
"creationTime"
)]
[
JsonConverter
(
typeof
(
DateTimeOffsetConverter
))]
public
DateTimeOffset
CreationTime
{
get
;
set
;
}
}
}
Breeze/src/Breeze.Wallet/WalletManager.cs
View file @
c5fa0532
using
System
;
using
System
;
using
System.Collections.Generic
;
using
System.IO
;
using
System.IO
;
using
System.Linq
;
using
Breeze.Wallet.Helpers
;
using
Breeze.Wallet.Helpers
;
using
Breeze.Wallet.Models
;
using
Breeze.Wallet.Models
;
using
NBitcoin
;
using
NBitcoin
;
...
@@ -12,11 +14,16 @@ namespace Breeze.Wallet
...
@@ -12,11 +14,16 @@ namespace Breeze.Wallet
/// </summary>
/// </summary>
public
class
WalletManager
:
IWalletManager
public
class
WalletManager
:
IWalletManager
{
{
/// <inheritdoc />
public
List
<
Wallet
>
Wallets
{
get
;
}
public
Mnemonic
CreateWallet
(
string
password
,
string
folderPath
,
string
name
,
string
network
,
string
passphrase
=
null
)
public
WalletManager
()
{
{
string
walletFilePath
=
Path
.
Combine
(
folderPath
,
$"
{
name
}
.json"
);
this
.
Wallets
=
new
List
<
Wallet
>();
}
/// <inheritdoc />
public
Mnemonic
CreateWallet
(
string
password
,
string
folderPath
,
string
name
,
string
network
,
string
passphrase
=
null
,
CoinType
coinType
=
CoinType
.
Bitcoin
)
{
// for now the passphrase is set to be the password by default.
// for now the passphrase is set to be the password by default.
if
(
passphrase
==
null
)
if
(
passphrase
==
null
)
{
{
...
@@ -29,7 +36,9 @@ namespace Breeze.Wallet
...
@@ -29,7 +36,9 @@ namespace Breeze.Wallet
ExtKey
extendedKey
=
mnemonic
.
DeriveExtKey
(
passphrase
);
ExtKey
extendedKey
=
mnemonic
.
DeriveExtKey
(
passphrase
);
// create a wallet file
// create a wallet file
Wallet
wallet
=
this
.
GenerateWalletFile
(
password
,
walletFilePath
,
WalletHelpers
.
GetNetwork
(
network
),
extendedKey
);
Wallet
wallet
=
this
.
GenerateWalletFile
(
password
,
folderPath
,
name
,
WalletHelpers
.
GetNetwork
(
network
),
extendedKey
,
coinType
);
this
.
Load
(
wallet
);
return
mnemonic
;
return
mnemonic
;
}
}
...
@@ -43,11 +52,13 @@ namespace Breeze.Wallet
...
@@ -43,11 +52,13 @@ namespace Breeze.Wallet
// load the file from the local system
// load the file from the local system
Wallet
wallet
=
JsonConvert
.
DeserializeObject
<
Wallet
>(
File
.
ReadAllText
(
walletFilePath
));
Wallet
wallet
=
JsonConvert
.
DeserializeObject
<
Wallet
>(
File
.
ReadAllText
(
walletFilePath
));
this
.
Load
(
wallet
);
return
wallet
;
return
wallet
;
}
}
/// <inheritdoc />
/// <inheritdoc />
public
Wallet
RecoverWallet
(
string
password
,
string
folderPath
,
string
name
,
string
network
,
string
mnemonic
,
string
passphrase
=
null
,
DateTimeOffset
?
creationTime
=
null
)
public
Wallet
RecoverWallet
(
string
password
,
string
folderPath
,
string
name
,
string
network
,
string
mnemonic
,
string
passphrase
=
null
,
CoinType
coinType
=
CoinType
.
Bitcoin
,
DateTimeOffset
?
creationTime
=
null
)
{
{
// for now the passphrase is set to be the password by default.
// for now the passphrase is set to be the password by default.
if
(
passphrase
==
null
)
if
(
passphrase
==
null
)
...
@@ -59,10 +70,55 @@ namespace Breeze.Wallet
...
@@ -59,10 +70,55 @@ namespace Breeze.Wallet
ExtKey
extendedKey
=
(
new
Mnemonic
(
mnemonic
)).
DeriveExtKey
(
passphrase
);
ExtKey
extendedKey
=
(
new
Mnemonic
(
mnemonic
)).
DeriveExtKey
(
passphrase
);
// create a wallet file
// create a wallet file
Wallet
wallet
=
this
.
GenerateWalletFile
(
password
,
Path
.
Combine
(
folderPath
,
$"
{
name
}
.json"
),
WalletHelpers
.
GetNetwork
(
network
),
extendedKey
,
creationTime
);
Wallet
wallet
=
this
.
GenerateWalletFile
(
password
,
folderPath
,
name
,
WalletHelpers
.
GetNetwork
(
network
),
extendedKey
,
coinType
,
creationTime
);
this
.
Load
(
wallet
);
return
wallet
;
return
wallet
;
}
}
/// <inheritdoc />
public
string
CreateNewAccount
(
string
walletName
,
string
accountName
)
{
Wallet
wallet
=
this
.
Wallets
.
SingleOrDefault
(
w
=>
w
.
Name
==
walletName
);
if
(
wallet
==
null
)
{
throw
new
Exception
(
$"No wallet with name
{
walletName
}
could be found."
);
}
var
lastAccountIndex
=
0
;
// validate account creation
if
(
wallet
.
Accounts
.
Any
())
{
// check account with same name doesn't already exists
if
(
wallet
.
Accounts
.
Any
(
a
=>
a
.
Name
==
accountName
))
{
throw
new
Exception
(
$"Account with name '
{
accountName
}
' already exists in '
{
walletName
}
'."
);
}
// check account at index i - 1 contains transactions.
lastAccountIndex
=
wallet
.
Accounts
.
Max
(
a
=>
a
.
Index
);
HdAccount
previousAccount
=
wallet
.
Accounts
.
Single
(
a
=>
a
.
Index
==
lastAccountIndex
);
if
(!
previousAccount
.
ExternalAddresses
.
Any
(
addresses
=>
addresses
.
Transactions
.
Any
())
&&
!
previousAccount
.
InternalAddresses
.
Any
(
addresses
=>
addresses
.
Transactions
.
Any
()))
{
throw
new
Exception
(
$"Cannot create new account '
{
accountName
}
' in '
{
walletName
}
' if the previous account '
{
previousAccount
.
Name
}
' has not been used."
);
}
}
wallet
.
Accounts
=
wallet
.
Accounts
.
Concat
(
new
[]
{
new
HdAccount
{
Index
=
lastAccountIndex
+
1
,
ExternalAddresses
=
new
List
<
HdAddress
>(),
InternalAddresses
=
new
List
<
HdAddress
>(),
Name
=
accountName
,
CreationTime
=
DateTimeOffset
.
Now
}});
this
.
SaveToFile
(
wallet
);
return
accountName
;
}
public
WalletGeneralInfoModel
GetGeneralInfo
(
string
name
)
public
WalletGeneralInfoModel
GetGeneralInfo
(
string
name
)
{
{
throw
new
System
.
NotImplementedException
();
throw
new
System
.
NotImplementedException
();
...
@@ -105,23 +161,31 @@ namespace Breeze.Wallet
...
@@ -105,23 +161,31 @@ namespace Breeze.Wallet
/// Generates the wallet file.
/// Generates the wallet file.
/// </summary>
/// </summary>
/// <param name="password">The password used to encrypt sensitive info.</param>
/// <param name="password">The password used to encrypt sensitive info.</param>
/// <param name="walletFilePath">The location of the wallet file.</param>
/// <param name="folderPath">The folder where the wallet will be generated.</param>
/// <param name="name">The name of the wallet.</param>
/// <param name="network">The network this wallet is for.</param>
/// <param name="network">The network this wallet is for.</param>
/// <param name="extendedKey">The root key used to generate keys.</param>
/// <param name="extendedKey">The root key used to generate keys.</param>
/// <param name="coinType">The type of coin for which this wallet is created.</param>
/// <param name="creationTime">The time this wallet was created.</param>
/// <param name="creationTime">The time this wallet was created.</param>
/// <returns></returns>
/// <returns></returns>
/// <exception cref="System.NotSupportedException"></exception>
/// <exception cref="System.NotSupportedException"></exception>
private
Wallet
GenerateWalletFile
(
string
password
,
string
walletFilePath
,
Network
network
,
ExtKey
extendedKey
,
DateTimeOffset
?
creationTime
=
null
)
private
Wallet
GenerateWalletFile
(
string
password
,
string
folderPath
,
string
name
,
Network
network
,
ExtKey
extendedKey
,
CoinType
coinType
=
CoinType
.
Bitcoin
,
DateTimeOffset
?
creationTime
=
null
)
{
{
string
walletFilePath
=
Path
.
Combine
(
folderPath
,
$"
{
name
}
.json"
);
if
(
File
.
Exists
(
walletFilePath
))
if
(
File
.
Exists
(
walletFilePath
))
throw
new
InvalidOperationException
(
$"Wallet already exists at
{
walletFilePath
}
"
);
throw
new
InvalidOperationException
(
$"Wallet already exists at
{
walletFilePath
}
"
);
Wallet
walletFile
=
new
Wallet
Wallet
walletFile
=
new
Wallet
{
{
Name
=
name
,
EncryptedSeed
=
extendedKey
.
PrivateKey
.
GetEncryptedBitcoinSecret
(
password
,
network
).
ToWif
(),
EncryptedSeed
=
extendedKey
.
PrivateKey
.
GetEncryptedBitcoinSecret
(
password
,
network
).
ToWif
(),
ChainCode
=
extendedKey
.
ChainCode
,
ChainCode
=
extendedKey
.
ChainCode
,
CreationTime
=
creationTime
??
DateTimeOffset
.
Now
,
CreationTime
=
creationTime
??
DateTimeOffset
.
Now
,
Network
=
network
Network
=
network
,
Accounts
=
new
List
<
HdAccount
>(),
CoinType
=
coinType
,
WalletFilePath
=
walletFilePath
};
};
// create a folder if none exists and persist the file
// create a folder if none exists and persist the file
...
@@ -130,5 +194,26 @@ namespace Breeze.Wallet
...
@@ -130,5 +194,26 @@ namespace Breeze.Wallet
return
walletFile
;
return
walletFile
;
}
}
/// <summary>
/// Saves the wallet into the file system.
/// </summary>
/// <param name="wallet">The wallet to save.</param>
private
void
SaveToFile
(
Wallet
wallet
)
{
File
.
WriteAllText
(
wallet
.
WalletFilePath
,
JsonConvert
.
SerializeObject
(
wallet
,
Formatting
.
Indented
));
}
/// <summary>
/// Loads the wallet to be used by the manager.
/// </summary>
/// <param name="wallet">The wallet to load.</param>
private
void
Load
(
Wallet
wallet
)
{
if
(
this
.
Wallets
.
All
(
w
=>
w
.
Name
!=
wallet
.
Name
))
{
this
.
Wallets
.
Add
(
wallet
);
}
}
}
}
}
}
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