Merklux

latest released version

in progress


What is Merklux
Merklux is a framework for general state plasma dApp.
This uses merkleized unidirectional data flow for state verification across multiple chains.
For more detail information check https://ethresear.ch/t/merklux-plasma-plant.
Tutorial
- Set your smart contract development environment (We use truffle as an example)
npm install -g truffle
npm install -g ganache-cli
mkdir your-plasma-dapp && cd your-plasma-dapp
truffle unbox blueprint
npm i merklux@next
// contracts/YourReducer.sol
pragma solidity ^0.4.24;
import "merklux/contracts/Merklux.sol";
import "merklux/contracts/MerkluxStore.sol";
import "merklux/contracts/MerkluxReducer.sol";
contract YourReducer is MerkluxReducer {
function reduce(
MerkluxStore _tree,
address _from,
bytes _data
) public view returns (
bytes keys,
bytes values,
bytes32[] references
) {
// Write your reduce logic here
}
}
- Deploy Merklux onto your plasma chain and also register your reducer
const Merklux = artifacts.require('Merklux')
const YourReducer = artifacts.require('YourReducer')
const STORE_KEY = web3.sha3('namespace', { encoding: 'hex' })
module.exports = function (deployer) {
deployer.deploy(Merklux).then(async (merklux) => {
await merklux.newStore(STORE_KEY)
await merklux.setReducer(
STORE_KEY,
'actionName',
YourReducer.bytecode
)
})
}
const Merklux = artifacts.require('Merklux')
const rlp = require('rlp')
const STORE_KEY = web3.sha3('namespace', { encoding: 'hex' })
const rlpEncode = (data) => '0x' + rlp.encode(data).toString('hex')
contract('Merklux', async ([deployer, ...accounts]) => {
let merklux
before(async () => {
merklux = await Merklux.deployed()
})
it('should update its value by the reducer information', async () => {
const VALUE_TO_INCREASE = 8
await merklux.dispatch(STORE_KEY, 'increaseBalance', rlpEncode(VALUE_TO_INCREASE), { from: deployer })
let updatedValue = await merklux.get(STORE_KEY, deployer)
assert.equal(web3.toDecimal(updatedValue), VALUE_TO_INCREASE)
})
})
$ truffle develop
> migrate
...
> test
...
Done! Now you wrote a verifiable general state plasma dApp.
Demo(work in progress)
Thus you can start the dApp for demonstration with the following command.
(This demo dApp uses ReactJS and Drizzle)
npm run start
- Pre requisites
- Run a root chain and a child chain.
- Make state transitions on the child chain
- Deploy merklux smart contract to the child chain.
- Insert some items into the child chain.
- Get root edge from the Merklux of the child chain and store it as the original root.
- Get all nodes which are stored in the MerkluxTree at that time.
- Insert more items into the child chain.
- Get root edge from the Merklux of the child chain and store it as the target root.
- Make a proof case on the root chain.
- Deploy a MerkluxCase to the root chain with the original root and the target root as its construtor's parameter.
- Commit all nodes
- Insert same items into the MerkluxCase of the root chain
- Verify its state tranisition
Tests
Test cases include the information about how the functions work, but also includes a demo scenario.
Running and reading the test cases will help you understand how it works.
npm run test
Features
-
State verification
const primary = '0xACCOUNT'
it('should reenact the state transitions', async () => {
const treeOnChildChain = await MerkluxTree.new({ from: primary })
await treeOnChildChain.insert('key1', 'val1', { from: primary })
await treeOnChildChain.insert('key2', 'val2', { from: primary })
const firstPhaseRootEdge = await treeOnChildChain.getRootEdge()
const firstPhaseRootHash = firstPhaseRootEdge[2]
const dataToCommit = await getDataToCommit(treeOnChildChain, firstPhaseRootHash);
await treeOnChildChain.insert('key3', 'val3', { from: primary })
await treeOnChildChain.insert('key4', 'val4', { from: primary })
const secondPhaseRootEdge = await treeOnChildChain.getRootEdge()
const caseOnRootChain = await MerkluxCase.new(...firstPhaseRootEdge, ...secondPhaseRootEdge, { from: primary })
const commitNodes = async (nodes) => {
for (const node of nodes) {
await caseOnRootChain.commitNode(...node, { from: primary })
}
}
const commitValues = async (values) => {
for (const value of values) {
await caseOnRootChain.commitValue(value, { from: primary })
}
}
await commitNodes(dataToCommit.nodes)
await commitValues(dataToCommit.values)
await caseOnRootChain.seal({ from: primary })
await caseOnRootChain.insert('key3', 'val3', { from: primary })
await caseOnRootChain.insert('key4', 'val4', { from: primary })
await merkluxCase.proof({ from: primary })
assert.equal(
(await merkluxCase.status()).toNumber(),
Status.SUCCESS,
'it should return its status as SUCCESS'
)
})
Please check MerkluxCase.test.js to get more detail information.
-
Sharded namespaces
To be updated
Credits
Merklux uses Christian Reitwießner's patricia-trie for its basic data structure.
And he already mentioned that it can be used for verifying evm-based sidechain executions. Thus, this is kind of an implementation case of his idea.
Contributors
License
MIT LICENSE