kaspanet / kaspad

Kaspad was the reference full node Kaspa implementation written in Go (golang), now rewritten in Rust: https://github.com/kaspanet/rusty-kaspa
ISC License
449 stars 230 forks source link

Use a protocol that skips downloading headers and starts by syncing from the pruning point #1734

Open someone235 opened 3 years ago

someone235 commented 3 years ago

The node should just ask its peers what is the pruning point with the most blue work, and download its antipast. As a first step the node should not validate that the pruning point actually has the most blue work. The validation part will be implemented after https://github.com/kaspanet/research/issues/3 is specked

someone235 commented 3 years ago

The full protocol will look like this:

The syncer sends:

  1. For each block b in {P} ∪ P.Anticone:
    • b.DAAScore
    • b.DAAWindow
    • b.GhostdagData and GhostdagData for the last k blocks in b.SelectedChain
  2. {P} ∪ P.Antipast headers

The syncee then applies the following steps:

  1. It setups a new temporary consensus instance. If the header validation will fail, the new instance will be deleted.

  2. The syncee should manually set all the details related to #1 in its data stores.

  3. The syncee creates a virtual genesis vg with hardcoded hash, and replaces the old genesis with it (we should have a genesis store for that) so everywhere that assumes genesis hash will use vg.hash.

  4. When the syncee is inserting block headers from #2 she validates them like any other header, except that:

    • When updating block relation store, it should ignore parents that are not provided by the syncer.
    • If the block doesn't have any known parents, it should point to vg
  5. The syncee replaces the old consensus with the new one.

  6. The syncee requests P.Antipast full blocks and P.UtxoSet like the current protocol.

In order to allow temporary consensus we'll change NewConsensus signature to NewConsensus(config *Config, db infrastructuredatabase.Database, prefix byte) and maintain a list in the database for all prefixes (maximum of 2), and a key that points to the current active prefix. consensusdatabase.New will get the prefix as an extra param and will prepend it to all db calls. We'll add a new function to Domain that will be called SetConsensus(consensus externalapi.Consensus, prefix byte) that will make the database replace the prefix to the new consensus, set Domain.Consensus = consensus, and will delete all the data that is related to the old prefix (and eventually the prefix itself from the list). In case anything fails, whenever a new node is launching it will delete all data that is related to non-active prefixes.

someone235 commented 3 years ago

One problem that we have when pruning headers is that when we're using time-based relative time locks we can't determine the timestamp of the block where the UTXO was created if we prune its header. I suggest solving it by cancelling the functionality of time-based relative time locks, and leave only DAA score relative time locks

svarogg commented 3 years ago

One problem that we have when pruning headers is that when we're using time-based relative time locks we can't determine the timestamp of the block where the UTXO was created if we prune its header. I suggest solving it by cancelling the functionality of time-based relative time locks, and leave only DAA score relative time locks

I think this is somewhat presumptuous to remove such a basic feature. Instead, I suggest including the timestamp inside the UTXOEntry. It will be somewhat complicated, since all utxo-diff comparisons would have to take both DAAScore and timestamp, but it's not going to be the end of the world IMHO.

someone235 commented 3 years ago

I think this is somewhat presumptuous to remove such a basic feature. Instead, I suggest including the timestamp inside the UTXOEntry. It will be somewhat complicated, since all utxo-diff comparisons would have to take both DAAScore and timestamp, but it's not going to be the end of the world IMHO.

It's a good idea, but we just need to remember it means 8 extra bytes per UTXO

hashdag commented 3 years ago

I vote for cancelling this functionality, let's ship the MVP

someone235 commented 3 years ago

Once IBD is triggered (from an orphan block as usual), the flow will look like this:

  1. We'll try to find the highest shared block hash between the nodes (not deeper than the pruning point in the syncee side).
  2. If there's such hash the syncee requests the full blocks from that point, validates them, and resolve the virtual after that.
  3. Otherwise, the syncee request the blue work of the sent orphan.
  4. If it's lower than the syncee virtual blue work, the syncee stops the IBD.
  5. If it's higher than the syncee virtual blue work, the syncee requests the headers proof (TBD).
  6. If the proof is invalid, the syncee bans the peer.
  7. The syncee creates staging consensus, if 7-8 fails, she deletes the staging consensus and bans the peer.
  8. The syncee requests the pruning point and its anticone with meta data (DAA score etc...).
  9. The syncee requests the full blocks in the future of the pruning point and validates them.
  10. The syncee commits the staged consensus.
  11. The syncee resolves the virtual.