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

Add federated block-signing server implementation #4

Open
wants to merge 28 commits into
base: dvep
Choose a base branch
from

Conversation

maaku
Copy link

@maaku maaku commented Aug 14, 2021

This pull request adds a federated block-signer built into the Elements daemon. To use, load the wallets of N elements daemons with the signing keys for for a M-of-N proof script, and configure the nodes to talk to each other with -blocksignnode=. For a detailed example, see contrib/federation/blocksign.sh.

This pull request is a work-in-progress. The federation peer-to-peer code and block-signing capability works, but is currently in the form of a minimal proof-of-concept. There is potentially a lot more work that needs to be done to make it both robust and secure. In addition, to be used in production will probably require some sort of hardware wallet signing support.

The block signer has its own thread to manage active roles in the federation protocol.  Most state transitions happen in response to messages received over the network, and are therefore processed in the network thread.  However the master node for a round is responsible for kicking off the round with a block proposal, and it is the block-signer thread that does that.
src/federation.cpp Outdated Show resolved Hide resolved
src/federation.cpp Outdated Show resolved Hide resolved
Comment on lines 134 to 147
g_our_block_signing_key = pk;
}
#endif // ENABLE_WALLET
}

if (!g_our_block_signing_key.IsValid()) {
LogPrintf("Error: unable to determine block-signing key.\n");
return false;
}

std::sort(g_block_signing_keys.begin(),
g_block_signing_keys.end());

g_our_block_signing_key_index = 0;

Choose a reason for hiding this comment

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

a93c459

It seems like you could move the above wallet-check into the post-sorted loop below, and set both index and key. You could even move that entire thing into an ENABLED_WALLET ifdef, unless the plan is to somehow have a pubkey but not a privkey for our own key (external HWW signer or something).

Copy link
Author

Choose a reason for hiding this comment

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

That's precisely what I had in mind for production: a pubkey set as a configuration option, with the privkey stored within a secure hardware enclave or attached hardware wallet. I haven't really worked out the low-level details of this though. For example, do we keep the wallet enabled and add the pubkey as a watch-only key? Or do we remove the wallet and replicate the features we need for a smaller exposed attack surface? Honestly I haven't spend more than 5 minutes thinking about that yet. Obviously right now if you try to run this without the wallet enabled it will error out because it can't find any keys it recognizes.

Copy link
Author

Choose a reason for hiding this comment

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

I moved the wallet-check into the post-sorted loop as suggested because I think that's cleaner. Thanks for the suggestion. I'm not marking this as resolved yet though because of the other unresolved aspects.

src/federation.cpp Outdated Show resolved Hide resolved
src/federation.cpp Outdated Show resolved Hide resolved
src/federation.cpp Outdated Show resolved Hide resolved
src/federation.cpp Outdated Show resolved Hide resolved
src/federation.cpp Outdated Show resolved Hide resolved
src/federation.cpp Outdated Show resolved Hide resolved
if (!GenerateBlockProposal()) {
LogPrint(BCLog::FEDERATION, "%s: Unable to generate block proposal. Terminating round early.\n", __func__);
int64_t sec_since_epoch = nextblocktime.time_since_epoch().count();
g_roundno = sec_since_epoch / 5;

Choose a reason for hiding this comment

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

0107ee1

I am assuming several things here, but does this define the current round as the block time in seconds divided by 5 modulo member count? If so, won't this have a pretty poor distribution when there are e.g. 12 peers and 1 minute per block, since the peer will tend to be the same one each time? (12*5 = 60 s = 1 min = block interval).

Copy link
Author

Choose a reason for hiding this comment

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

This is a mistake. We should be dividing by the length of the period (60 seconds) to get the current round, of course. During development I had this shortened to 5 seconds so I didn't have to sit around waiting for blocks to be generated. Looks like I accidentally committed that constant change at this point.

We should make the round parameters configurable, but that's a larger issue.

Copy link
Author

Choose a reason for hiding this comment

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

Fixed the constant. Not marking as resolved until we decide how to make this configurable.

Choose a reason for hiding this comment

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

Ahh, that makes sense. Can't you use Params().GetConsensus().nPowTargetSpacing directly?

Copy link
Author

Choose a reason for hiding this comment

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

Well yes, that would be the obvious thing to do. Duh.

src/federation.cpp Outdated Show resolved Hide resolved
…ons.'

Move the search for our public key to the second post-sort loop.
…ock-signer.'

Switch -blocksignnode to use pubkey@ip:port syntax.
…ons.'

Switch -blocksignnode to use pubkey@ip:port syntax.
…ons.'

Make code clearer at the small cost of repeated op[] calls.
Remove FIXME note that doesn't need to live in the code.
…s, acks, and signatures.'

Terminate round early if we're not willing to sign the block.
Reduce redundant code copied from HandshakePeer
…s, acks, and signatures.'

Fix incorrect round length
…ons.'

int -> size_t to avoid compiler warnings
@maaku maaku marked this pull request as draft August 22, 2021 18:41
@maaku maaku marked this pull request as ready for review December 7, 2021 01:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants