Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stateless MPT-based token script #11

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ all: build test
build:
cd scripts/helloworld && cargo build --release && chisel run --config chisel.toml
cd scripts/bazaar && cargo build --release && chisel run --config chisel.toml
cd scripts/smpt && cargo build --release && chisel run --config chisel.toml
cargo build --release

test:
target/release/phase2-scout
target/release/phase2-scout bazaar.yaml
target/release/phase2-scout smpt.yaml
8 changes: 8 additions & 0 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ jobs:
- image: rust:1
steps:
- checkout
- run:
name: Install dependencies
command: |
echo "deb http://apt.llvm.org/stretch/ llvm-toolchain-stretch main" >> /etc/apt/sources.list
echo "deb-src http://apt.llvm.org/stretch/ llvm-toolchain-stretch main" >> /etc/apt/sources.list
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
apt-get update
apt-get install -y clang lldb lld
- run:
name: Update environment
command: |
Expand Down
2 changes: 2 additions & 0 deletions scripts/smpt/.cargo/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[build]
target = "wasm32-unknown-unknown"
37 changes: 37 additions & 0 deletions scripts/smpt/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[package]
name = "smpt"
version = "0.0.0"
license = "Apache-2.0"
repository = "https://github.com/ewasm/scout"
description = "Stateless merkle patricia balance tree"
publish = false
edition = "2018"

[dependencies]
patricia-trie-ethereum = "0.1.0"
hash-db = "0.12.4"
trie-db = "0.12.4"
keccak-hasher = "0.12.4"
kvdb = "0.1"
memory-db = "0.12.4"
rlp = "0.4.0"
elastic-array = "0.10"
ethereum-types = "0.6.0"
tiny-keccak = "1.4.2"
plain_hasher = "0.2"
libsecp256k1 = "0.2.2"
#eth-secp256k1 = { git = "https://github.com/paritytech/rust-secp256k1" }
#ethkey = "0.2.5"

[lib]
crate-type = ["cdylib"]

[dependencies.ewasm_api]
git = "https://github.com/ewasm/ewasm-rust-api"
rev = "1c01982"
s1na marked this conversation as resolved.
Show resolved Hide resolved
default-features = false
features = ["std", "eth2", "qimalloc"]

[profile.release]
lto = true
debug = false
9 changes: 9 additions & 0 deletions scripts/smpt/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
all: build test

build:
cargo build --release
chisel run --config chisel.toml; true

test:
node relayer/index.js
../../target/release/phase2-scout smpt.yaml
8 changes: 8 additions & 0 deletions scripts/smpt/chisel.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
smpt:
file: "target/wasm32-unknown-unknown/release/smpt.wasm"
trimexports:
preset: "ewasm"
verifyexports:
preset: "ewasm"
repack:
preset: "ewasm"
135 changes: 135 additions & 0 deletions scripts/smpt/relayer/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
const assert = require('assert')
const { promisify } = require('util')
const BN = require('bn.js')
const Trie = require('merkle-patricia-tree')
const Account = require('ethereumjs-account').default
const StateManager = require('ethereumjs-vm/dist/state/stateManager').default
const PStateManager = require('ethereumjs-vm/dist/state/promisified').default
const { keccak256, ecsign, stripZeros } = require('ethereumjs-util')
const { encode } = require('rlp')
const Wallet = require('ethereumjs-wallet')
const yaml = require('js-yaml')
const fs = require('fs')

const prove = promisify(Trie.prove)
const verifyProof = promisify(Trie.verifyProof)

async function main () {
const testSuite = {
'beacon_state': {
'execution_scripts': [
'target/wasm32-unknown-unknown/release/smpt.wasm'
],
},
'shard_pre_state': {
'exec_env_states': [
]
},
'shard_blocks': [
],
'shard_post_state': {
'exec_env_states': [
]
}
}

const rawState = new StateManager()
const state = new PStateManager(rawState)

// Generate random accounts
let accounts = await generateAccounts(state)

let root = await state.getStateRoot()
testSuite.shard_pre_state.exec_env_states.push(root.toString('hex'))

// Generate txes
let txes = await generateTxes(state, accounts)

// Serialize witnesses and tx data
const blockData = encode(txes)
console.log(`block data length: ${blockData.length}`)
testSuite.shard_blocks.push({
'env': 0,
'data': blockData.toString('hex')
})

root = await state.getStateRoot()
testSuite.shard_post_state.exec_env_states.push(root.toString('hex'))

const serializedTestSuite = yaml.safeDump(testSuite)
fs.writeFileSync('smpt.yaml', serializedTestSuite)
}

async function generateTxes (state, accounts) {
let txes = []
for (let i = 0; i < 20; i++) {
const root = await state.getStateRoot()
const from = accounts[i].address
const to = accounts[i + 1].address
const value = new BN('00000000000000000000000000000000000000000000000000000000000000ff', 16)
const nonce = new BN('0000000000000000000000000000000000000000000000000000000000000000', 16)

const fromAccount = await state.getAccount(from)
const fromWitness = await prove(state._wrapped._trie, keccak256(from))
let val = await verifyProof(root, keccak256(from), fromWitness)
assert(val.equals(fromAccount.serialize()), "valid from witness")

const toAccount = await state.getAccount(to)
const toWitness = await prove(state._wrapped._trie, keccak256(to))
val = await verifyProof(root, keccak256(to), toWitness)
assert(val.equals(toAccount.serialize()), "valid to witness")

const txRlp = encode([to, stripZeros(value.toBuffer('be', 32)), stripZeros(nonce.toBuffer('be', 32))])
const txHash = keccak256(txRlp)
const txSig = ecsign(txHash, accounts[i].privateKey)

txes.push([
[to, stripZeros(value.toBuffer('be', 32)), stripZeros(nonce.toBuffer('be', 32)), [stripZeros(txSig.r), stripZeros(txSig.s), txSig.v]],
fromWitness,
toWitness
])

await transfer(state, { from, to, value, nonce })
}
return txes
}

async function transfer (state, tx) {
let { from, to, value, nonce } = tx
assert(value.gten(0))

const fromAcc = await state.getAccount(from)
const toAcc = await state.getAccount(to)

assert(new BN(fromAcc.balance).gte(value))
assert(new BN(fromAcc.nonce).eq(nonce))

const newFromBalance = new BN(fromAcc.balance).sub(value)
fromAcc.balance = newFromBalance.toBuffer()
fromAcc.nonce = nonce.addn(1).toBuffer()
const newToBalance = new BN(toAcc.balance).add(value)
toAcc.balance = newToBalance.toBuffer()

await state.putAccount(from, fromAcc)
await state.putAccount(to, toAcc)
}

async function generateAccounts (state) {
let accounts = []
for (let i = 0; i < 500; i++) {
let wallet = Wallet.generate()
let address = wallet.getAddress()
let privateKey = wallet.getPrivateKey()
let account = new Account()
account.balance = new BN('ffffff', 16).toBuffer()
accounts.push({
address,
privateKey,
account
})
await state.putAccount(address, account)
}
return accounts
}

main().then(() => {}).catch((e) => console.log(e))
21 changes: 21 additions & 0 deletions scripts/smpt/relayer/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "mpt",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"bn.js": "^4.11.8",
"ethereumjs-account": "^3.0.0",
"ethereumjs-util": "^6.1.0",
"ethereumjs-vm": "^4.0.0-beta.1",
"ethereumjs-wallet": "^0.6.3",
"js-yaml": "^3.13.1",
"merkle-patricia-tree": "^3.0.0",
"rlp": "^2.2.3"
}
}
35 changes: 35 additions & 0 deletions scripts/smpt/src/account.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use ethereum_types::{H256, U256};
use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream};

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BasicAccount {
pub nonce: U256,
pub balance: U256,
pub storage_root: H256,
pub code_hash: H256,
}

impl Decodable for BasicAccount {
fn decode(d: &Rlp) -> Result<Self, DecoderError> {
if d.item_count()? != 4 {
return Err(DecoderError::RlpIncorrectListLen);
}

Ok(BasicAccount {
nonce: d.val_at(0)?,
balance: d.val_at(1)?,
storage_root: d.val_at(2)?,
code_hash: d.val_at(3)?,
})
}
}

impl Encodable for BasicAccount {
fn rlp_append(&self, s: &mut RlpStream) {
s.begin_list(4);
s.append(&self.nonce);
s.append(&self.balance);
s.append(&self.storage_root);
s.append(&self.code_hash);
}
}
41 changes: 41 additions & 0 deletions scripts/smpt/src/keccak_hasher.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright 2015-2019 Parity Technologies (UK) Ltd.
// This file is part of Parity Ethereum.

// Parity Ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Parity Ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Parity Ethereum. If not, see <http://www.gnu.org/licenses/>.

//! Hasher implementation for the Keccak-256 hash
//! Taken from:
//! https://github.com/paritytech/parity-ethereum/blob/6bb106a784678cc2cadabfd621981371f477c48d/util/keccak-hasher/src/lib.rs
extern crate ethereum_types;
extern crate hash_db;
extern crate plain_hasher;
extern crate tiny_keccak;

use ethereum_types::H256;
use hash_db::Hasher;
use plain_hasher::PlainHasher;
use tiny_keccak::Keccak;
/// Concrete `Hasher` impl for the Keccak-256 hash
#[derive(Default, Debug, Clone, PartialEq)]
pub struct KeccakHasher;
impl Hasher for KeccakHasher {
type Out = H256;
type StdHasher = PlainHasher;
const LENGTH: usize = 32;
fn hash(x: &[u8]) -> Self::Out {
let mut out = [0; 32];
Keccak::keccak256(x, &mut out);
out.into()
}
}
Loading