Skip to content

Commit 9271719

Browse files
committed
breakthrough: sample code for joining a copay wallet
1 parent 301c192 commit 9271719

File tree

4 files changed

+182
-3
lines changed

4 files changed

+182
-3
lines changed

src/Makefile.am

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
AM_CPPFLAGS = -I$(top_srcdir)/include -I../vendor/bitcoin/src
1+
AM_CPPFLAGS = -I$(top_srcdir)/include -I../vendor/bitcoin/src -I../vendor/bitcoin/src/config
22

33
libunival_CONFIG_INCLUDES=-I$(builddir)/config
44
libunival_INCLUDES=-I$(builddir) -I$(builddir)/obj univalue/univalue_escapes.h ../include/univalue.h

src/dbb_app.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,9 +203,53 @@ int main(int argc, char** argv)
203203
ECC_Start();
204204

205205
BitPayWalletClient client;
206+
207+
std::string myName = "test";
208+
209+
printf("start seed\n");
210+
client.seed();
211+
printf("seed done\n");
212+
std::string requestPubKey;
213+
if (!client.GetRequestPubKey(requestPubKey))
214+
printf("Retriving request key failed!\n");
215+
216+
std::string copayerHash;
217+
client.GetCopayerHash(myName, copayerHash);
218+
printf("---copayer hash: %s\n", copayerHash.c_str());
206219
CKey aKey = client.GetNewKey();
207220
assert(aKey.VerifyPubKey(aKey.GetPubKey()) == 1);
208221

222+
BitpayWalletInvitation invitation;
223+
if (!client.ParseWalletInvitation("MJRiJFPTE8cJtQ2YBHtasnL3vNy8NF4BRLBNLemcBCMg55DmEWeY9FaXGrQEBjRd1ZsoZ9hVfgL", invitation))
224+
printf("parse invitation failed!\n");
225+
226+
std::string copayerSignature;
227+
client.GetCopayerSignature(copayerHash, invitation.walletPrivKey, copayerSignature);
228+
printf("copayerSignature %s\n", copayerSignature.c_str());
229+
printf("walletid: %s\n", invitation.walletID.c_str());
230+
printf("network: %s\n", invitation.network.c_str());
231+
232+
UniValue jsonArgs(UniValue::VOBJ);
233+
jsonArgs.push_back(Pair("walletId", invitation.walletID));
234+
jsonArgs.push_back(Pair("name", myName));
235+
jsonArgs.push_back(Pair("xPubKey", client.GetXPubKey()));
236+
jsonArgs.push_back(Pair("requestPubKey", requestPubKey));
237+
jsonArgs.push_back(Pair("isTemporaryRequestKey", false));
238+
jsonArgs.push_back(Pair("copayerSignature", copayerSignature));
239+
240+
std::string json = jsonArgs.write();
241+
printf("JSON: %s", json.c_str());
242+
243+
std::string method = "post";
244+
std::string url = "/v1/wallets/"+invitation.walletID+"/copayers";
245+
246+
std::string xSignature = client.SignRequest(method, url, json);
247+
std::string xIdentity = requestPubKey;
248+
std::string postUrl = "https://bws.bitpay.com/bws/api";
249+
250+
std::string header = "\n\ncurl -X POST -H \"Content-Type: application/json\" -d '"+json+"' --header \"x-identity: "+xIdentity+"\" --header \"x-signature: "+xSignature+"\" -v "+postUrl+url+"\n\n";
251+
printf("header: %s", header.c_str());
252+
209253
ECC_Stop();
210254

211255
// unsigned char vchPub[65];

src/libbitpay-wallet-client/bpwalletclient.cpp

Lines changed: 120 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,21 @@
77
#include <assert.h>
88
#include <string.h>
99

10+
#include "base58.h"
11+
#include "eccryptoverify.h"
12+
#include "keystore.h"
1013
#include "util.h"
14+
#include "utilstrencodings.h"
1115

16+
#include "libdbb/crypto.h"
1217

1318

1419
//ignore osx depracation warning
1520
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
1621

1722
BitPayWalletClient::BitPayWalletClient()
1823
{
19-
24+
SelectParams(CBaseChainParams::MAIN);
2025
}
2126

2227
BitPayWalletClient::~BitPayWalletClient()
@@ -30,7 +35,120 @@ CKey BitPayWalletClient::GetNewKey()
3035
return key;
3136
}
3237

