Aeternity node is an implementation of the Aeternity protocol in Erlang. The following is a description of how this can be adapted to build Hyperchains on top of AE blockchain using the aeternity node.
Block validation
Header structure
The plan is to keep the child chain headers as they are. If we can get away without changing them, that would be great. Currently we don’t see a reason to change them.
In order to be able to validate the child chain keyblock, one needs a reference to the parent keyblock that ends the child election cycle. At the moment the structure of the keyblock is:
-record(key_header, {
height = 0 :: height(),
prev_hash = <<0:?BLOCK_HEADER_HASH_BYTES/unit:8>> :: block_header_hash(),
prev_key = <<0:?BLOCK_HEADER_HASH_BYTES/unit:8>> :: block_header_hash(),
root_hash = <<0:?STATE_HASH_BYTES/unit:8>> :: state_hash(),
target = ?HIGHEST_TARGET_SCI :: aeminer_pow:sci_target(),
nonce = 0 :: non_neg_integer(),
time = 0 :: non_neg_integer(),
version :: non_neg_integer(),
pow_evidence = no_value :: aeminer_pow_cuckoo:solution() | no_value,
miner = <<0:?MINER_PUB_BYTES/unit:8>> :: miner_pubkey(),
beneficiary = <<0:?BENEFICIARY_PUB_BYTES/unit:8>> :: beneficiary_pubkey(),
info = <<>> :: bin_info()
}).
In the context of hyperchains, nonce and pow_evidence are unnecessary. In their place we need a parent_prev_key instead
Approach 1: recycle the pow_evidence
Since:
-type solution() :: [integer()].
We could reuse the structure and store there the prev hash of the parent chain as a list of integers. Then expose a function to “read” the pow_evidence into the correct format. I think this is a wrong approach but it could be explored further.
Approach 2: change the record structure with a plugin approach
Add an extra field in the #key_header{}
extra = #{} :: map()
We can change the serialization of the keyblocks to accomodate it. In this map we can store anything and thus - expand it. Some problems that could arise are:
- we must be extra cautious so a
parentchain type of block does not end up in thechildchain - we must still be able to read both
childandparentchain keyblocks
Approach 3: bloat the #key_block{}
Add another record #child_key_block{} and addapt all the module exported functions accordingly. Also expose a new one for the parent_prev_key that works for #child_key_block{} only. This will have a lot of code and test duplication.
parent and child node incorporation
In order for the child node to be able to validate who the elected leader is, it should fetch the latest parent chain keyblock. How to structure this in the code has two different approaches, each with its drawbacks.
Approach 1: separate parent and child nodes
In this scenario we have a parent chain node that is independent running node. It syncs with the parent chain and it has independent DB.
The child chain calls its APIs in order to fetch new keyblocks. It should have a polling mechanism to detect forks on the parent chain. This is especially important with regards of commitments posting. This is the fastest MVP approach.
A big drawback is that the HTTP requests introduce latency to the keyblock validation. This means that if a node receives a new child chain keyblock from the next leader, it should fetch the parent chain’s keyblock as it is the source of entropy. This request will take significantly longer than what is currently the Cuckoo’s cycle validation. This might open the gates for DoS attacks, flooding certain nodes with keyblocks that take time to proove invalid. This is OK for MVP but it might not OK for a real-life scenario. This should be investigated further.
Approach 2: integrate parent and child nodes
In this scenario the child and the parent nodes are one piece of software - one is baked in the other. This results in much faster child chain keyblock validation. This requires duplication of whole applications: two sync-s, two DBs and etc. This will not be a trivial change and will require some heavy testing to make sure the two nodes do not end up entangled.
