Skip to content

Commit a6d2a05

Browse files
committed
[Stealth] Add import stealth address RPC command
1 parent e00b5a2 commit a6d2a05

File tree

7 files changed

+146
-26
lines changed

7 files changed

+146
-26
lines changed

src/bdap/stealth.cpp

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,29 @@ CStealthAddress::CStealthAddress(const CKey& scanKey, const CKey& spendKey)
5555
options = 0;
5656
//uint8_t tmp32[32];
5757
//CSHA256().Write(scanKey.begin(), 32).Finalize(tmp32);
58-
//memcpy(&prefix.number_bits, tmp32, 4);
58+
//memcpy(&prefix_number_bits, tmp32, 4);
5959
//CSHA256().Write(spendKey.begin(), 32).Finalize(tmp32);
60-
//size_t nPrefixBytes = std::ceil((float)prefix.number_bits / 8.0);
61-
//memcpy(&prefix.bitfield, tmp32, nPrefixBytes);
62-
prefix.number_bits = 0;
63-
prefix.bitfield = 0;
60+
//size_t nPrefixBytes = std::ceil((float)prefix_number_bits / 8.0);
61+
//memcpy(&prefix_bitfield, tmp32, nPrefixBytes);
62+
prefix_number_bits = 0;
63+
prefix_bitfield = 0;
64+
number_signatures = 1;
65+
66+
scan_pubkey.resize(scanKey.GetPubKey().size());
67+
memcpy(&scan_pubkey[0], scanKey.GetPubKey().begin(), scanKey.GetPubKey().size());
68+
69+
spend_pubkey.resize(spendKey.GetPubKey().size());
70+
memcpy(&spend_pubkey[0], spendKey.GetPubKey().begin(), spendKey.GetPubKey().size());
71+
72+
scan_secret = scanKey;
73+
spend_secret_id = spendKey.GetPubKey().GetID();
74+
}
75+
76+
CStealthAddress::CStealthAddress(const CKey& scanKey, const CKey& spendKey, const uint8_t bits, const uint32_t prefix)
77+
{
78+
options = 0;
79+
prefix_number_bits = bits;
80+
prefix_bitfield = prefix;
6481
number_signatures = 1;
6582

6683
scan_pubkey.resize(scanKey.GetPubKey().size());
@@ -124,16 +141,16 @@ int CStealthAddress::FromRaw(const uint8_t *p, size_t nSize)
124141
memcpy(&spend_pubkey[0], p, EC_COMPRESSED_SIZE * spend_pubkeys);
125142
p += EC_COMPRESSED_SIZE * spend_pubkeys;
126143
number_signatures = *p++;
127-
prefix.number_bits = *p++;
128-
prefix.bitfield = 0;
129-
size_t nPrefixBytes = std::ceil((float)prefix.number_bits / 8.0);
144+
prefix_number_bits = *p++;
145+
prefix_bitfield = 0;
146+
size_t nPrefixBytes = std::ceil((float)prefix_number_bits / 8.0);
130147

131148
if (nSize < MIN_STEALTH_RAW_SIZE + EC_COMPRESSED_SIZE * (spend_pubkeys-1) + nPrefixBytes) {
132149
return 1;
133150
}
134151

135152
if (nPrefixBytes) {
136-
memcpy(&prefix.bitfield, p, nPrefixBytes);
153+
memcpy(&prefix_bitfield, p, nPrefixBytes);
137154
}
138155

139156
return 0;
@@ -144,7 +161,7 @@ int CStealthAddress::ToRaw(std::vector<uint8_t> &raw) const
144161
// https://wiki.unsystem.net/index.php/DarkWallet/Stealth#Address_format
145162
// [version] [options] [scan_key] [N] ... [Nsigs] [prefix_length] ...
146163

147-
size_t nPrefixBytes = std::ceil((float)prefix.number_bits / 8.0);
164+
size_t nPrefixBytes = std::ceil((float)prefix_number_bits / 8.0);
148165
size_t nPkSpend = spend_pubkey.size() / EC_COMPRESSED_SIZE;
149166
if (scan_pubkey.size() != EC_COMPRESSED_SIZE
150167
|| spend_pubkey.size() < EC_COMPRESSED_SIZE
@@ -163,9 +180,9 @@ int CStealthAddress::ToRaw(std::vector<uint8_t> &raw) const
163180
raw[o] = nPkSpend; o++;
164181
memcpy(&raw[o], &spend_pubkey[0], EC_COMPRESSED_SIZE * nPkSpend); o += EC_COMPRESSED_SIZE * nPkSpend;
165182
raw[o] = number_signatures; o++;
166-
raw[o] = prefix.number_bits; o++;
183+
raw[o] = prefix_number_bits; o++;
167184
if (nPrefixBytes) {
168-
memcpy(&raw[o], &prefix.bitfield, nPrefixBytes); o += nPrefixBytes;
185+
memcpy(&raw[o], &prefix_bitfield, nPrefixBytes); o += nPrefixBytes;
169186
}
170187

171188
return 0;
@@ -475,7 +492,8 @@ int PrepareStealthOutput(const CStealthAddress &sx, CScript& scriptPubKey, std::
475492
CPubKey pkEphem = sEphem.GetPubKey();
476493
scriptPubKey = GetScriptForDestination(CPubKey(pkSendTo).GetID());
477494
uint32_t nStealthPrefix;
478-
if (0 != MakeStealthData(sx.prefix, sShared, pkEphem, vData, nStealthPrefix, sError)) {
495+
stealth_prefix prefix {sx.prefix_number_bits, sx.prefix_bitfield};
496+
if (0 != MakeStealthData(prefix, sShared, pkEphem, vData, nStealthPrefix, sError)) {
479497
return 1;
480498
}
481499
return 0;

src/bdap/stealth.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ class CStealthAddress
3838
static const int CURRENT_VERSION=1;
3939
int nVersion;
4040
uint8_t options;
41-
stealth_prefix prefix;
41+
uint8_t prefix_number_bits;
42+
uint32_t prefix_bitfield;
4243
int number_signatures;
4344
ec_point scan_pubkey;
4445
ec_point spend_pubkey;
@@ -50,10 +51,11 @@ class CStealthAddress
5051
{
5152
options = 0;
5253
number_signatures = 0;
53-
prefix.number_bits = 0;
54+
prefix_number_bits = 0;
5455
};
5556

5657
CStealthAddress(const CKey& scanKey, const CKey& spendKey);
58+
CStealthAddress(const CKey& scanKey, const CKey& spendKey, const uint8_t bits, const uint32_t prefix);
5759

5860
bool IsNull() const { return (scan_pubkey.size() == 0 || spend_pubkey.size() == 0 || spend_secret_id.IsNull()); }
5961

@@ -81,7 +83,8 @@ class CStealthAddress
8183
inline CStealthAddress operator=(const CStealthAddress& b) {
8284
nVersion = b.nVersion;
8385
options = b.options;
84-
prefix = b.prefix;
86+
prefix_number_bits = b.prefix_number_bits;
87+
prefix_bitfield = b.prefix_bitfield;
8588
number_signatures = b.number_signatures;
8689
scan_pubkey = b.scan_pubkey;
8790
spend_pubkey = b.spend_pubkey;
@@ -96,8 +99,8 @@ class CStealthAddress
9699
inline void SerializationOp(Stream& s, Operation ser_action) {
97100
READWRITE(this->nVersion);
98101
READWRITE(options);
99-
READWRITE(prefix.number_bits);
100-
READWRITE(prefix.bitfield);
102+
READWRITE(prefix_number_bits);
103+
READWRITE(prefix_bitfield);
101104
READWRITE(number_signatures);
102105
READWRITE(scan_pubkey);
103106
READWRITE(spend_pubkey);

src/rpcmisc.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,8 +220,8 @@ class DescribeAddressVisitor : public boost::static_visitor<UniValue>
220220
UniValue operator()(const CStealthAddress& sxAddr) const {
221221
UniValue obj(UniValue::VOBJ);
222222
obj.pushKV("isstealthaddress", true);
223-
obj.pushKV("prefix_num_bits", sxAddr.prefix.number_bits);
224-
obj.pushKV("prefix_bitfield", strprintf("0x%04x", sxAddr.prefix.bitfield));
223+
obj.pushKV("prefix_num_bits", sxAddr.prefix_number_bits);
224+
obj.pushKV("prefix_bitfield", strprintf("0x%04x", sxAddr.prefix_bitfield));
225225
return obj;
226226
}
227227

src/wallet/rpcdump.cpp

Lines changed: 90 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,94 @@ UniValue importprivkey(const JSONRPCRequest& request)
164164
return NullUniValue;
165165
}
166166

167+
UniValue importstealthaddress(const JSONRPCRequest& request)
168+
{
169+
if (!EnsureWalletIsAvailable(request.fHelp))
170+
return NullUniValue;
171+
172+
if (request.fHelp || request.params.size() < 2 || request.params.size() > 4)
173+
throw std::runtime_error(
174+
"importstealthaddress \"scan private key\" \"spend private key\" ( \"prefix bits\" ) ( \"prefix number\" ) \n"
175+
"\nAdds a stealth address (as returned by dumpprivkey) to your wallet.\n"
176+
"\nArguments:\n"
177+
"1. \"scan private key\" (string, required) The stealth scan private key (see dumpprivkey)\n"
178+
"2. \"spend private key\" (string, required) The stealth spend private key (see dumpprivkey)\n"
179+
"3. \"prefix bits\" (int, optional, default=0)\n"
180+
"4. \"prefix number\" (int, optional) If prefix number is not specified the prefix will be selected deterministically.\n"
181+
"\nNote: This call can take minutes to complete because it rescans the chain for transactions.\n"
182+
"\nExamples:\n"
183+
"\nDump stealth address private keys\n" +
184+
HelpExampleCli("importstealthaddress", "\"scan_key\" \"spend_key\"") +
185+
HelpExampleRpc("importstealthaddress", "\"scan_key\" \"spend_key\""));
186+
187+
LOCK2(cs_main, pwalletMain->cs_wallet);
188+
189+
EnsureWalletIsUnlocked();
190+
191+
std::string strScanKey = request.params[0].get_str();
192+
std::string strSpendKey = request.params[1].get_str();
193+
uint8_t prefix_bits = 0;
194+
if (request.params.size() > 2)
195+
prefix_bits = request.params[2].get_int();
196+
197+
uint32_t prefix_number = 0;
198+
if (request.params.size() > 3)
199+
prefix_number = request.params[3].get_int();
200+
201+
CDynamicSecret vchScanSecret, vchSpendSecret;
202+
bool fGood = vchScanSecret.SetString(strScanKey);
203+
204+
if (!fGood)
205+
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid scan private key encoding");
206+
207+
fGood = vchSpendSecret.SetString(strSpendKey);
208+
if (!fGood)
209+
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid spend private key encoding");
210+
211+
CKey keyScan = vchScanSecret.GetKey();
212+
if (!keyScan.IsValid())
213+
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Scan private key outside allowed range");
214+
215+
CKey keySpend = vchSpendSecret.GetKey();
216+
if (!keySpend.IsValid())
217+
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Spend private key outside allowed range");
218+
219+
CPubKey pubkeyScan = keyScan.GetPubKey();
220+
assert(keyScan.VerifyPubKey(pubkeyScan));
221+
CPubKey pubkeySpend = keySpend.GetPubKey();
222+
assert(keySpend.VerifyPubKey(pubkeySpend));
223+
CKeyID scanID = pubkeyScan.GetID();
224+
CKeyID spendID = pubkeySpend.GetID();
225+
{
226+
pwalletMain->MarkDirty();
227+
pwalletMain->SetAddressBook(scanID, "", "receive");
228+
pwalletMain->SetAddressBook(spendID, "", "receive");
229+
// Don't throw error in case a key is already there
230+
if (!pwalletMain->HaveKey(scanID)) {
231+
pwalletMain->mapKeyMetadata[scanID].nCreateTime = 1;
232+
if (!pwalletMain->AddKeyPubKey(keyScan, pubkeyScan))
233+
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding scan key to wallet");
234+
}
235+
if (!pwalletMain->HaveKey(spendID)) {
236+
pwalletMain->mapKeyMetadata[spendID].nCreateTime = 1;
237+
if (!pwalletMain->AddKeyPubKey(keySpend, pubkeySpend))
238+
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding spend key to wallet");
239+
}
240+
CStealthAddress sxAddr(keyScan, keySpend, prefix_bits, prefix_number);
241+
if (!pwalletMain->HaveStealthAddress(spendID)) {
242+
if (!pwalletMain->AddStealthAddress(sxAddr)) {
243+
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding spend key to wallet");
244+
}
245+
else {
246+
// whenever a key is imported, we need to scan the whole chain
247+
pwalletMain->UpdateTimeFirstKey(1);
248+
pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), true);
249+
}
250+
}
251+
}
252+
return NullUniValue;
253+
}
254+
167255
UniValue importbdapkeys(const JSONRPCRequest& request)
168256
{
169257
if (!EnsureWalletIsAvailable(request.fHelp))
@@ -904,8 +992,8 @@ UniValue dumpprivkey(const JSONRPCRequest& request)
904992
throw JSONRPCError(RPC_WALLET_ERROR, "Private key for spend address " + CDynamicAddress(sxAddr.spend_secret_id).ToString() + " is not known");
905993

906994
result.push_back(Pair("spend_private_key", CDynamicSecret(vchSpendSecret).ToString()));
907-
result.push_back(Pair("prefix_number_bits", (int)sxAddr.prefix.number_bits));
908-
result.push_back(Pair("prefix_bitfield", (int)sxAddr.prefix.bitfield));
995+
result.push_back(Pair("prefix_number_bits", (int)sxAddr.prefix_number_bits));
996+
result.push_back(Pair("prefix_bitfield", (int)sxAddr.prefix_bitfield));
909997

910998
return result;
911999
} else {

src/wallet/rpcwallet.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ static UniValue getnewstealthaddress(const JSONRPCRequest &request)
182182
throw std::runtime_error(strprintf("%s -- Failed to get spend private key for stealth address\n", __func__));
183183

184184
CStealthAddress sxAddr(scanKey, spendKey);
185-
if (!pwalletMain->AddStealthAddress(sxAddr, spendKey))
185+
if (!pwalletMain->AddStealthAddress(sxAddr))
186186
throw std::runtime_error(strprintf("%s -- Failed to write stealth address to local wallet.\n", __func__));
187187

188188
return sxAddr.ToString();
@@ -2997,6 +2997,7 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
29972997

29982998
extern UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp
29992999
extern UniValue importprivkey(const JSONRPCRequest& request);
3000+
extern UniValue importstealthaddress(const JSONRPCRequest& request);
30003001
extern UniValue importaddress(const JSONRPCRequest& request);
30013002
extern UniValue importpubkey(const JSONRPCRequest& request);
30023003
extern UniValue dumpwallet(const JSONRPCRequest& request);
@@ -3039,6 +3040,7 @@ static const CRPCCommand commands[] =
30393040
{"wallet", "getwalletinfo", &getwalletinfo, false, {}},
30403041
{"wallet", "importmulti", &importmulti, true, {"requests", "options"}},
30413042
{"wallet", "importprivkey", &importprivkey, true, {"privkey", "label", "rescan"}},
3043+
{"wallet", "importstealthaddress", &importstealthaddress, true, {"privkey", "label", "rescan"}},
30423044
{"wallet", "importwallet", &importwallet, true, {"filename", "forcerescan"}},
30433045
{"wallet", "importaddress", &importaddress, true, {"address", "label", "rescan", "p2sh"}},
30443046
{"wallet", "importprunedfunds", &importprunedfunds, true, {"rawtransaction", "txoutproof"}},

src/wallet/wallet.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5498,7 +5498,7 @@ bool CWallet::ProcessStealthOutput(const CTxDestination& address, std::vector<ui
54985498
if (sxAddr.IsNull())
54995499
continue;
55005500

5501-
if (!MatchPrefix(sxAddr.prefix.number_bits, sxAddr.prefix.bitfield, prefix, fHavePrefix)) {
5501+
if (!MatchPrefix(sxAddr.prefix_number_bits, sxAddr.prefix_bitfield, prefix, fHavePrefix)) {
55025502
continue;
55035503
}
55045504
if (!sxAddr.scan_secret.IsValid()) {
@@ -5717,7 +5717,7 @@ bool CWallet::ScanForOwnedOutputs(const CTransaction& tx)
57175717
return fIsMine;
57185718
}
57195719

5720-
bool CWallet::AddStealthAddress(const CStealthAddress& sxAddr, const CKey& skSpend)
5720+
bool CWallet::AddStealthAddress(const CStealthAddress& sxAddr)
57215721
{
57225722
LogPrintf("%s: %s\n", __func__, sxAddr.Encoded());
57235723
CWalletDB* pwdb = GetWalletDB();
@@ -5750,6 +5750,14 @@ CWalletDB* CWallet::GetWalletDB()
57505750
assert(pwdb);
57515751
return pwdb;
57525752
}
5753+
5754+
bool CWallet::HaveStealthAddress(const CKeyID& address) const
5755+
{
5756+
if (mapstealthAddresses.count(address) > 0)
5757+
return true;
5758+
return false;
5759+
}
5760+
57535761
//! End Stealth Address Support
57545762

57555763
// This should be called carefully:

src/wallet/wallet.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1226,10 +1226,11 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
12261226
int CheckForStealthTxOut(const CTxOut* pTxOut, const CTxOut* pTxData);
12271227
bool HasBDAPLinkTx(const CTransaction& tx, CScript& bdapOpScript);
12281228
bool ScanForOwnedOutputs(const CTransaction& tx);
1229-
bool AddStealthAddress(const CStealthAddress& sxAddr, const CKey& skSpend);
1229+
bool AddStealthAddress(const CStealthAddress& sxAddr);
12301230
bool AddStealthToMap(const std::pair<CKeyID, CStealthAddress>& pairStealthAddress);
12311231
bool AddToStealthQueue(const std::pair<CKeyID, CStealthKeyQueueData>& pairStealthQueue);
12321232
CWalletDB* GetWalletDB();
1233+
bool HaveStealthAddress(const CKeyID& address) const;
12331234

12341235
};
12351236

0 commit comments

Comments
 (0)