-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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 Logging and persist async operation for Sapling migration #4002
Conversation
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.
ut(ACK+cov)
@@ -67,6 +67,7 @@ void AsyncRPCOperation_saplingmigration::main() { | |||
} | |||
|
|||
bool AsyncRPCOperation_saplingmigration::main_impl() { | |||
LogPrint("zrpcunsafe", "Beginning AsyncRPCOperation_saplingmigration. id=%s\n", getId()); |
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.
%s should be at the start of the log message, to follow the convention used by other async operations. Also makes it easier to visually identify when examining log file or perform parsing based on opid.
@@ -126,21 +140,30 @@ bool AsyncRPCOperation_saplingmigration::main_impl() { | |||
builder.AddSaplingOutput(ovkForShieldingFromTaddr(seed), migrationDestAddress, amountToSend - FEE); | |||
CTransaction tx = builder.Build().GetTxOrThrow(); | |||
if (isCancelled()) { | |||
LogPrint("zrpcunsafe", "%s: Canceled. Stopping.\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.
Runtime error will occur. Format specifier without any data to output. I guess you want a getId() here.
int noteIndex = 0; | ||
CCoinsViewCache coinsView(pcoinsTip); | ||
do { | ||
CAmount amountToSend = chooseAmount(availableFunds); | ||
auto builder = TransactionBuilder(consensusParams, targetHeight_, pwalletMain, pzcashParams, &coinsView, &cs_main); | ||
LogPrint("zrpcunsafe", "%s: Beginning creating transaction with Sapling output amount=%s\n", getId(), amountToSend - FEE); |
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.
Possible runtime error here. Format specifier is %s, but passing in CAmount. Missing FormatMoney() here?
@@ -108,6 +113,15 @@ bool AsyncRPCOperation_saplingmigration::main_impl() { | |||
} | |||
availableFunds -= fromNoteAmount; | |||
for (const CSproutNotePlaintextEntry& sproutEntry : fromNotes) { | |||
std::string data(sproutEntry.plaintext.memo().begin(), sproutEntry.plaintext.memo().end()); | |||
LogPrint("zrpcunsafe", "%s: Adding Sprout note input (txid=%s, vjoinsplit=%d, ciphertext=%d, amount=%s, memo=%s)\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.
ciphertext= is incorrect, as the uint8_t index of the joinsplit output is passed in here. Should be jsoutindex= (and in other asyncrpcoperation classes, where this line was copied from).
asyncrpcoperation_mergetoaddress.cpp:648: LogPrint("zrpcunsafe", "%s: spending note (txid=%s, vjoinsplit=%d, ciphertext=%d, amount=%s, height=%d, confirmations=%d)\n",
asyncrpcoperation_saplingmigration.cpp:117: LogPrint("zrpcunsafe", "%s: Adding Sprout note input (txid=%s, vjoinsplit=%d, ciphertext=%d, amount=%s, memo=%s)\n",
asyncrpcoperation_sendmany.cpp:787: LogPrint("zrpcunsafe", "%s: spending note (txid=%s, vjoinsplit=%d, ciphertext=%d, amount=%s, height=%d, confirmations=%d)\n",
asyncrpcoperation_sendmany.cpp:1051: LogPrint("zrpcunsafe", "%s: found unspent Sprout note (txid=%s, vjoinsplit=%d, ciphertext=%d, amount=%s, memo=%s)\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.
@Eirik0 commit 5600562 is missing the fix for mergetoaddress and one (of the two) in sendmany.
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.
Fixed and squashed in to the same commit.
++numTxCreated; | ||
amountMigrated += amountToSend; | ||
amountMigrated += amountToSend - FEE; |
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.
Why was the FEE excluded previously, but now included?
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.
This is to prevent confusion between this value and the amount_migrated
returned by z_getmigrationstatus
. It probably should have excluded the FEE originally.
@@ -604,7 +604,7 @@ void CWallet::RunSaplingMigration(int blockHeight) { | |||
// height N-5 | |||
if (blockHeight % 500 == 495) { | |||
std::shared_ptr<AsyncRPCQueue> q = getAsyncRPCQueue(); | |||
std::shared_ptr<AsyncRPCOperation> lastOperation = q->popOperationForId(saplingMigrationOperationId); | |||
std::shared_ptr<AsyncRPCOperation> lastOperation = q->getOperationForId(saplingMigrationOperationId); | |||
if (lastOperation != nullptr) { | |||
lastOperation->cancel(); |
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.
So now the base class cancel() will be invoked. Is that the intended behaviour? Will the cancel state be set correctly i.e. is the operation currently in READY state to transition to CANCEL state?
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.
Also note that the operation will still exist in an internal job map used by AsyncRPCQueue. That means, the queue will still try and process the operation. If the cancel state has been set correctly, this is fine. If not, the operation will be executed. Popping the operation removes the operation from the internal job map, which means the queue will have nothing to execute.
/**
* Return the operation for a given operation id and then remove the operation from internal storage.
*/
std::shared_ptr<AsyncRPCOperation> AsyncRPCQueue::popOperationForId(AsyncRPCOperationId id) {
std::shared_ptr<AsyncRPCOperation> ptr = getOperationForId(id);
if (ptr) {
std::lock_guard<std::mutex> guard(lock_);
// Note: if the id still exists in the operationIdQueue, when it gets processed by a worker
// there will no operation in the map to execute, so nothing will happen.
operation_map_.erase(id);
}
return ptr;
}
...
void AsyncRPCQueue::run(size_t workerId) {
...
if (!operation) {
// cannot find operation in map, may have been removed
} else if (operation->isCancelled()) {
// skip cancelled operation
} else {
operation->main();
}
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.
So now the base class cancel() will be invoked. Is that the intended behaviour? Will the cancel state be set correctly i.e. is the operation currently in READY state to transition to CANCEL state?
The intent of this is to cancel the operation if it is already running, this however does not work, and the fix does not seem to be simple (see #3988). What will currently happen is if a node is not finished generating migration transactions by the time we try to send them, then we will send what we have, but it will continue to generate transactions after the fact. These extra transactions will then be discarded in the next round of migrations.
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.
Also note that the operation will still exist in an internal job map used by AsyncRPCQueue. That means, the queue will still try and process the operation. If the cancel state has been set correctly, this is fine. If not, the operation will be executed. Popping the operation removes the operation from the internal job map, which means the queue will have nothing to execute.
This has been changed from pop to get
5600562
to
7f3b00b
Compare
Suggestion, we can probably catch many of the
With this change, running the test (without Eirik's latest commit) generates the expected error:
The only downside is that more lines get written to the test's I'd even suggest enabling all debug logging (for all RPC tests), and run the full test suite -- I bet we'd uncover many more of these format string bugs. I may just try that. |
@LarryRuane I like your suggestion. I will add this. |
7f3b00b
to
42daf46
Compare
The error found by @LarryRuane was in the |
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.
ACK with sprout-to-sapling test passing.
I rebased locally and there were no conflicts. |
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.
utACK (well... some testing, only ran the python test)
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.
re-utACK
42daf46
to
23d9826
Compare
@zkbot r+ |
📌 Commit 23d9826 has been approved by |
Add Logging and persist async operation for Sapling migration Currently zcashd will automatically remove the last async migration operations when it reaches the height where it sends the transactions it just made. This is not in alignment with other async operations, which are not removed until a node is restarted or a user calls `z_getoperationresult`. This PR removes the calls to pop the operations so that they can be accessed and reviewed later. In this PR I also correct the operation's `amount_migrated` field to exclude the transaction fee (this field existed for debugging purposes, but should be consistent with `z_getmigrationstatus`), and have included the list of migration txids in the operation's result (this is similar to the async rpcs such as `z_sendmany`). Documentation: none needed. Test plan: * After migration transactions have been created, list the opids and call `z_getoperationresult` on them. * Check that the operations' `amount_migrated` fields exclude the fee.
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.
Rereviewed from scratch after force push. utACK.
Currently zcashd will automatically remove the last async migration operations when it reaches the height where it sends the transactions it just made. This is not in alignment with other async operations, which are not removed until a node is restarted or a user calls
z_getoperationresult
. This PR removes the calls to pop the operations so that they can be accessed and reviewed later. In this PR I also correct the operation'samount_migrated
field to exclude the transaction fee (this field existed for debugging purposes, but should be consistent withz_getmigrationstatus
), and have included the list of migration txids in the operation's result (this is similar to the async rpcs such asz_sendmany
).Documentation: none needed.
Test plan:
z_getoperationresult
on them.amount_migrated
fields exclude the fee.