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
da338538
Commit
da338538
authored
Apr 25, 2017
by
Jeremy Bokobza
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added creatig receiving addresses for an account
parent
c5fa0532
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
236 additions
and
11 deletions
+236
-11
Wallet.postman_collection.json
...Api.Tests/Postman requests/Wallet.postman_collection.json
+61
-1
WalletController.cs
Breeze/src/Breeze.Wallet/Controllers/WalletController.cs
+27
-1
IWalletManager.cs
Breeze/src/Breeze.Wallet/IWalletManager.cs
+10
-1
CreateAccountModel.cs
Breeze/src/Breeze.Wallet/Models/CreateAccountModel.cs
+8
-0
CreateAddressModel.cs
Breeze/src/Breeze.Wallet/Models/CreateAddressModel.cs
+22
-0
Wallet.cs
Breeze/src/Breeze.Wallet/Wallet.cs
+17
-3
WalletManager.cs
Breeze/src/Breeze.Wallet/WalletManager.cs
+91
-5
No files found.
Breeze/src/Breeze.Api.Tests/Postman requests/Wallet.postman_collection.json
View file @
da338538
...
@@ -2,7 +2,7 @@
...
@@ -2,7 +2,7 @@
"variables"
:
[],
"variables"
:
[],
"info"
:
{
"info"
:
{
"name"
:
"Wallet"
,
"name"
:
"Wallet"
,
"_postman_id"
:
"
25ac0bc5-23f5-e00d-932f-a8fde7787e7a
"
,
"_postman_id"
:
"
5eec0912-fcf0-50f5-05a2-0835fa13c670
"
,
"description"
:
"Requests relating to operations on the wallet"
,
"description"
:
"Requests relating to operations on the wallet"
,
"schema"
:
"https://schema.getpostman.com/json/collection/v2.0.0/collection.json"
"schema"
:
"https://schema.getpostman.com/json/collection/v2.0.0/collection.json"
},
},
...
@@ -206,6 +206,66 @@
...
@@ -206,6 +206,66 @@
"description"
:
"Gets all the wallets files stored in the default folder"
"description"
:
"Gets all the wallets files stored in the default folder"
},
},
"response"
:
[]
"response"
:
[]
},
{
"name"
:
"New account for non-existant wallet fails"
,
"request"
:
{
"url"
:
"http://localhost:5000/api/v1/wallet/account"
,
"method"
:
"POST"
,
"header"
:
[
{
"key"
:
"Content-Type"
,
"value"
:
"application/json"
,
"description"
:
""
}
],
"body"
:
{
"mode"
:
"raw"
,
"raw"
:
"{
\n\t\"
walletName
\"
:
\"
myFirstWallet
\"
,
\n\t\"
accountName
\"
:
\"
account one
\"\n
}"
},
"description"
:
""
},
"response"
:
[]
},
{
"name"
:
"Create new account for wallet"
,
"request"
:
{
"url"
:
"http://localhost:5000/api/v1/wallet/account"
,
"method"
:
"POST"
,
"header"
:
[
{
"key"
:
"Content-Type"
,
"value"
:
"application/json"
,
"description"
:
""
}
],
"body"
:
{
"mode"
:
"raw"
,
"raw"
:
"{
\n\t\"
walletName
\"
:
\"
myFirstWallet
\"
,
\n\t\"
accountName
\"
:
\"
account one
\"
,
\n\t\"
password
\"
:
\"
123456
\"\n
}"
},
"description"
:
""
},
"response"
:
[]
},
{
"name"
:
"Create new address for wallet"
,
"request"
:
{
"url"
:
"http://localhost:5000/api/v1/wallet/address"
,
"method"
:
"POST"
,
"header"
:
[
{
"key"
:
"Content-Type"
,
"value"
:
"application/json"
,
"description"
:
""
}
],
"body"
:
{
"mode"
:
"raw"
,
"raw"
:
"{
\n\t\"
walletName
\"
:
\"
myFirstWallet
\"
,
\n\t\"
accountName
\"
:
\"
account one
\"\n
}"
},
"description"
:
""
},
"response"
:
[]
}
}
]
]
}
}
\ No newline at end of file
Breeze/src/Breeze.Wallet/Controllers/WalletController.cs
View file @
da338538
...
@@ -328,7 +328,33 @@ namespace Breeze.Wallet.Controllers
...
@@ -328,7 +328,33 @@ namespace Breeze.Wallet.Controllers
try
try
{
{
var
result
=
this
.
walletManager
.
CreateNewAccount
(
request
.
WalletName
,
request
.
AccountName
);
var
result
=
this
.
walletManager
.
CreateNewAccount
(
request
.
WalletName
,
request
.
AccountName
,
request
.
Password
);
return
this
.
Json
(
result
);
}
catch
(
Exception
e
)
{
return
ErrorHelpers
.
BuildErrorResponse
(
HttpStatusCode
.
BadRequest
,
e
.
Message
,
e
.
ToString
());
}
}
/// <summary>
/// Creates a new address for a wallet.
/// </summary>
/// <returns>An address in Base58 format.</returns>
[
Route
(
"address"
)]
[
HttpPost
]
public
IActionResult
CreateNewAddress
([
FromBody
]
CreateAddressModel
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
.
CreateNewAddress
(
request
.
WalletName
,
request
.
AccountName
);
return
this
.
Json
(
result
);
return
this
.
Json
(
result
);
}
}
catch
(
Exception
e
)
catch
(
Exception
e
)
...
...
Breeze/src/Breeze.Wallet/IWalletManager.cs
View file @
da338538
...
@@ -55,12 +55,21 @@ namespace Breeze.Wallet
...
@@ -55,12 +55,21 @@ namespace Breeze.Wallet
/// </summary>
/// </summary>
/// <param name="walletName">The name of the wallet in which this account will be created.</param>
/// <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>
/// <param name="accountName">The name by which this account will be identified.</param>
/// <param name="password">The password used to decrypt the private key.</param>
/// <remarks>
/// <remarks>
/// According to BIP44, an account at index (i) can only be created when the account
/// According to BIP44, an account at index (i) can only be created when the account
/// at index (i - 1) contains transactions.
/// at index (i - 1) contains transactions.
/// </remarks>
/// </remarks>
/// <returns>The name of the new account.</returns>
/// <returns>The name of the new account.</returns>
string
CreateNewAccount
(
string
walletName
,
string
accountName
);
string
CreateNewAccount
(
string
walletName
,
string
accountName
,
string
password
);
/// <summary>
/// Creates the new address.
/// </summary>
/// <param name="walletName">The name of the wallet in which this address will be created.</param>
/// <param name="accountName">The name of the account in which this address will be created.</param>
/// <returns>The new address, in Base58 format.</returns>
string
CreateNewAddress
(
string
walletName
,
string
accountName
);
WalletGeneralInfoModel
GetGeneralInfo
(
string
walletName
);
WalletGeneralInfoModel
GetGeneralInfo
(
string
walletName
);
...
...
Breeze/src/Breeze.Wallet/Models/CreateAccountModel.cs
View file @
da338538
...
@@ -18,5 +18,13 @@ namespace Breeze.Wallet.Models
...
@@ -18,5 +18,13 @@ namespace Breeze.Wallet.Models
/// </summary>
/// </summary>
[
Required
]
[
Required
]
public
string
AccountName
{
get
;
set
;
}
public
string
AccountName
{
get
;
set
;
}
/// <summary>
/// The password for this wallet.
/// </summary>
[
Required
]
public
string
Password
{
get
;
set
;
}
}
}
}
}
Breeze/src/Breeze.Wallet/Models/CreateAddressModel.cs
0 → 100644
View file @
da338538
using
System
;
using
System.Collections.Generic
;
using
System.ComponentModel.DataAnnotations
;
using
System.Text
;
namespace
Breeze.Wallet.Models
{
public
class
CreateAddressModel
{
/// <summary>
/// The name of the wallet in which to create the address.
/// </summary>
[
Required
]
public
string
WalletName
{
get
;
set
;
}
/// <summary>
/// The name of the account in which to create the address.
/// </summary>
[
Required
]
public
string
AccountName
{
get
;
set
;
}
}
}
Breeze/src/Breeze.Wallet/Wallet.cs
View file @
da338538
...
@@ -2,6 +2,7 @@
...
@@ -2,6 +2,7 @@
using
System.Collections.Generic
;
using
System.Collections.Generic
;
using
Breeze.Wallet.JsonConverters
;
using
Breeze.Wallet.JsonConverters
;
using
NBitcoin
;
using
NBitcoin
;
using
NBitcoin.JsonConverters
;
using
Newtonsoft.Json
;
using
Newtonsoft.Json
;
namespace
Breeze.Wallet
namespace
Breeze.Wallet
...
@@ -93,6 +94,12 @@ namespace Breeze.Wallet
...
@@ -93,6 +94,12 @@ namespace Breeze.Wallet
[
JsonProperty
(
PropertyName
=
"name"
)]
[
JsonProperty
(
PropertyName
=
"name"
)]
public
string
Name
{
get
;
set
;
}
public
string
Name
{
get
;
set
;
}
/// <summary>
/// An extended pub key used to generate addresses.
/// </summary>
[
JsonProperty
(
PropertyName
=
"extPubKey"
)]
public
string
ExtendedPubKey
{
get
;
set
;
}
/// <summary>
/// <summary>
/// Gets or sets the creation time.
/// Gets or sets the creation time.
/// </summary>
/// </summary>
...
@@ -118,6 +125,12 @@ namespace Breeze.Wallet
...
@@ -118,6 +125,12 @@ namespace Breeze.Wallet
/// </summary>
/// </summary>
public
class
HdAddress
public
class
HdAddress
{
{
/// <summary>
/// The index of the address.
/// </summary>
[
JsonProperty
(
PropertyName
=
"index"
)]
public
int
Index
{
get
;
set
;
}
/// <summary>
/// <summary>
/// Gets or sets the creation time.
/// Gets or sets the creation time.
/// </summary>
/// </summary>
...
@@ -129,13 +142,14 @@ namespace Breeze.Wallet
...
@@ -129,13 +142,14 @@ namespace Breeze.Wallet
/// The script pub key for this address.
/// The script pub key for this address.
/// </summary>
/// </summary>
[
JsonProperty
(
PropertyName
=
"scriptPubKey"
)]
[
JsonProperty
(
PropertyName
=
"scriptPubKey"
)]
[
JsonConverter
(
typeof
(
ScriptJsonConverter
))]
public
Script
ScriptPubKey
{
get
;
set
;
}
public
Script
ScriptPubKey
{
get
;
set
;
}
/// <summary>
/// <summary>
/// The Base58 representation of this address.
/// The Base58 representation of this address.
/// </summary>
/// </summary>
[
JsonProperty
(
PropertyName
=
"address"
)]
[
JsonProperty
(
PropertyName
=
"address"
)]
public
BitcoinAddress
Address
{
get
;
set
;
}
public
string
Address
{
get
;
set
;
}
/// <summary>
/// <summary>
/// A path to the address as defined in BIP44.
/// A path to the address as defined in BIP44.
...
...
Breeze/src/Breeze.Wallet/WalletManager.cs
View file @
da338538
...
@@ -6,6 +6,7 @@ using Breeze.Wallet.Helpers;
...
@@ -6,6 +6,7 @@ using Breeze.Wallet.Helpers;
using
Breeze.Wallet.Models
;
using
Breeze.Wallet.Models
;
using
NBitcoin
;
using
NBitcoin
;
using
Newtonsoft.Json
;
using
Newtonsoft.Json
;
using
Stratis.Bitcoin.Utilities
;
namespace
Breeze.Wallet
namespace
Breeze.Wallet
{
{
...
@@ -77,7 +78,7 @@ namespace Breeze.Wallet
...
@@ -77,7 +78,7 @@ namespace Breeze.Wallet
}
}
/// <inheritdoc />
/// <inheritdoc />
public
string
CreateNewAccount
(
string
walletName
,
string
accountName
)
public
string
CreateNewAccount
(
string
walletName
,
string
accountName
,
string
password
)
{
{
Wallet
wallet
=
this
.
Wallets
.
SingleOrDefault
(
w
=>
w
.
Name
==
walletName
);
Wallet
wallet
=
this
.
Wallets
.
SingleOrDefault
(
w
=>
w
.
Name
==
walletName
);
if
(
wallet
==
null
)
if
(
wallet
==
null
)
...
@@ -85,7 +86,7 @@ namespace Breeze.Wallet
...
@@ -85,7 +86,7 @@ namespace Breeze.Wallet
throw
new
Exception
(
$"No wallet with name
{
walletName
}
could be found."
);
throw
new
Exception
(
$"No wallet with name
{
walletName
}
could be found."
);
}
}
var
last
AccountIndex
=
0
;
int
new
AccountIndex
=
0
;
// validate account creation
// validate account creation
if
(
wallet
.
Accounts
.
Any
())
if
(
wallet
.
Accounts
.
Any
())
...
@@ -97,17 +98,27 @@ namespace Breeze.Wallet
...
@@ -97,17 +98,27 @@ namespace Breeze.Wallet
}
}
// check account at index i - 1 contains transactions.
// check account at index i - 1 contains transactions.
lastAccountIndex
=
wallet
.
Accounts
.
Max
(
a
=>
a
.
Index
);
int
lastAccountIndex
=
wallet
.
Accounts
.
Max
(
a
=>
a
.
Index
);
HdAccount
previousAccount
=
wallet
.
Accounts
.
Single
(
a
=>
a
.
Index
==
lastAccountIndex
);
HdAccount
previousAccount
=
wallet
.
Accounts
.
Single
(
a
=>
a
.
Index
==
lastAccountIndex
);
if
(!
previousAccount
.
ExternalAddresses
.
Any
(
addresses
=>
addresses
.
Transactions
.
Any
())
&&
!
previousAccount
.
InternalAddresses
.
Any
(
addresses
=>
addresses
.
Transactions
.
Any
()))
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."
);
throw
new
Exception
(
$"Cannot create new account '
{
accountName
}
' in '
{
walletName
}
' if the previous account '
{
previousAccount
.
Name
}
' has not been used."
);
}
}
newAccountIndex
=
lastAccountIndex
+
1
;
}
}
// get the extended pub key used to generate addresses for this account
var
privateKey
=
Key
.
Parse
(
wallet
.
EncryptedSeed
,
password
,
wallet
.
Network
);
var
seedExtKey
=
new
ExtKey
(
privateKey
,
wallet
.
ChainCode
);
KeyPath
keyPath
=
new
KeyPath
(
$"m/44'/
{(
int
)
wallet
.
CoinType
}
'/
{
newAccountIndex
}
'"
);
var
accountExtKey
=
seedExtKey
.
Derive
(
keyPath
);
ExtPubKey
accountExtPubKey
=
accountExtKey
.
Neuter
();
wallet
.
Accounts
=
wallet
.
Accounts
.
Concat
(
new
[]
{
new
HdAccount
wallet
.
Accounts
=
wallet
.
Accounts
.
Concat
(
new
[]
{
new
HdAccount
{
{
Index
=
lastAccountIndex
+
1
,
Index
=
newAccountIndex
,
ExtendedPubKey
=
accountExtPubKey
.
ToString
(
wallet
.
Network
),
ExternalAddresses
=
new
List
<
HdAddress
>(),
ExternalAddresses
=
new
List
<
HdAddress
>(),
InternalAddresses
=
new
List
<
HdAddress
>(),
InternalAddresses
=
new
List
<
HdAddress
>(),
Name
=
accountName
,
Name
=
accountName
,
...
@@ -119,6 +130,56 @@ namespace Breeze.Wallet
...
@@ -119,6 +130,56 @@ namespace Breeze.Wallet
return
accountName
;
return
accountName
;
}
}
/// <inheritdoc />
public
string
CreateNewAddress
(
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."
);
}
HdAccount
account
=
wallet
.
Accounts
.
SingleOrDefault
(
a
=>
a
.
Name
==
accountName
);
if
(
account
==
null
)
{
throw
new
Exception
(
$"No account with name
{
accountName
}
could be found."
);
}
int
newAddressIndex
=
0
;
// validate address creation
if
(
account
.
ExternalAddresses
.
Any
())
{
// check last created address contains transactions.
int
lastAddressIndex
=
account
.
ExternalAddresses
.
Max
(
a
=>
a
.
Index
);
var
lastAddress
=
account
.
ExternalAddresses
.
SingleOrDefault
(
a
=>
a
.
Index
==
lastAddressIndex
);
if
(
lastAddress
!=
null
&&
!
lastAddress
.
Transactions
.
Any
())
{
throw
new
Exception
(
$"Cannot create new address in account '
{
accountName
}
' if the previous address '
{
lastAddress
.
Address
}
' has not been used."
);
}
newAddressIndex
=
lastAddressIndex
+
1
;
}
// generate new receiving address
BitcoinPubKeyAddress
address
=
this
.
GenerateAddress
(
account
.
ExtendedPubKey
,
newAddressIndex
,
false
,
wallet
.
Network
);
// add address details
account
.
ExternalAddresses
=
account
.
ExternalAddresses
.
Concat
(
new
[]
{
new
HdAddress
{
Index
=
newAddressIndex
,
HdPath
=
CreateBip44Path
(
wallet
.
CoinType
,
account
.
Index
,
newAddressIndex
,
false
),
ScriptPubKey
=
address
.
ScriptPubKey
,
Address
=
address
.
ToString
(),
Transactions
=
new
List
<
TransactionData
>(),
CreationTime
=
DateTimeOffset
.
Now
}});
this
.
SaveToFile
(
wallet
);
return
address
.
ToString
();
}
public
WalletGeneralInfoModel
GetGeneralInfo
(
string
name
)
public
WalletGeneralInfoModel
GetGeneralInfo
(
string
name
)
{
{
throw
new
System
.
NotImplementedException
();
throw
new
System
.
NotImplementedException
();
...
@@ -215,5 +276,30 @@ namespace Breeze.Wallet
...
@@ -215,5 +276,30 @@ namespace Breeze.Wallet
this
.
Wallets
.
Add
(
wallet
);
this
.
Wallets
.
Add
(
wallet
);
}
}
}
}
private
BitcoinPubKeyAddress
GenerateAddress
(
string
accountExtPubKey
,
int
index
,
bool
isChange
,
Network
network
)
{
int
change
=
isChange
?
1
:
0
;
KeyPath
keyPath
=
new
KeyPath
(
$"
{
change
}
/
{
index
}
"
);
ExtPubKey
extPubKey
=
ExtPubKey
.
Parse
(
accountExtPubKey
).
Derive
(
keyPath
);
return
extPubKey
.
PubKey
.
GetAddress
(
network
);
}
/// <summary>
/// Creates the bip44 path.
/// </summary>
/// <param name="coinType">Type of the coin.</param>
/// <param name="accountIndex">Index of the account.</param>
/// <param name="addressIndex">Index of the address.</param>
/// <param name="isChange">if set to <c>true</c> [is change].</param>
/// <returns></returns>
public
static
string
CreateBip44Path
(
CoinType
coinType
,
int
accountIndex
,
int
addressIndex
,
bool
isChange
=
false
)
{
//// populate the items according to the BIP44 path
//// [m/purpose'/coin_type'/account'/change/address_index]
int
change
=
isChange
?
1
:
0
;
return
$"m/44'/
{(
int
)
coinType
}
'/
{
accountIndex
}
'/
{
change
}
/
{
addressIndex
}
"
;
}
}
}
}
}
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