Skip to content

Commit

Permalink
Merge pull request #60 from yrashk/new-readme
Browse files Browse the repository at this point in the history
Problem: README is oudated
  • Loading branch information
yrashk authored May 2, 2018
2 parents 6a371cf + 2c8defc commit 67c59b6
Showing 1 changed file with 65 additions and 198 deletions.
263 changes: 65 additions & 198 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,118 +1,50 @@
# POA-Ethereum Bridge

[![Join the chat at https://gitter.im/paritytech/parity-bridge](https://badges.gitter.im/paritytech/parity-bridge.svg)](https://gitter.im/paritytech/parity-bridge?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Join the chat at https://gitter.im/poanetwork/poa-bridge](https://badges.gitter.im/poanetwork/poa-bridge.svg)](https://gitter.im/poanetwork/poa-bridge?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)

[![Build Status][travis-image]][travis-url]
[![Solidity Coverage Status][coveralls-image]][coveralls-url] (contracts only)
This is software to be operated by *POA bridge validators* to faciliate proof-of-authority
based briding of POA to tokens on an **another** Ethereum-based blockchain.

[travis-image]: https://travis-ci.org/paritytech/parity-bridge.svg?branch=master
[travis-url]: https://travis-ci.org/paritytech/parity-bridge
[coveralls-image]: https://coveralls.io/repos/github/paritytech/parity-bridge/badge.svg?branch=master
[coveralls-url]: https://coveralls.io/github/paritytech/parity-bridge?branch=master
The validators work with POA bridge contracts to convert ether on one chain into the same
amount of ERC20 tokens on the other and back.

the bridge is an
[ERC20 token](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md)
contract on one ethereum-based blockchain that is backed by ether on **another** ethereum-based blockchain.
This software works in conjunction with other projects:

users can convert ether
on one chain into the same amount of ERC20 tokens on the other and back.
the bridge securely relays these conversions.
* [POA Bridge UI](https://github.com/poanetwork/bridge-ui)
* [POA Bridge Smart Contracts](https://github.com/poanetwork/poa-bridge-contracts)
* [POA Bridge Monitoring service](https://github.com/poanetwork/bridge-monitor)
* [POA Bridge Deployment scripts](https://github.com/poanetwork/deployment-bridge)

**the bridge can mitigate scaling issues:**
by deploying a [proof-of-authority](https://paritytech.github.io/wiki/Proof-of-Authority-Chains.html)
network and bridging it to the Ethereum Foundation network ('mainnet') users can convert their mainnet ether
into ERC20 tokens on the PoA chain
and there transfer them with much lower transaction fees,
faster block times and unaffected by mainnet congestion.
### Functionality

the users can withdraw their tokens worth of ether on the mainnet at any point.
The bridge connects two chains (`home` and `foreign`). When a user deposits ether into the
bridge contract contract on `home` they get the same amount of ERC20 tokens on `foreign`,
and they can convert them back as well.

parity is using the bridge project to prototype
the system that will eventually connect ethereum and other non-parachains to
[polkadot](https://polkadot.io/).
#### Deposit

### next steps

1. deploy to bridge **ethereum** and **kovan** (bridge authorities TBD)
2. make the bridge work with contract-based dynamic validator sets
3. after kovan hardfork 2: deploy to kovan again with dynamic validator set

### current functionality

the bridge connects two chains `home` and `foreign`.

when users deposit ether into the `HomeBridge` contract on `home`
they get the same amount of ERC20 tokens on `foreign`.

[they can use `ForeignBridge` as they would use any ERC20 token.](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md)

to convert their `foreign` ERC20 into ether on `home`
users can always call `ForeignBridge.transferHomeViaRelay(homeRecipientAddress, value, homeGasPrice)`.

`foreign` is assumed to use PoA (proof of authority) consensus.
relays between the chains happen in a byzantine fault tolerant way using the authorities of `foreign`.

### high level explanation of home ether -> foreign ERC20 relay

`sender` deposits `value` into `HomeBridge`.
the `HomeBridge` fallback function emits `Deposit(sender, value)`.

for each `Deposit` event on `HomeBridge` every authority executes
`ForeignBridge.deposit(sender, value, transactionHash)`.

once there are `ForeignBridge.requiredSignatures` such transactions
with identical arguments and from distinct authorities then
`ForeignBridge.balanceOf(sender)` is increased by `value`.

### high level explanation of foreign ERC20 -> home ether relay

`sender` executes `ForeignBridge.transferHomeViaRelay(recipient, value, homeGasPrice)`
which checks and reduces `ForeignBridge.balances(sender)` by `value` and emits `ForeignBridge.Withdraw(recipient, value, homeGasPrice)`.

for every `ForeignBridge.Withdraw`, every bridge authority creates a message containing
`value`, `recipient` and the `transactionHash` of the transaction referenced by the `ForeignBridge.Withdraw` event;
signs that message and executes `ForeignBridge.submitSignature(signature, message)`.
this collection of signatures is on `foreign` because transactions are free for the authorities on `foreign`,
but not free on `home`.

once `ForeignBridge.requiredSignatures` signatures by distinct authorities are collected
a `ForeignBridge.CollectedSignatures(authorityThatSubmittedLastSignature, messageHash)` event is emitted.

everyone (usually `authorityThatSubmittedLastSignature`) can then call `ForeignBridge.message(messageHash)` and
`ForeignBridge.signature(messageHash, 0..requiredSignatures)`
to look up the message and signatures and execute `HomeBridge.withdraw(vs, rs, ss, message)`
and complete the withdraw.

`HomeBridge.withdraw(vs, rs, ss, message)` recovers the addresses from the signatures,
checks that enough authorities in its authority list have signed and
finally transfers `value` ether ([minus the relay gas costs](#recipient-pays-relay-cost-to-relaying-authority))
to `recipient`.

### run truffle smart contract tests
![deposit](./res/deposit.png)

requires `yarn` to be `$PATH`. [installation instructions](https://yarnpkg.com/lang/en/docs/install/)
#### Withdraw

```
cd truffle
yarn test
```
![withdraw](./res/withdraw.png)

### build
### How to build

requires `rust` and `cargo`: [installation instructions.](https://www.rust-lang.org/en-US/install.html)
Requires `rust` and `cargo`: [installation instructions.](https://www.rust-lang.org/en-US/install.html)

requires `solc` to be in `$PATH`: [installation instructions.](https://solidity.readthedocs.io/en/develop/installing-solidity.html)
Requires `solc` to be in `$PATH`: [installation instructions.](https://solidity.readthedocs.io/en/develop/installing-solidity.html)

assuming you've cloned the bridge (`git clone [email protected]:poanetwork/parity-bridge.git`)
and are in the project directory (`cd parity-bridge`) run:
Assuming you've cloned the bridge (`git clone [email protected]:poanetwork/poa-bridge.git`), run

```
cd poa-bridge
make
```

and install `../target/release/bridge` in your `$PATH`.

### run
### Running

```
bridge --config config.toml --database db.toml
Expand All @@ -121,26 +53,41 @@ bridge --config config.toml --database db.toml
- `--config` - location of the configuration file. configuration file must exist
- `--database` - location of the database file.

### configuration [file example](./examples/config.toml)
#### Exit Status Codes

| Code | Meaning |
|------|----------------------|
| 0 | Success |
| 1 | Unknwon error |
| 2 | I/O error |
| 3 | Shutdown requested |
| 4 | Insufficient funds |
| 5 | Gas too low |
| 6 | Gas price is too low |
| 7 | Nonce reused |
| 10 | Cannot connect |
| 11 | Connection lost |
| 12 | Bridge crashed |
| 20 | RPC error |

### Configuration [file example](./examples/config.toml)

```toml
estimated_gas_cost_of_withdraw = 100000
keystore = "/path/to/keystore"

[home]
account = "0x006e27b6a72e1f34c626762f3c4761547aff1421"
ipc = "/Users/marek/Library/Application Support/io.parity.ethereum/jsonrpc.ipc"
rpc_host = "http://localhost"
rpc_port = 8545
required_confirmations = 0

[home.contract]
bin = "contracts/EthereumBridge.bin"
password = "home_password.txt"

[foreign]
account = "0x006e27b6a72e1f34c626762f3c4761547aff1421"
ipc = "/Users/marek/Library/Application Support/io.parity.ethereum/jsonrpc.ipc"
rpc_host = "http://localhost"
rpc_port = 9545
required_confirmations = 0

[foreign.contract]
bin = "contracts/KovanBridge.bin"
password = "foreign_password.txt"

[authorities]
accounts = [
Expand All @@ -149,33 +96,26 @@ accounts = [
"0x006e27b6a72e1f34c626762f3c4761547aff1421"
]
required_signatures = 2
```

#### options

- `estimated_gas_cost_of_withdraw` - how much gas a transaction to `HomeBridge.withdraw` consumes (**required**)
- currently recommended value: `100000`
- run [tools/estimate_gas_costs.sh](tools/estimate_gas_costs.sh) to compute an estimate
- see [recipient pays relay cost to relaying authority](#recipient-pays-relay-cost-to-relaying-authority) for why this config option is needed

#### home options
[transactions]
deposit_relay = { gas = 3000000 }
withdraw_relay = { gas = 3000000 }
withdraw_confirm = { gas = 3000000 }
```

- `home.account` - authority address on the home (**required**)
- `home.ipc` - path to home parity ipc handle (**required**)
- `home.contract.bin` - path to the compiled bridge contract (**required**)
- `home.required_confirmations` - number of confirmation required to consider transaction final on home (default: **12**)
- `home.poll_interval` - specify how often home node should be polled for changes (in seconds, default: **1**)
- `home.request_timeout` - specify request timeout (in seconds, default: **5**)
#### Options

#### foreign options
- `keystore` - path to a keystore directory with JSON keys

- `foreign.account` - authority address on the foreign (**required**)
- `foreign.ipc` - path to foreign parity ipc handle (**required**)
- `foreign.contract.bin` - path to the compiled bridge contract (**required**)
- `foreign.required_confirmations` - number of confirmation required to consider transaction final on foreign (default: **12**)
- `foreign.poll_interval` - specify how often home node should be polled for changes (in seconds, default: **1**)
- `foreign.request_timeout` - specify request timeout (in seconds, default: **5**)
#### home/foreign options

- `home/foreign.account` - authority address on the home (**required**)
- `home/foreign.rpc_host` - RPC host (**required**)
- `home/foreign.rpc_port` - RPC port (**defaults to 8545**)
- `home/foreign.required_confirmations` - number of confirmation required to consider transaction final on home (default: **12**)
- `home/foreign.poll_interval` - specify how often home node should be polled for changes (in seconds, default: **1**)
- `home/foreign.request_timeout` - specify request timeout (in seconds, default: **5**)
- `home/foreign.password` - path to the file containing a password for the validator's account (to decrypt the key from the keystore)

#### authorities options

Expand All @@ -191,7 +131,7 @@ required_signatures = 2
- `transaction.withdraw_relay.gas` - specify how much gas should be consumed by withdraw relay
- `transaction.withdraw_relay.gas_price` - specify gas price for withdraw relay

### database file format
### Database file format

```toml
home_contract_address = "0x49edf201c1e139282643d5e7c6fb0c7219ad1db7"
Expand All @@ -208,76 +148,3 @@ checked_withdraw_confirm = 121
- `checked_deposit_relay` - number of the last block for which an authority has relayed deposits to the foreign
- `checked_withdraw_relay` - number of the last block for which an authority has relayed withdraws to the home
- `checked_withdraw_confirm` - number of the last block for which an authority has confirmed withdraw

### example run

```
./target/release/bridge --config examples/config.toml --database db.toml
```

- example run requires a parity instance running
- this parity instance can be started by running `examples/parity_start.sh`
- it connects to this parity instance twice. one connection treats the node as `home`, whereas the other as `foreign`
- by default, parity tries to unlock account generates from seedphrase `this is sparta` - `0x006e27b6a72e1f34c626762f3c4761547aff1421`
- this is just an example. the 'real world' bridge needs to connect to the two different parity instances

### deposit

![deposit](./res/deposit.png)

### withdraw

![withdraw](./res/withdraw.png)

### recipient pays relay cost to relaying authority

a bridge `authority` has to pay for gas (`cost`) to execute `HomeBridge.withdraw` when
withdrawing `value` from `foreign` chain to `home` chain.
`value - cost` is transferred to the `recipient`. `cost` is transferred to the `authority`
executing `HomeBridge.withdraw`.
the `recipient` pays the relaying `authority` for the execution of the transaction.
that shuts down an attack that enabled exhaustion of authorities funds on `home`.

read on for a more thorough explanation.

parity-bridge connects a value-bearing ethereum blockchain `home`
(initially the ethereum foundation chain)
to a non-value-bearing PoA ethereum blockchain `foreign` (initially the kovan testnet).

value-bearing means that the ether on that chain has usable value in the sense that
in order to obtain it one has to either mine it (trade in electricity)
or trade in another currency.
non-value-bearing means that one can easily obtain a large amount of ether
on that chain for free.
through a faucet in the case of testnets for example.

the bridge authorities are also the validators of the `foreign` PoA chain.
transactions by the authorities are therefore free (gas price = 0) on `foreign`.

to execute a transaction on `home` a bridge authority has to spend ether to
pay for the gas.

this opened up an attack where a malicious user could
deposit a very small amount of wei on `HomeBridge`, get it relayed to `ForeignBridge`,
then spam `ForeignBridge.transferHomeViaRelay` with `1` wei withdraws.
it would cost the attacker very little `home` chain wei and essentially
free `foreign` testnet wei to cause the authorities to spend orders of magnitude more wei
to relay the withdraw to `home` by executing `HomeBridge.withdraw`.
an attacker was able to exhaust bridge authorities funds on `home`.

to shut down this attack `HomeBridge.withdraw` was modified so
`value - cost` is transferred to the `recipient` and `cost` is transferred to the `authority`
doing the relay.
this way the `recipient` pays the relaying `authority` for the execution of the `withdraw` transaction.

relayers can set the gas price for `HomeBridge.withdraw`.
they could set a very high gas price resulting in a very high `cost` through which they could burn large portions of `value`.
to shut down this attack the `homeGasPrice` param was added to `ForeignBridge.transferHomeViaRelay`.
end users have control over the cost/latency tradeoff of their relay transaction through the `homeGasPrice`.
relayers have to set gas price to `homeGasPrice` when calling `HomeBridge.withdraw`.
the `recipient` for `value` is the exception and can freely choose any gas price.
see https://github.com/paritytech/parity-bridge/issues/112 for more details.

`HomeBridge.withdraw` is currently the only transaction bridge authorities execute on `home`.
care must be taken to secure future functions that bridge authorities will execute
on `home` in similar ways.

0 comments on commit 67c59b6

Please sign in to comment.