Skip to content
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

feat(rpc): submit chainlock signature if needed RPC #5765

Merged
merged 11 commits into from
Dec 19, 2023

Conversation

ogabrielides
Copy link
Collaborator

@ogabrielides ogabrielides commented Dec 13, 2023

Issue being fixed or feature implemented

Once Platform is live, there could be an edge case where the CL could arrive to an EvoNode faster through Platform quorum than regular P2P propagation.

What was done?

This PR introduces a new RPC submitchainlock with the following 3 mandatory parameters:

  • blockHash, signature and height.

Besides some basic tests:

  • If the block is unknown then the RPC returns an error (could happen if the node is stucked) the CL still proceed, RPC returns true and CL is broadcasted
  • If the signature is not verified then the RPC return an error.
  • If the node already has this CL, the RPC returns true.
  • If the node doesn't have this CL, it inserts it, broadcast it through the inv system and return true.

How Has This Been Tested?

feature_llmq_chainlocks.py was modified with the following scenario:

  1. node0 is isolated from the rest of the network
  2. node1 mines a new block and waits for CL
  3. Make sure node0 doesn't know the new block/CL (by checking getbestchainlock())
  4. CL is submitted via the new RPC on node0
  5. checking getbestchainlock() and make sure the CL was processed + 'known_block' is false
  6. reconnect node0

Breaking Changes

no

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have made corresponding changes to the documentation
  • I have assigned this pull request to a milestone (for repository code-owners and collaborators only)

@ogabrielides ogabrielides changed the title feat(rpc): add chainlock signature if needed RPC. feat(rpc): add chainlock signature if needed RPC Dec 13, 2023
@ogabrielides ogabrielides added the RPC Some notable changes to RPC params/behaviour/descriptions label Dec 13, 2023
@ogabrielides ogabrielides added this to the 20.1 milestone Dec 13, 2023
@PastaPastaPasta
Copy link
Member

submitchainlock would make this more in line with other RPCs such as submitblock or submitrawtransaction

@ogabrielides ogabrielides changed the title feat(rpc): add chainlock signature if needed RPC feat(rpc): submit chainlock signature if needed RPC Dec 14, 2023
Copy link

@UdjinM6 UdjinM6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there could be an edge case where the CL could arrive to an EvoNode faster through Platform quorum than regular P2P propagation.

But why is this an issue exactly? Why do we want to submit chainlock ourselves instead of maybe verifying it via verifychainlock and allowing the node to receive it the usual way?

Also, that's a lot of code duplication with verifychainlock but for some reason behaviour differs in some parts. Why is that?

src/rpc/quorums.cpp Outdated Show resolved Hide resolved
src/rpc/quorums.cpp Outdated Show resolved Hide resolved
src/rpc/quorums.cpp Outdated Show resolved Hide resolved
src/rpc/quorums.cpp Outdated Show resolved Hide resolved
src/rpc/quorums.cpp Outdated Show resolved Hide resolved
@PastaPastaPasta
Copy link
Member

What if we simplify this down to

Subject: [PATCH] simplify everything
---
Index: src/rpc/quorums.cpp
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/rpc/quorums.cpp b/src/rpc/quorums.cpp
--- a/src/rpc/quorums.cpp	(revision 9b401107067b73eb0719813783ae150ae008f2da)
+++ b/src/rpc/quorums.cpp	(date 1702570318089)
@@ -1007,49 +1007,23 @@
 
     const uint256 nBlockHash(ParseHashV(request.params[0], "blockHash"));
 
-    const NodeContext& node = EnsureAnyNodeContext(request.context);
-    const ChainstateManager& chainman = EnsureChainman(node);
-
     const int nBlockHeight = ParseInt32V(request.params[2], "blockHeight");
