MadNet
Mad Network Layer 1
Environment Setup
First, clone this repository.
Next, clone the bridge repository to the same directory as MadNet;
that is, if this repository is located at the path ~/mn/MadNet
,
then the bridge repository should be located at the path ~/mn/bridge
.
Also, clone the MadNetWallet-JS repository;
directions for installing the Wallet software will be described below.
Build MadNet
To build the project, run the following command in the root
of the repository:
make build
Build Wallet
Install NodeJS
Run the following commands to install NodeJS
:
sudo apt -y install curl dirmngr apt-transport-https lsb-release ca-certificates
curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
sudo apt-get install -y nodejs
This is required to build the wallet.
Install Wallet
Inside the MadNetWallet-JS repository,
execute the following to build the wallet:
npm install
In the same repository, we now need to create
./tests/.env
with the following contents:
PRIVATE_KEY="6aea45ee1273170fb525da34015e4f20ba39fe792f486ba74020bcacc9badfc1"
CHAIN_ID="42"
RPC="http://127.0.0.1:8888/v1/"
This is required for the tests to run.
Setup Test Environment
In order to initialize a chain with all validators
online and a valid group key, we use the snapshot.zip
file
and then proceed from a valid snapshot of the chain.
The commands which follow enable all tests to be run
except for the ETHDKG test;
this test is described in its entirety here.
First, build the latest version of the repository.
We now setup the bootnode to allow for communication between
the nodes:
./scripts/bootnode.sh
First execute
./scripts/geth-local-snapshot-restore.sh
This will restore the testnet Ethereum database and
the validator databases and allows us to not have to run ETHDKG
to test functionality.
This script must run to completion before continuing the test setup.
Open a new terminal and run
./scripts/geth-local-resume.sh
Open five additional terminals and execute the validator scripts
./scripts/validator0.sh
./scripts/validator1.sh
./scripts/validator2.sh
./scripts/validator3.sh
./scripts/validator4.sh
The first 4 validators (validator0 -- validator3) mine transactions.
validator4 sends transactions to the other validators.
Deposits are required in order to submit DataStores.
Run the following at least 4 times in order to deposit enough funds
to inject datastores.
./madnet --config=./assets/config/owner.toml utils deposit
Note that DataStores are injected in the Wallet-JS
tests,
so submitting these deposits are required for the
tests to be successful.
At this point, the testnet should now be ready to run the standard tests.
Test Sequences
In order to run the Wallet-JS and standard MadNet
tests, ensure the Test Environment is set up according to
Setup Test Environment.
The ETHDKG Test runs the entire distributed key generation
procedure and has its own protocol.
Wallet-JS Tests
First, ensure the wallet has been compiled.
In the root of the Wallet repository, run the following:
npm test
Use mocha
to run a specific subset of tests.
For example, execute the following to run the Account tests:
npx mocha --timeout 45000 tests/account.js
Standard MadNet Tests
First, run the Wallet-JS tests; only run these MadNet tests
if all the wallet tests pass.
Inject DataStore Test
The first test we run is to inject datastores into MadNet.
Go to ./cmd/testutils/inject/
and run the following command:
go run . -d -m=<message> -i=<index>
As noted in the test setup, deposits to MadNet are required in
order for the datastore tests to succeed.
Successful completion of this test requires that the
datastores be consumed upon expiration;
datastore expiration and consumption is discussed
below
and is not considered part of this test.
We describe how to interact with MadNet in order to view the contents
of a DataStore here.
Spam Transactions Test
Upon successful completion of DataStore injection, we proceed
to inject many transactions to Madnet:
go run . -b <baseIdx> -n <numChildren>
numChildren
will specify the number of the transactions
that are submitted every second.
Random Kill and Restart
Randomly kill and restart the individual validators.
There should be no noticeable change in the behavior of
the other validators and MadNet consensus should not be affected.
Extended Kill and Restart
Randomly kill one validator for an extended period;
in particular, let at least 10 blocks pass before restarting
the killed validator.
This will cause the validator to be out of sync.
The validator should be able to rejoin and resync without
much delay.
Kill Half Nodes and Restart
Kill half the validators.
This will cause the remaining validators to stop because
they are unable to reach consensus due to a lack of validators.
After waiting for at least 20 seconds, restart the killed validators.
After resynchronizing, consensus should continue as before
and blocks should be mined.
Kill All Nodes and Restart
Shut down all the validators.
Wait at least 20 seconds and restart all of them.
Once the validators are resynchronized, blocks should continue
to be mined as though the shutdown did not occur.
DataStore Consumption Test
There are 1024 blocks per epoch; this is defined in
./constants/shared.go
as EpochLength = 1024
.
Blocks are mined approximately every 6 seconds,
so one epoch lasts over one hour.
The DataStore test stores the datastore for 5 epochs,
implying the data would be stored for over 8.5 hours.
To make this test more reasonable and
ensure that datastores are consumed, change
EpochLength = 16
.
ETHDKG Test
This test requires a different set of commands than
the previous tests and all commands have been included.
First ensure the project has been built.
From here, start the bootnode:
./scripts/bootnode.sh
Now, setup a local geth environment:
./scripts/geth-local.sh
This differs from the standard tests because the standard
tests store a valid snapshot of data after ETHDKG has been
completed in order to save time when running tests.
On the Ethereum mainnet, this test would take approximately
40 minutes; on the testnet, this test should take approximately
20 minutes.
Remove all directories containing previous validator information:
rm -r ~/validator*
We now deploy the ETHDKG smart contract, approve tokens,
transfer tokens to the validators, and register the validators:
./scripts/deploy.sh && ./scripts/approvetokens.sh && ./scripts/transfertokens.sh && ./scripts/register.sh
This script must run to completion before the test continues.
We now turn on the validators:
./scripts/validator0.sh
./scripts/validator1.sh
./scripts/validator2.sh
./scripts/validator3.sh
Note that we do not run validator4 at this time;
it is not required for this test.
We now wait for the validators to synchronize with the
Ethereum (or testnet) blockchain.
We will look for InSync: false -> true
in the log statements.
Once the validators are synched, we finally run ETHDKG:
./scripts/ethdkg.sh
Once completed, the validators will start mining blocks.
Interaction
API Docs and Limited GUI
The swagger-ui for the localRPC of a validator may be found at
http://localhost:####/swagger/ ;
the default port for validator4 is 8888.
Thus, you may speak to validator 4 (a non-mining node) at
http://localhost:8888/swagger/ .
View DataStore
Tests involving datastores require being able to determine
whether the datastores are present.
One way to ensure this is using the Swagger API.
To understand how to run through this, we run an example.
We assume that the test environment has been successfully
setup and that the Swagger API is available.
Furthermore, validator4 must be running.
We submit
go run . -d -m=hello -i=foo
This test submits a DataStore of message hello
at index foo
and then overwrites the DataStore, setting the value to hello-two
Here is a truncation of the output:
Running in DataStore Mode
...
...
...
secp
Consumed Value:12000 ValueOut:2695
DS: index:4943a3029516011ac24ba62f5e4183eb6e7dbbe8a3c6644fbc3a515188eef7f8 deposit:2695 EpochOfExpire:7 msg:hello-two
Consumed Value:12000 ValueOut:2695
secp
Consumed Value:12000 ValueOut:12000
...
...
...
Getting Tx err: rpc error: code = Unknown desc = unknown transaction: d3e6cc649be314aca13c9172c2b5bb9faa9a389440e277c323a0d9cbcfcd0ed5
Sending Tx
Sending Tx err: rpc error: code = Unknown desc = the object is invalid:utxoID already in trie
GetMinedTransaction Tx err: <nil>
Here, d3e6cc649be314aca13c9172c2b5bb9faa9a389440e277c323a0d9cbcfcd0ed5
(64 hex characters on the fourth-from-bottom line)
is the TxHash of the transaction of which overwrote the original
transaction we submitted.
As noted above, the original message is hello
and the new message
is hello-two
In the Swagger API, if we click on get-mined-transaction
and set the TxHash value to
d3e6cc649be314aca13c9172c2b5bb9faa9a389440e277c323a0d9cbcfcd0ed5
.
Here is a portion of the transaction:
"DataStore": {
"DSLinker": {
"DSPreImage": {
"ChainID": 42,
"Index": "4943a3029516011ac24ba62f5e4183eb6e7dbbe8a3c6644fbc3a515188eef7f8",
"IssuedAt": 1,
"Deposit": 2695,
"RawData": "68656c6c6f2d74776f",
"Owner": "0301546f99f244b7b58b855330ae0e2bc1b30b41302f"
},
"TxHash": "d3e6cc649be314aca13c9172c2b5bb9faa9a389440e277c323a0d9cbcfcd0ed5"
},
"Signature": "03018e362b2f4fade93d4b06f1cd32b43a8905ccf6dce9f2326a0411a96b652211026671c240cc5d0a23335eabe10a5accd0199dc0dec050f0ec463af8130235557d00"
}
Although we could use the value of Rawdata
to obtain its value,
we will use another Swagger API call.
This is useful because upon consumption, Rawdata
will not be
present and this will ensure that the DataStore was consumed
as expected.
Instead, we will use get-data
, which requires the CurveSpec
,
Account
, and Index
.
We have CurveSpec = 1
because the test uses the Secp256k1
elliptic curve.
The value for Account
comes from removing the first 4 characters
of the Owner
hexidecimal string.
Index
can be copied directly from above.
Thus, we enter
{
"CurveSpec": 1,
"Account": "546f99f244b7b58b855330ae0e2bc1b30b41302f",
"Index": "4943a3029516011ac24ba62f5e4183eb6e7dbbe8a3c6644fbc3a515188eef7f8"
}
Upon execution, we have
{
"Rawdata": "68656c6c6f2d74776f"
}
This matches Rawdata
from the transaction and decodes to
hello-two
, as expected.
If a DataStore is not present, then a "Key not found"
error will be returned;
this error will happen when the DataStore is consumed.
Programmatic Interaction
The localRPC directory contains a client library that abstracts the localRPC system for easier
development.
Graphical Wallet
Coming soon