-
Notifications
You must be signed in to change notification settings - Fork 35.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add option to specify rescan starting timestamp to RPC import calls #6570
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -63,7 +63,7 @@ std::string DecodeDumpString(const std::string &str) { | |
for (unsigned int pos = 0; pos < str.length(); pos++) { | ||
unsigned char c = str[pos]; | ||
if (c == '%' && pos+2 < str.length()) { | ||
c = (((str[pos+1]>>6)*9+((str[pos+1]-'0')&15)) << 4) | | ||
c = (((str[pos+1]>>6)*9+((str[pos+1]-'0')&15)) << 4) | | ||
((str[pos+2]>>6)*9+((str[pos+2]-'0')&15)); | ||
pos += 2; | ||
} | ||
|
@@ -76,15 +76,16 @@ UniValue importprivkey(const UniValue& params, bool fHelp) | |
{ | ||
if (!EnsureWalletIsAvailable(fHelp)) | ||
return NullUniValue; | ||
if (fHelp || params.size() < 1 || params.size() > 3) | ||
|
||
if (fHelp || params.size() < 1 || params.size() > 4) | ||
throw runtime_error( | ||
"importprivkey \"bitcoinprivkey\" ( \"label\" rescan )\n" | ||
"importprivkey \"bitcoinprivkey\" ( \"label\" rescan timestamp)\n" | ||
"\nAdds a private key (as returned by dumpprivkey) to your wallet.\n" | ||
"\nArguments:\n" | ||
"1. \"bitcoinprivkey\" (string, required) The private key (see dumpprivkey)\n" | ||
"2. \"label\" (string, optional, default=\"\") An optional label\n" | ||
"3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n" | ||
"4. timestamp (numeric, optional, default=0) Rescan starts at timestamp\n" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the default should be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ruimarinho: note that I'm not sure, how the time might be further affected by setmocktime, so I mention it just for completeness. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would leave the default value as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not just repurpose the rescan parameter? That is, if it's a boolean, you get the current behaviour, but if it's a Number, you interpret it as a timestamp. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @luke-jr that seems less clear and more confusing for no good reason (unless I'm missing the good reason, of course). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @luke-jr So your suggestion is something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about something like... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would work for me. And then throw the Edit: fixed typo per @jtimon There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you mean "is not numeric as expected"? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
@MarcoFalke Exactly. This is clean. |
||
"\nNote: This call can take minutes to complete if rescan is true.\n" | ||
"\nExamples:\n" | ||
"\nDump a private key\n" | ||
|
@@ -114,6 +115,14 @@ UniValue importprivkey(const UniValue& params, bool fHelp) | |
if (params.size() > 2) | ||
fRescan = params[2].get_bool(); | ||
|
||
CBlockIndex *pindex = chainActive.Genesis(); | ||
if (params.size() > 3) { | ||
int nTime = params[3].get_int(); | ||
pindex = chainActive.FindLatestBefore(nTime); | ||
if (!pindex) | ||
throw JSONRPCError(RPC_INVALID_PARAMETER, "No block before timestamp"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With the current implementation of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's also a useless error - the user doesn't care if there was a block before their timestamp... just rescan the entire chain in that case. |
||
} | ||
|
||
CBitcoinSecret vchSecret; | ||
bool fGood = vchSecret.SetString(strSecret); | ||
|
||
|
@@ -142,7 +151,7 @@ UniValue importprivkey(const UniValue& params, bool fHelp) | |
pwalletMain->nTimeFirstKey = 1; // 0 would be considered 'no value' | ||
|
||
if (fRescan) { | ||
pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), true); | ||
pwalletMain->ScanForWalletTransactions(pindex, true); | ||
} | ||
} | ||
|
||
|
@@ -180,16 +189,17 @@ UniValue importaddress(const UniValue& params, bool fHelp) | |
{ | ||
if (!EnsureWalletIsAvailable(fHelp)) | ||
return NullUniValue; | ||
if (fHelp || params.size() < 1 || params.size() > 4) | ||
|
||
if (fHelp || params.size() < 1 || params.size() > 5) | ||
throw runtime_error( | ||
"importaddress \"address\" ( \"label\" rescan p2sh )\n" | ||
"importaddress \"address\" ( \"label\" rescan p2sh timestamp)\n" | ||
"\nAdds a script (in hex) or address that can be watched as if it were in your wallet but cannot be used to spend.\n" | ||
"\nArguments:\n" | ||
"1. \"script\" (string, required) The hex-encoded script (or address)\n" | ||
"2. \"label\" (string, optional, default=\"\") An optional label\n" | ||
"3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n" | ||
"4. p2sh (boolean, optional, default=false) Add the P2SH version of the script as well\n" | ||
"5. timestamp (numeric, optional, default=0) Rescan starts at timestamp\n" | ||
"\nNote: This call can take minutes to complete if rescan is true.\n" | ||
"If you have the full public key, you should call importpublickey instead of this.\n" | ||
"\nExamples:\n" | ||
|
@@ -213,6 +223,14 @@ UniValue importaddress(const UniValue& params, bool fHelp) | |
if (params.size() > 2) | ||
fRescan = params[2].get_bool(); | ||
|
||
CBlockIndex *pindex = chainActive.Genesis(); | ||
if (params.size() > 4) { | ||
int nTime = params[4].get_int(); | ||
pindex = chainActive.FindLatestBefore(nTime); | ||
if (!pindex) | ||
throw JSONRPCError(RPC_INVALID_PARAMETER, "No block before timestamp"); | ||
} | ||
|
||
// Whether to import a p2sh version, too | ||
bool fP2SH = false; | ||
if (params.size() > 3) | ||
|
@@ -234,7 +252,7 @@ UniValue importaddress(const UniValue& params, bool fHelp) | |
|
||
if (fRescan) | ||
{ | ||
pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), true); | ||
pwalletMain->ScanForWalletTransactions(pindex, true); | ||
pwalletMain->ReacceptWalletTransactions(); | ||
} | ||
|
||
|
@@ -248,12 +266,13 @@ UniValue importpubkey(const UniValue& params, bool fHelp) | |
|
||
if (fHelp || params.size() < 1 || params.size() > 4) | ||
throw runtime_error( | ||
"importpubkey \"pubkey\" ( \"label\" rescan )\n" | ||
"importpubkey \"pubkey\" ( \"label\" rescan timestamp)\n" | ||
"\nAdds a public key (in hex) that can be watched as if it were in your wallet but cannot be used to spend.\n" | ||
"\nArguments:\n" | ||
"1. \"pubkey\" (string, required) The hex-encoded public key\n" | ||
"2. \"label\" (string, optional, default=\"\") An optional label\n" | ||
"3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n" | ||
"4. timestamp (numeric, optional, default=0) Rescan starts at timestamp\n" | ||
"\nNote: This call can take minutes to complete if rescan is true.\n" | ||
"\nExamples:\n" | ||
"\nImport a public key with rescan\n" | ||
|
@@ -276,6 +295,14 @@ UniValue importpubkey(const UniValue& params, bool fHelp) | |
if (params.size() > 2) | ||
fRescan = params[2].get_bool(); | ||
|
||
CBlockIndex *pindex = chainActive.Genesis(); | ||
if (params.size() > 3) { | ||
int nTime = params[3].get_int(); | ||
pindex = chainActive.FindLatestBefore(nTime); | ||
if (!pindex) | ||
throw JSONRPCError(RPC_INVALID_PARAMETER, "No block before timestamp"); | ||
} | ||
|
||
if (!IsHex(params[0].get_str())) | ||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey must be a hex string"); | ||
std::vector<unsigned char> data(ParseHex(params[0].get_str())); | ||
|
@@ -290,7 +317,7 @@ UniValue importpubkey(const UniValue& params, bool fHelp) | |
|
||
if (fRescan) | ||
{ | ||
pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), true); | ||
pwalletMain->ScanForWalletTransactions(pindex, true); | ||
pwalletMain->ReacceptWalletTransactions(); | ||
} | ||
|
||
|
@@ -302,7 +329,7 @@ UniValue importwallet(const UniValue& params, bool fHelp) | |
{ | ||
if (!EnsureWalletIsAvailable(fHelp)) | ||
return NullUniValue; | ||
|
||
if (fHelp || params.size() != 1) | ||
throw runtime_error( | ||
"importwallet \"filename\"\n" | ||
|
@@ -388,9 +415,7 @@ UniValue importwallet(const UniValue& params, bool fHelp) | |
file.close(); | ||
pwalletMain->ShowProgress("", 100); // hide progress dialog in GUI | ||
|
||
CBlockIndex *pindex = chainActive.Tip(); | ||
while (pindex && pindex->pprev && pindex->GetBlockTime() > nTimeBegin - 7200) | ||
pindex = pindex->pprev; | ||
CBlockIndex *pindex = chainActive.FindLatestBefore(nTimeBegin - 7200); | ||
|
||
if (!pwalletMain->nTimeFirstKey || nTimeBegin < pwalletMain->nTimeFirstKey) | ||
pwalletMain->nTimeFirstKey = nTimeBegin; | ||
|
@@ -409,7 +434,7 @@ UniValue dumpprivkey(const UniValue& params, bool fHelp) | |
{ | ||
if (!EnsureWalletIsAvailable(fHelp)) | ||
return NullUniValue; | ||
|
||
if (fHelp || params.size() != 1) | ||
throw runtime_error( | ||
"dumpprivkey \"bitcoinaddress\"\n" | ||
|
@@ -447,7 +472,7 @@ UniValue dumpwallet(const UniValue& params, bool fHelp) | |
{ | ||
if (!EnsureWalletIsAvailable(fHelp)) | ||
return NullUniValue; | ||
|
||
if (fHelp || params.size() != 1) | ||
throw runtime_error( | ||
"dumpwallet \"filename\"\n" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As already commented, i think we should go down serval blocks (~144) because of possible inaccurate timestamps.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jonasschnelli what is the variance on timestamps? Is ~144 enough (or to much)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The cost of rescaning 144 blocks deeper (~1day) is pretty low (+some seconds/minutes). Given that the source of the timestamp is unknown (user could enter it manually because he know when he approx. created the key, or other wallet software did mess around with timezones, etc.) this cost of +144 blocks is probably worth taking.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jonasschnelli However if you know the exact timestamp then that extra time is unnecessary. For a person the offset may be useful, but for a system I think not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed with @promag, a system can add the safety net of ~144 blocks to the timestamp if necessary and a person likely knows what they are doing if they opt in for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jonasschnelli have to agree with @ruimarinho, this safety net could easily be added by the API user, if desired.
Overall, the only danger for this lies in people using the
timestamp
aspect of the API instead of a block height.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe just using the median time past is sufficient?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using the median time past seems to solve the concern without introducing much extra complexity.