-    CBlockIndex* pIndex{nullptr};
-    {
-        LOCK(cs_main);
-        if (nBlockHeight < 0) {
-            throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid block height");
-        }
-        if (nBlockHeight > chainman.ActiveChain().Height()) {
-            throw JSONRPCError(RPC_INVALID_PARAMETER, "future block");
-        }
-        pIndex = chainman.ActiveChain()[nBlockHeight];
-        if (nBlockHash != pIndex->GetBlockHash()) {
-            throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid blockhash");
-        }
+    if (nBlockHeight <= 0) {
+        throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid block height");
     }
 
     CBLSSignature sig;
-    if (pIndex) {
-        bool use_legacy_signature = !llmq::utils::IsV19Active(pIndex);
-        if (!sig.SetHexStr(request.params[1].get_str(), use_legacy_signature)) {
-            throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid signature format");
-        }
-    } else {
-        if (!sig.SetHexStr(request.params[1].get_str(), false) &&
-            !sig.SetHexStr(request.params[1].get_str(), true)
-                ) {
-            throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid signature format");
-        }
+    if (!sig.SetHexStr(request.params[1].get_str(), false) &&
+        !sig.SetHexStr(request.params[1].get_str(), true) ) {
+        throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid signature format");
     }
 
-    const LLMQContext& llmq_ctx = EnsureLLMQContext(node);
-    llmq::CChainLockSig clsig = llmq::CChainLockSig(nBlockHeight, nBlockHash, sig);
+    const LLMQContext& llmq_ctx = EnsureLLMQContext(EnsureAnyNodeContext(request.context));
+    auto clsig = llmq::CChainLockSig(nBlockHeight, nBlockHash, sig);
     if (!llmq_ctx.clhandler->VerifyChainLock(clsig)) {
         throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid signature");
     }
 
-    if (llmq_ctx.clhandler->HasChainLock(nBlockHeight, nBlockHash)) {
-        return true;
-    }
-
     llmq_ctx.clhandler->ProcessNewChainLock(-1, clsig, ::SerializeHash(clsig));
     return true;
 }

@ogabrielides
Copy link
Collaborator Author

@UdjinM6

there could be an edge case where the CL could arrive to an EvoNode faster through Platform quorum than regular P2P propagation.

But why is this an issue exactly? Why do we want to submit chainlock ourselves instead of maybe verifying it via verifychainlock and allowing the node to receive it the usual way?

Also, that's a lot of code duplication with verifychainlock but for some reason behaviour differs in some parts. Why is that?

I will try to answer to all of your comments here.

But why is this an issue exactly? Why do we want to submit chainlock ourselves instead of maybe verifying it via verifychainlock and allowing the node to receive it the usual way?

Regarding Core only, this is indeed not an issue. But if we see Core+Platform as a system, this is a race condition for Chainlock signature. This PR will help Platform avoid waiting for Core to get the CL from regular propagation.

We usually relay "future" chainlocks (check ProcessNewChainLock). Why is it not the case here?

Didn't know that. All right, will change it. That means that height can be optional yes.

@ogabrielides
Copy link
Collaborator Author

@UdjinM6 Applied @PastaPastaPasta 's suggestions.
It still feels weird to have optional height and accept CL for "future" blocks.

test/functional/feature_llmq_chainlocks.py Outdated Show resolved Hide resolved
src/rpc/quorums.cpp Outdated Show resolved Hide resolved
src/rpc/server.cpp Show resolved Hide resolved
test/functional/rpc_platform_filter.py Outdated Show resolved Hide resolved
UdjinM6
UdjinM6 previously approved these changes Dec 16, 2023
Copy link

@UdjinM6 UdjinM6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK

@UdjinM6
Copy link

UdjinM6 commented Dec 16, 2023

Not sure what's going on but Gitlab CI just won't start here for some reason...

EDIT: pushed the branch manually https://gitlab.com/dashpay/dash/-/pipelines/1109351723

UdjinM6
UdjinM6 previously approved these changes Dec 16, 2023
Copy link

@UdjinM6 UdjinM6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re-utACK

@ogabrielides
Copy link
Collaborator Author

re-utACK

Thanks for approving your changes 😃

Copy link
Member

@PastaPastaPasta PastaPastaPasta left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK for squash merge

Copy link
Member

@PastaPastaPasta PastaPastaPasta left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry; needs release notes first; otherwise looks good

knst
knst previously approved these changes Dec 17, 2023
Copy link
Collaborator

@knst knst left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

knst
knst previously approved these changes Dec 18, 2023
Copy link
Collaborator

@knst knst left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Collaborator

@thephez thephez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small typo fix in release notes

doc/release-notes-5765.md Outdated Show resolved Hide resolved
Copy link
Collaborator

@thephez thephez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Copy link

@UdjinM6 UdjinM6 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK

Copy link
Member

@PastaPastaPasta PastaPastaPasta left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK for squash merge

@PastaPastaPasta PastaPastaPasta merged commit 3bc77a6 into dashpay:develop Dec 19, 2023
9 checks passed
@ogabrielides ogabrielides deleted the rpc_insert_clsig branch December 19, 2023 10:28
@QuantumExplorer
Copy link
Member

So we have If the block is unknown then the RPC returns an error (could happen if the node is stucked) If the signature is not verified then the RPC return an error.

Is it really an error if the block is unknown?, Shouldn't we just be returning false? From platform's perspective this is not an error, and while we do vote down the proposal, this is not an "error" pathway.

@knst
Copy link
Collaborator

knst commented Jan 8, 2024

Is it really an error if the block is unknown?, Shouldn't we just be returning false?

sounds reasonable.
So, if we don't have a block with blockHash known yet - we would return false, all other cases are not changed.

@QuantumExplorer
Copy link
Member

Yes @knst could you make that happen?

assert_equal(best_0['signature'], best_1['signature'])
assert_equal(best_0['known_block'], False)
self.reconnect_isolated_node(0, 1)
self.sync_all()
Copy link
Collaborator

@knst knst Jan 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@QuantumExplorer that works not as described in the PR description.
If block is unknown -> CL is still submitted and if it succeeds we return true.

We do not return error if block is not know, we just quietly process it, verify, re-transmit to network and add to known chainlocks.
Nothing to change already.

here's even tests for it.

@QuantumExplorer
Copy link
Member

@knst Part of the requirement is that it return true or false based on whether it had the block (so we didn't have to call another RPC), right now it can't ever return false.

@PastaPastaPasta
Copy link
Member

Talked to Sam; we want change the API in this way
First (if height <= bestchainlockheight return bestchainlockheight)
else submitchainlock then return bestchainlockheight

This provides two benefits; 1st is a short circuiting if we know the proposed chainlock could not overwrite the current one (due to lower height)
Second; returns the bestchainlockheight to platform

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
RPC Some notable changes to RPC params/behaviour/descriptions
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants