Specification
For more detail discussion, visit https://ethresear.ch/t/merklux-plasma-plant.
[WIP] Merklux implementation
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
- Write a reducer
// 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
// migrations/2_deploy_merklux_app.js
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, // namespace
'actionName', // action name
YourReducer.bytecode // reducer's bytecode
)
})
}
- Test your application
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)
})
})
- Migrate & test
$ 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 () => { // Deploy a MerkluxTree to the child chain const treeOnChildChain = await MerkluxTree.new({ from: primary }) // Make state transitions await treeOnChildChain.insert('key1', 'val1', { from: primary }) await treeOnChildChain.insert('key2', 'val2', { from: primary }) // Snapshot the state const firstPhaseRootEdge = await treeOnChildChain.getRootEdge() const firstPhaseRootHash = firstPhaseRootEdge[2] // Get data to commit // getDataToCOmmit() is defined in the MerkluxCase.test.js const dataToCommit = await getDataToCommit(treeOnChildChain, firstPhaseRootHash); // Make extra state transitions await treeOnChildChain.insert('key3', 'val3', { from: primary }) await treeOnChildChain.insert('key4', 'val4', { from: primary }) // Snapshot again const secondPhaseRootEdge = await treeOnChildChain.getRootEdge() // Create a case to verify the state transition in another chain const caseOnRootChain = await MerkluxCase.new(...firstPhaseRootEdge, ...secondPhaseRootEdge, { from: primary }) // Commit nodes and values 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) // It will be reverted when the committed nodes and values does not match with the firstPhaseRoot await caseOnRootChain.seal({ from: primary }) // insert correct items await caseOnRootChain.insert('key3', 'val3', { from: primary }) await caseOnRootChain.insert('key4', 'val4', { from: primary }) // try proof await merkluxCase.proof({ from: primary }) // The status of the case is now SUCCESS 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.