38+
39+
std::vector<std::string> BitPayWalletClient::split(const std::string& str, std::vector<int> indexes) {
40+
std::vector<std::string> parts;
41+
indexes.push_back(str.size());
42+
int i = 0;
43+
while (i < indexes.size()) {
44+
int from = i == 0 ? 0 : indexes[i - 1];
45+
parts.push_back(str.substr(from, indexes[i]-from));
46+
i++;
47+
};
48+
return parts;
49+
};
50+
51+
std::string BitPayWalletClient::_copayerHash(const std::string& name, const std::string& xPubKey, const std::string& requestPubKey) {
52+
return name+"|"+xPubKey+"|"+requestPubKey;
53+
};
54+
55+
std::string BitPayWalletClient::GetXPubKey()
56+
{
57+
CBitcoinExtPubKey xpubkey;
58+
xpubkey.SetKey(masterPubKey);
59+
return xpubkey.ToString();
60+
}
61+
bool BitPayWalletClient::GetCopayerHash(const std::string& name, std::string& out) {
62+
if (!requestKey.IsValid())
63+
return false;
64+
65+
CBitcoinExtPubKey xpubkey;
66+
xpubkey.SetKey(masterPubKey);
67+
68+
std::string requestKeyHex;
69+
if (!GetRequestPubKey(requestKeyHex))
70+
return false;
71+
72+
out = _copayerHash(name, xpubkey.ToString(), requestKeyHex);
73+
return true;
74+
};
75+
76+
bool BitPayWalletClient::GetCopayerSignature(const std::string& stringToHash, const CKey& privKey, std::string& sigHexOut) {
77+
uint256 hash = Hash(stringToHash.begin(), stringToHash.end());
78+
std::vector<unsigned char> signature;
79+
privKey.Sign(hash, signature);
80+
81+
sigHexOut = HexStr(signature);
82+
return true;
83+
};
84+
85+
void BitPayWalletClient::seed()
86+
{
87+
CKeyingMaterial vSeed;
88+
vSeed.resize(32);
89+
90+
RandAddSeedPerfmon();
91+
do {
92+
GetRandBytes(&vSeed[0], vSeed.size());
93+
} while (!eccrypto::Check(&vSeed[0]));
94+
95+
CExtKey masterPrivKeyRoot;
96+
masterPrivKeyRoot.SetMaster(&vSeed[0], vSeed.size()); //m
97+
masterPrivKeyRoot.Derive(masterPrivKey, 45); //m/45' xpriv
98+
masterPubKey = masterPrivKey.Neuter(); //m/45' xpub
99+
100+
CExtKey requestKeyChain;
101+
masterPrivKeyRoot.Derive(requestKeyChain, 1); //m/1'
102+
103+
CExtKey requestKeyExt;
104+
requestKeyChain.Derive(requestKeyExt, 0);
105+
106+
requestKey = requestKeyExt.key;
107+
}
108+
109+
bool BitPayWalletClient::GetRequestPubKey(std::string &pubKeyOut)
110+
{
111+
if (!requestKey.IsValid())
112+
return false;
113+
114+
pubKeyOut = HexStr(requestKey.GetPubKey(), false);
115+
return true;
116+
}
117+
33118
bool BitPayWalletClient::ParseWalletInvitation(const std::string& walletInvitation, BitpayWalletInvitation& invitationOut)
34119
{
120+
std::vector<int> splits = {22, 74};
121+
std::vector<std::string> secretSplit = split(walletInvitation, splits);
122+
123+
//TODO: var widBase58 = secretSplit[0].replace(/0/g, '');
124+
std::string widBase58 = secretSplit[0];
125+
std::vector<unsigned char> vch;
126+
if (!DecodeBase58(widBase58.c_str(), vch))
127+
return false;
128+
129+
std::string widHex = HexStr(vch, false);
130+
131+
splits = {8, 12, 16, 20};
132+
std::vector<std::string> walletIdParts = split(widHex, splits);
133+
invitationOut.walletID = walletIdParts[0]+"-"+walletIdParts[1]+"-"+walletIdParts[2]+"-"+walletIdParts[3]+"-"+walletIdParts[4];
134+
135+
std::string walletPrivKeyStr = secretSplit[1];
136+
CBitcoinSecret vchSecret;
137+
if (!vchSecret.SetString(walletPrivKeyStr))
138+
return false;
139+
140+
invitationOut.walletPrivKey = vchSecret.GetKey();
141+
invitationOut.network = secretSplit[2] == "T" ? "testnet" : "livenet";
35142
return true;
36-
}
143+
}
144+
145+
std::string BitPayWalletClient::SignRequest(const std::string& method,
146+
const std::string& url,
147+
const std::string& args)
148+
{
149+
std::string message = method+"|"+url+"|"+args;
150+
uint256 hash = Hash(message.begin(), message.end());
151+
std::vector<unsigned char> signature;
152+
requestKey.Sign(hash, signature);
153+
return HexStr(signature);
154+
};

src/libbitpay-wallet-client/bpwalletclient.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,21 @@ class BitPayWalletClient
6464

6565
CKey GetNewKey();
6666
bool ParseWalletInvitation(const std::string& walletInvitation, BitpayWalletInvitation& invitationOut);
67+
bool GetRequestPubKey(std::string &pubKeyOut);
68+
bool GetCopayerHash(const std::string &name, std::string &hashOut);
69+
bool GetCopayerSignature(const std::string& stringToHash, const CKey& privKey, std::string& sigHexOut);
70+
void seed();
71+
std::string GetXPubKey();
72+
std::string SignRequest(const std::string& method,
73+
const std::string& url,
74+
const std::string& args);
75+
private:
76+
77+
CExtKey masterPrivKey; // "m/45'"
78+
CExtPubKey masterPubKey; // "m/45'"
79+
80+
CKey requestKey; //"m/1'/0"
81+
82+
std::vector<std::string> split(const std::string& str, std::vector<int> indexes);
83+
std::string _copayerHash(const std::string& name, const std::string& xPubKey, const std::string& requestPubKey);
6784
};

0 commit comments

Comments
 (0)