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
parent
chain type of block does not end up in thechild
chain - we must still be able to read both
child
andparent
chain 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.