Veil-Project / veil

Veil-Project
MIT License
118 stars 91 forks source link

Implement RingCT Staking #1019

Open Zannick opened 1 year ago

Zannick commented 1 year ago

Summary

This PR implements the functionality necessary for the creation and validation of blocks via Proof of Stake using unspent RingCT outputs, as described in #1015 and taking much inspiration (and code) from #678.

This does not yet enable RingCT staking (by placing the default start at block 50 million).

Details

Potential RingCT inputs are divided into brackets; the stake weight of a coin corresponds to the minimum value of its bracket, with some higher levels further reduced, similar to zerocoin (see #1015 for details). A RingCT output becomes eligible for staking after 1000 blocks, same as zerocoin.

A RingCT stake block always has two key transactions, the coinbase and the coinstake. The coinbase contains either an empty standard output (most blocks), or the standard outputs from the superblock, plus two RingCT outputs which provably sum to the block stake reward. The coinstake contains a single RingCT input (the staking coin), and three outputs: an empty standard output indicating that it's a coinstake, a data output, and a RingCT output of the same value as the input. The rangeproof for this transaction proves that the input is large enough to pass the stake difficulty, by providing a min_value equal to the minimum value of a bracket. For additional privacy, this is not the bracket the coin is in but the lowest possible bracket that still passes the stake difficulty.

The block is signed using the same key as the staking coin.

Other changes

[^1]: Possibly I should split this out for a separate review, though I'm not sure what it affects.

Potential improvements (for the project as a whole)

Cache the pubkey used to mark the RingCT stake uniqueness (#678 does this), since it may be repetitive across stake attempts to otherwise access it.

Remove the brackets and use a flatter scale with some high cutoff like 1M, given that we can reduce the rangeproof min_value anyway.

Add separate config settings for staking via Zerocoin or RingCT.

Add UI elements for the GUI wallet.

Add new tests/fix existing tests. :)

What next?

This PR needs a thorough review, including a good security review that it won't be possible to generate free coins, and testing in dev. We can discuss further features as well. Finally, we have to pick a height at which to enable on testnet (causing a fork).

Zannick commented 1 year ago

devnet will start ringct staking alongside zerocoin staking at height 25000.

Zannick commented 1 year ago

DevNet mining is underway. Sean notes:

In the GUI the RingCT stakes are incorrectly labeled as "Mined on 3txz...longStealthAddress". It's easy to identify them with listtransactions.

We should update those transactions to be RingCT stakes. The transactions themselves will look similar to mining transactions; we'll have to look at the stake transaction next to it.

seanPhill commented 1 year ago

DevNet mining is underway. Sean notes:

In the GUI the RingCT stakes are incorrectly labeled as "Mined on 3txz...longStealthAddress". It's easy to identify them with listtransactions.

We should update those transactions to be RingCT stakes. The transactions themselves will look similar to mining transactions; we'll have to look at the stake transaction next to it.

A second case in the display of (RingCT stake winning) transactions in the GUI is that (maybe if the winning RingCT utxo is seen by the wallet first) it displays as "Zerocoin Stake", but shows the value of the RingCT utxo. Users would be confused as their wallet will say they gained 18001.99083067 Veil, (magically) for example, but their balance will of course have only gained the amount of the block reward (50 Veil on devnet, and always ten Veil on mainnet).

Additional note: These entries in the GUI list where the size of the RingCT winning utxo is shown (as "Zerocoin Stake") are actually matched by the other GUI entry for "Mined on 3tXz..." 50 VEIL (or as it would be on mainnet, 10 Veil.

seanPhill commented 1 year ago

While syncing or resyncing we might need to look into this error too. (Much repeated!)

ERROR: ProcessNewBlock: AcceptBlock FAILED (bad-block-sig, PoS block signature not valid (code 16))(-1)

The above is happening on a peer trying to sync, while the below is happening on the leading wallet, even though this block has already been built on top of and is over fifty blocks confirmed.

Console:

getblockhash 25254
4bbbedcd972c8372e4810ca2af4420071713424392a2bfc8478563411d43a073

debug.log: (much repeated)


2023-01-20T20:25:57Z Reject block code 16: bad-block-sig: hash 4bbbedcd972c8372e4810ca2af4420071713424392a2bfc8478563411d43a073
2023-01-20T20:25:57Z received: getdata (37 bytes) peer=8
2023-01-20T20:25:57Z received getdata (1 invsz) peer=8
2023-01-20T20:25:57Z received getdata for: witness-block 4bbbedcd972c8372e4810ca2af4420071713424392a2bfc8478563411d43a073 peer=8
2023-01-20T20:25:57Z sending block (10309 bytes) peer=8
2023-01-20T20:25:57Z received: reject (53 bytes) peer=8
2023-01-20T20:25:57Z Reject block code 16: bad-block-sig: hash 4bbbedcd972c8372e4810ca2af4420071713424392a2bfc8478563411d43a073
2023-01-20T20:25:57Z received: getdata (37 bytes) peer=8
2023-01-20T20:25:57Z received getdata (1 invsz) peer=8
2023-01-20T20:25:57Z received getdata for: witness-block 4bbbedcd972c8372e4810ca2af4420071713424392a2bfc8478563411d43a073 peer=8
2023-01-20T20:25:57Z sending block (10309 bytes) peer=8
2023-01-20T20:25:57Z received: reject (53 bytes) peer=8

As I don't think any other peer has got past that block, I think I will invalidate it.

(Even after invalidatingblock and a restart, the "leading" wallet refuses to let go of the subsequently staked blocks, while any reconsidering of the block results in it being rejected because of a "bod-block-sig".) Will try the snapshot again, and use genoverride=1 when ready. (Didn't get to wait to use genoverride, as the node, like others goes off and generates RingCT-staked blocks that duplicate the block height of another node or two that it was supposedly waiting to sync from.)

Another problem after resyncing.

2023-01-21T00:25:17Z ERROR: ValidateBlockSignature: No pubkeys verified for RingCT stake 2023-01-21T00:25:17Z *** Corrupt block found indicating potential hardware failure; shutting down

Currently I have TWO happily synced and staking devnet wallets [only].

seanPhill commented 1 year ago

While syncing or resyncing we might need to look into this error too. (Much repeated!) ... Currently I have TWO happily synced and staking devnet wallets [only].

WHEN a wallet resynced from snapshot or otherwise has several blocks forked it is failing to stop requesting the same block from the connect=192.168.0... peer with ERROR: ProcessNewBlock: AcceptBlock FAILED (bad-block-sig, PoS block signature not valid (code 16))(-1) even though I've already tried invalidateblock (a slightly earlier bad block) as the replacement for that block is rejected, while the perfectly happy forced-connected node that is synced and staking along with the other peer, is accumulating MILLIONS of banscore, as the connection is force, so it won't disconnect.

BUT, the cause of the third wallet forking immediately after starting up after loading the snapshot, without syncing from the running wallets, is that it was not a locked wallet, and it didn't wait to sync after starting!! ... Now this third wallet is locked and HAS synced and has no gazillion of errors accumulating in the debug log.

What I don't know yet is whether any of this was caused by anything less reliable in the code, or it was just an unlucky data corruption due to the leading wallets being on my power grid as it suffered multiple outages.