parent
f831e4a660
commit
8fb2db9269
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,23 @@ |
||||
[package] |
||||
name = "optics-base" |
||||
version = "0.1.0" |
||||
authors = ["James Prestwich <prestwich@clabs.co>"] |
||||
edition = "2018" |
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
||||
|
||||
[dependencies] |
||||
tokio = { version = "1.0.1", features = ["rt"] } |
||||
optics-core = { path = "../optics-core" } |
||||
config = "0.10" |
||||
serde = "1.0.120" |
||||
serde_json = { version = "1.0.61", default-features = false } |
||||
|
||||
ethers = { git = "https://github.com/gakonst/ethers-rs" } |
||||
ethers-core = { git = "https://github.com/gakonst/ethers-rs" } |
||||
ethers-providers = { git = "https://github.com/gakonst/ethers-rs" } |
||||
ethers-contract = { git = "https://github.com/gakonst/ethers-rs", features = ["abigen"] } |
||||
|
||||
thiserror = { version = "1.0.22", default-features = false } |
||||
async-trait = { version = "0.1.42", default-features = false } |
||||
url = { version = "2.2.0", default-features = false } |
@ -0,0 +1,10 @@ |
||||
[home] |
||||
chain = "Ethereum" |
||||
http = "http://localhost:8545" |
||||
address = "0x0000000000000000000000000000000000000000" |
||||
|
||||
[replicas] |
||||
[replicas.Celo] |
||||
chain = "Ethereum" |
||||
address = "0x0000000000000000000000000000000000000000" |
||||
ws = "wss://" |
@ -0,0 +1,347 @@ |
||||
[ |
||||
{ |
||||
"inputs": [ |
||||
{ |
||||
"internalType": "uint32", |
||||
"name": "_originSLIP44", |
||||
"type": "uint32" |
||||
}, |
||||
{ |
||||
"internalType": "address", |
||||
"name": "_updater", |
||||
"type": "address" |
||||
}, |
||||
{ |
||||
"internalType": "bytes32", |
||||
"name": "_current", |
||||
"type": "bytes32" |
||||
} |
||||
], |
||||
"stateMutability": "payable", |
||||
"type": "constructor" |
||||
}, |
||||
{ |
||||
"anonymous": false, |
||||
"inputs": [ |
||||
{ |
||||
"indexed": true, |
||||
"internalType": "uint32", |
||||
"name": "destination", |
||||
"type": "uint32" |
||||
}, |
||||
{ |
||||
"indexed": true, |
||||
"internalType": "uint32", |
||||
"name": "sequence", |
||||
"type": "uint32" |
||||
}, |
||||
{ |
||||
"indexed": true, |
||||
"internalType": "bytes32", |
||||
"name": "current", |
||||
"type": "bytes32" |
||||
}, |
||||
{ |
||||
"indexed": false, |
||||
"internalType": "bytes", |
||||
"name": "message", |
||||
"type": "bytes" |
||||
} |
||||
], |
||||
"name": "Dispatch", |
||||
"type": "event" |
||||
}, |
||||
{ |
||||
"anonymous": false, |
||||
"inputs": [ |
||||
{ |
||||
"indexed": false, |
||||
"internalType": "bytes32[2]", |
||||
"name": "_oldRoot", |
||||
"type": "bytes32[2]" |
||||
}, |
||||
{ |
||||
"indexed": false, |
||||
"internalType": "bytes32[2]", |
||||
"name": "_newRoot", |
||||
"type": "bytes32[2]" |
||||
}, |
||||
{ |
||||
"indexed": false, |
||||
"internalType": "bytes", |
||||
"name": "_signature", |
||||
"type": "bytes" |
||||
}, |
||||
{ |
||||
"indexed": false, |
||||
"internalType": "bytes", |
||||
"name": "_signature2", |
||||
"type": "bytes" |
||||
} |
||||
], |
||||
"name": "DoubleUpdate", |
||||
"type": "event" |
||||
}, |
||||
{ |
||||
"anonymous": false, |
||||
"inputs": [], |
||||
"name": "ImproperUpdate", |
||||
"type": "event" |
||||
}, |
||||
{ |
||||
"anonymous": false, |
||||
"inputs": [ |
||||
{ |
||||
"indexed": true, |
||||
"internalType": "bytes32", |
||||
"name": "_oldRoot", |
||||
"type": "bytes32" |
||||
}, |
||||
{ |
||||
"indexed": true, |
||||
"internalType": "bytes32", |
||||
"name": "_newRoot", |
||||
"type": "bytes32" |
||||
}, |
||||
{ |
||||
"indexed": false, |
||||
"internalType": "bytes", |
||||
"name": "signature", |
||||
"type": "bytes" |
||||
} |
||||
], |
||||
"name": "Update", |
||||
"type": "event" |
||||
}, |
||||
{ |
||||
"inputs": [], |
||||
"name": "DOMAIN_HASH", |
||||
"outputs": [ |
||||
{ |
||||
"internalType": "bytes32", |
||||
"name": "", |
||||
"type": "bytes32" |
||||
} |
||||
], |
||||
"stateMutability": "view", |
||||
"type": "function" |
||||
}, |
||||
{ |
||||
"inputs": [], |
||||
"name": "current", |
||||
"outputs": [ |
||||
{ |
||||
"internalType": "bytes32", |
||||
"name": "", |
||||
"type": "bytes32" |
||||
} |
||||
], |
||||
"stateMutability": "view", |
||||
"type": "function" |
||||
}, |
||||
{ |
||||
"inputs": [ |
||||
{ |
||||
"internalType": "bytes32[2]", |
||||
"name": "_oldRoot", |
||||
"type": "bytes32[2]" |
||||
}, |
||||
{ |
||||
"internalType": "bytes32[2]", |
||||
"name": "_newRoot", |
||||
"type": "bytes32[2]" |
||||
}, |
||||
{ |
||||
"internalType": "bytes", |
||||
"name": "_signature", |
||||
"type": "bytes" |
||||
}, |
||||
{ |
||||
"internalType": "bytes", |
||||
"name": "_signature2", |
||||
"type": "bytes" |
||||
} |
||||
], |
||||
"name": "doubleUpdate", |
||||
"outputs": [], |
||||
"stateMutability": "nonpayable", |
||||
"type": "function" |
||||
}, |
||||
{ |
||||
"inputs": [ |
||||
{ |
||||
"internalType": "uint32", |
||||
"name": "destination", |
||||
"type": "uint32" |
||||
}, |
||||
{ |
||||
"internalType": "bytes32", |
||||
"name": "recipient", |
||||
"type": "bytes32" |
||||
}, |
||||
{ |
||||
"internalType": "bytes", |
||||
"name": "body", |
||||
"type": "bytes" |
||||
} |
||||
], |
||||
"name": "enqueue", |
||||
"outputs": [], |
||||
"stateMutability": "nonpayable", |
||||
"type": "function" |
||||
}, |
||||
{ |
||||
"inputs": [ |
||||
{ |
||||
"internalType": "bytes32", |
||||
"name": "_oldRoot", |
||||
"type": "bytes32" |
||||
}, |
||||
{ |
||||
"internalType": "bytes32", |
||||
"name": "_newRoot", |
||||
"type": "bytes32" |
||||
}, |
||||
{ |
||||
"internalType": "bytes", |
||||
"name": "_signature", |
||||
"type": "bytes" |
||||
} |
||||
], |
||||
"name": "improperUpdate", |
||||
"outputs": [ |
||||
{ |
||||
"internalType": "bool", |
||||
"name": "", |
||||
"type": "bool" |
||||
} |
||||
], |
||||
"stateMutability": "nonpayable", |
||||
"type": "function" |
||||
}, |
||||
{ |
||||
"inputs": [], |
||||
"name": "originSLIP44", |
||||
"outputs": [ |
||||
{ |
||||
"internalType": "uint32", |
||||
"name": "", |
||||
"type": "uint32" |
||||
} |
||||
], |
||||
"stateMutability": "view", |
||||
"type": "function" |
||||
}, |
||||
{ |
||||
"inputs": [], |
||||
"name": "root", |
||||
"outputs": [ |
||||
{ |
||||
"internalType": "bytes32", |
||||
"name": "", |
||||
"type": "bytes32" |
||||
} |
||||
], |
||||
"stateMutability": "view", |
||||
"type": "function" |
||||
}, |
||||
{ |
||||
"inputs": [ |
||||
{ |
||||
"internalType": "uint32", |
||||
"name": "", |
||||
"type": "uint32" |
||||
} |
||||
], |
||||
"name": "sequences", |
||||
"outputs": [ |
||||
{ |
||||
"internalType": "uint32", |
||||
"name": "", |
||||
"type": "uint32" |
||||
} |
||||
], |
||||
"stateMutability": "view", |
||||
"type": "function" |
||||
}, |
||||
{ |
||||
"inputs": [], |
||||
"name": "state", |
||||
"outputs": [ |
||||
{ |
||||
"internalType": "enum Common.States", |
||||
"name": "", |
||||
"type": "uint8" |
||||
} |
||||
], |
||||
"stateMutability": "view", |
||||
"type": "function" |
||||
}, |
||||
{ |
||||
"inputs": [], |
||||
"name": "suggestUpdate", |
||||
"outputs": [ |
||||
{ |
||||
"internalType": "bytes32", |
||||
"name": "", |
||||
"type": "bytes32" |
||||
}, |
||||
{ |
||||
"internalType": "bytes32", |
||||
"name": "", |
||||
"type": "bytes32" |
||||
} |
||||
], |
||||
"stateMutability": "view", |
||||
"type": "function" |
||||
}, |
||||
{ |
||||
"inputs": [], |
||||
"name": "tree", |
||||
"outputs": [ |
||||
{ |
||||
"internalType": "uint256", |
||||
"name": "count", |
||||
"type": "uint256" |
||||
} |
||||
], |
||||
"stateMutability": "view", |
||||
"type": "function" |
||||
}, |
||||
{ |
||||
"inputs": [ |
||||
{ |
||||
"internalType": "bytes32", |
||||
"name": "_oldRoot", |
||||
"type": "bytes32" |
||||
}, |
||||
{ |
||||
"internalType": "bytes32", |
||||
"name": "_newRoot", |
||||
"type": "bytes32" |
||||
}, |
||||
{ |
||||
"internalType": "bytes", |
||||
"name": "_signature", |
||||
"type": "bytes" |
||||
} |
||||
], |
||||
"name": "update", |
||||
"outputs": [], |
||||
"stateMutability": "nonpayable", |
||||
"type": "function" |
||||
}, |
||||
{ |
||||
"inputs": [], |
||||
"name": "updater", |
||||
"outputs": [ |
||||
{ |
||||
"internalType": "address", |
||||
"name": "", |
||||
"type": "address" |
||||
} |
||||
], |
||||
"stateMutability": "view", |
||||
"type": "function" |
||||
} |
||||
] |
@ -0,0 +1,369 @@ |
||||
[ |
||||
{ |
||||
"inputs": [ |
||||
{ |
||||
"internalType": "uint32", |
||||
"name": "_originSLIP44", |
||||
"type": "uint32" |
||||
}, |
||||
{ |
||||
"internalType": "uint32", |
||||
"name": "_ownSLIP44", |
||||
"type": "uint32" |
||||
}, |
||||
{ |
||||
"internalType": "address", |
||||
"name": "_updater", |
||||
"type": "address" |
||||
}, |
||||
{ |
||||
"internalType": "uint256", |
||||
"name": "_optimisticSeconds", |
||||
"type": "uint256" |
||||
}, |
||||
{ |
||||
"internalType": "bytes32", |
||||
"name": "_start", |
||||
"type": "bytes32" |
||||
}, |
||||
{ |
||||
"internalType": "uint256", |
||||
"name": "_lastProcessed", |
||||
"type": "uint256" |
||||
} |
||||
], |
||||
"stateMutability": "nonpayable", |
||||
"type": "constructor" |
||||
}, |
||||
{ |
||||
"anonymous": false, |
||||
"inputs": [ |
||||
{ |
||||
"indexed": false, |
||||
"internalType": "bytes32[2]", |
||||
"name": "_oldRoot", |
||||
"type": "bytes32[2]" |
||||
}, |
||||
{ |
||||
"indexed": false, |
||||
"internalType": "bytes32[2]", |
||||
"name": "_newRoot", |
||||
"type": "bytes32[2]" |
||||
}, |
||||
{ |
||||
"indexed": false, |
||||
"internalType": "bytes", |
||||
"name": "_signature", |
||||
"type": "bytes" |
||||
}, |
||||
{ |
||||
"indexed": false, |
||||
"internalType": "bytes", |
||||
"name": "_signature2", |
||||
"type": "bytes" |
||||
} |
||||
], |
||||
"name": "DoubleUpdate", |
||||
"type": "event" |
||||
}, |
||||
{ |
||||
"anonymous": false, |
||||
"inputs": [ |
||||
{ |
||||
"indexed": true, |
||||
"internalType": "bytes32", |
||||
"name": "_oldRoot", |
||||
"type": "bytes32" |
||||
}, |
||||
{ |
||||
"indexed": true, |
||||
"internalType": "bytes32", |
||||
"name": "_newRoot", |
||||
"type": "bytes32" |
||||
}, |
||||
{ |
||||
"indexed": false, |
||||
"internalType": "bytes", |
||||
"name": "signature", |
||||
"type": "bytes" |
||||
} |
||||
], |
||||
"name": "Update", |
||||
"type": "event" |
||||
}, |
||||
{ |
||||
"inputs": [], |
||||
"name": "DOMAIN_HASH", |
||||
"outputs": [ |
||||
{ |
||||
"internalType": "bytes32", |
||||
"name": "", |
||||
"type": "bytes32" |
||||
} |
||||
], |
||||
"stateMutability": "view", |
||||
"type": "function" |
||||
}, |
||||
{ |
||||
"inputs": [], |
||||
"name": "PROCESS_GAS", |
||||
"outputs": [ |
||||
{ |
||||
"internalType": "uint256", |
||||
"name": "", |
||||
"type": "uint256" |
||||
} |
||||
], |
||||
"stateMutability": "view", |
||||
"type": "function" |
||||
}, |
||||
{ |
||||
"inputs": [], |
||||
"name": "RESERVE_GAS", |
||||
"outputs": [ |
||||
{ |
||||
"internalType": "uint256", |
||||
"name": "", |
||||
"type": "uint256" |
||||
} |
||||
], |
||||
"stateMutability": "view", |
||||
"type": "function" |
||||
}, |
||||
{ |
||||
"inputs": [], |
||||
"name": "confirm", |
||||
"outputs": [], |
||||
"stateMutability": "nonpayable", |
||||
"type": "function" |
||||
}, |
||||
{ |
||||
"inputs": [], |
||||
"name": "current", |
||||
"outputs": [ |
||||
{ |
||||
"internalType": "bytes32", |
||||
"name": "", |
||||
"type": "bytes32" |
||||
} |
||||
], |
||||
"stateMutability": "view", |
||||
"type": "function" |
||||
}, |
||||
{ |
||||
"inputs": [ |
||||
{ |
||||
"internalType": "bytes32[2]", |
||||
"name": "_oldRoot", |
||||
"type": "bytes32[2]" |
||||
}, |
||||
{ |
||||
"internalType": "bytes32[2]", |
||||
"name": "_newRoot", |
||||
"type": "bytes32[2]" |
||||
}, |
||||
{ |
||||
"internalType": "bytes", |
||||
"name": "_signature", |
||||
"type": "bytes" |
||||
}, |
||||
{ |
||||
"internalType": "bytes", |
||||
"name": "_signature2", |
||||
"type": "bytes" |
||||
} |
||||
], |
||||
"name": "doubleUpdate", |
||||
"outputs": [], |
||||
"stateMutability": "nonpayable", |
||||
"type": "function" |
||||
}, |
||||
{ |
||||
"inputs": [ |
||||
{ |
||||
"internalType": "bytes32", |
||||
"name": "", |
||||
"type": "bytes32" |
||||
} |
||||
], |
||||
"name": "messages", |
||||
"outputs": [ |
||||
{ |
||||
"internalType": "enum ProcessingReplica.MessageStatus", |
||||
"name": "", |
||||
"type": "uint8" |
||||
} |
||||
], |
||||
"stateMutability": "view", |
||||
"type": "function" |
||||
}, |
||||
{ |
||||
"inputs": [], |
||||
"name": "optimisticSeconds", |
||||
"outputs": [ |
||||
{ |
||||
"internalType": "uint256", |
||||
"name": "", |
||||
"type": "uint256" |
||||
} |
||||
], |
||||
"stateMutability": "view", |
||||
"type": "function" |
||||
}, |
||||
{ |
||||
"inputs": [], |
||||
"name": "originSLIP44", |
||||
"outputs": [ |
||||
{ |
||||
"internalType": "uint32", |
||||
"name": "", |
||||
"type": "uint32" |
||||
} |
||||
], |
||||
"stateMutability": "view", |
||||
"type": "function" |
||||
}, |
||||
{ |
||||
"inputs": [], |
||||
"name": "ownSLIP44", |
||||
"outputs": [ |
||||
{ |
||||
"internalType": "uint32", |
||||
"name": "", |
||||
"type": "uint32" |
||||
} |
||||
], |
||||
"stateMutability": "view", |
||||
"type": "function" |
||||
}, |
||||
{ |
||||
"inputs": [ |
||||
{ |
||||
"internalType": "bytes", |
||||
"name": "_message", |
||||
"type": "bytes" |
||||
} |
||||
], |
||||
"name": "process", |
||||
"outputs": [ |
||||
{ |
||||
"internalType": "bool", |
||||
"name": "", |
||||
"type": "bool" |
||||
}, |
||||
{ |
||||
"internalType": "bytes", |
||||
"name": "", |
||||
"type": "bytes" |
||||
} |
||||
], |
||||
"stateMutability": "nonpayable", |
||||
"type": "function" |
||||
}, |
||||
{ |
||||
"inputs": [ |
||||
{ |
||||
"internalType": "bytes32", |
||||
"name": "leaf", |
||||
"type": "bytes32" |
||||
}, |
||||
{ |
||||
"internalType": "bytes32[32]", |
||||
"name": "proof", |
||||
"type": "bytes32[32]" |
||||
}, |
||||
{ |
||||
"internalType": "uint256", |
||||
"name": "index", |
||||
"type": "uint256" |
||||
} |
||||
], |
||||
"name": "prove", |
||||
"outputs": [ |
||||
{ |
||||
"internalType": "bool", |
||||
"name": "", |
||||
"type": "bool" |
||||
} |
||||
], |
||||
"stateMutability": "nonpayable", |
||||
"type": "function" |
||||
}, |
||||
{ |
||||
"inputs": [ |
||||
{ |
||||
"internalType": "bytes32", |
||||
"name": "leaf", |
||||
"type": "bytes32" |
||||
}, |
||||
{ |
||||
"internalType": "bytes32[32]", |
||||
"name": "proof", |
||||
"type": "bytes32[32]" |
||||
}, |
||||
{ |
||||
"internalType": "uint256", |
||||
"name": "index", |
||||
"type": "uint256" |
||||
}, |
||||
{ |
||||
"internalType": "bytes", |
||||
"name": "message", |
||||
"type": "bytes" |
||||
} |
||||
], |
||||
"name": "proveAndProcess", |
||||
"outputs": [], |
||||
"stateMutability": "nonpayable", |
||||
"type": "function" |
||||
}, |
||||
{ |
||||
"inputs": [], |
||||
"name": "state", |
||||
"outputs": [ |
||||
{ |
||||
"internalType": "enum Common.States", |
||||
"name": "", |
||||
"type": "uint8" |
||||
} |
||||
], |
||||
"stateMutability": "view", |
||||
"type": "function" |
||||
}, |
||||
{ |
||||
"inputs": [ |
||||
{ |
||||
"internalType": "bytes32", |
||||
"name": "_oldRoot", |
||||
"type": "bytes32" |
||||
}, |
||||
{ |
||||
"internalType": "bytes32", |
||||
"name": "_newRoot", |
||||
"type": "bytes32" |
||||
}, |
||||
{ |
||||
"internalType": "bytes", |
||||
"name": "_signature", |
||||
"type": "bytes" |
||||
} |
||||
], |
||||
"name": "update", |
||||
"outputs": [], |
||||
"stateMutability": "nonpayable", |
||||
"type": "function" |
||||
}, |
||||
{ |
||||
"inputs": [], |
||||
"name": "updater", |
||||
"outputs": [ |
||||
{ |
||||
"internalType": "address", |
||||
"name": "", |
||||
"type": "address" |
||||
} |
||||
], |
||||
"stateMutability": "view", |
||||
"type": "function" |
||||
} |
||||
] |
@ -0,0 +1,180 @@ |
||||
use async_trait::async_trait; |
||||
use ethers_contract::abigen; |
||||
use ethers_core::types::{Address, H256, U256}; |
||||
use std::sync::Arc; |
||||
|
||||
use optics_core::{ |
||||
traits::{ChainCommunicationError, Common, Home, State, TxOutcome}, |
||||
Message, SignedUpdate, Update, |
||||
}; |
||||
|
||||
abigen!( |
||||
ReplicaContractInternal, |
||||
"src/abis/ProcessingReplica.abi.json" |
||||
); |
||||
|
||||
abigen!(HomeContractInternal, "src/abis/Home.abi.json"); |
||||
|
||||
#[derive(Debug)] |
||||
pub struct HomeContract<M> |
||||
where |
||||
M: ethers_providers::Middleware, |
||||
{ |
||||
contract: HomeContractInternal<M>, |
||||
slip44: u32, |
||||
} |
||||
|
||||
impl<M> HomeContract<M> |
||||
where |
||||
M: ethers_providers::Middleware, |
||||
{ |
||||
pub fn at(slip44: u32, address: Address, provider: Arc<M>) -> Self { |
||||
Self { |
||||
contract: HomeContractInternal::new(address, provider), |
||||
slip44, |
||||
} |
||||
} |
||||
} |
||||
|
||||
#[async_trait] |
||||
impl<M> Common for HomeContract<M> |
||||
where |
||||
M: ethers_providers::Middleware + 'static, |
||||
{ |
||||
async fn status(&self, txid: H256) -> Result<Option<TxOutcome>, ChainCommunicationError> { |
||||
let receipt_opt = self |
||||
.contract |
||||
.client() |
||||
.get_transaction_receipt(txid) |
||||
.await |
||||
.map_err(|e| ChainCommunicationError::CustomError(Box::new(e)))?; |
||||
|
||||
Ok(receipt_opt.map(Into::into)) |
||||
} |
||||
|
||||
fn origin_slip44(&self) -> u32 { |
||||
self.slip44 |
||||
} |
||||
|
||||
async fn updater(&self) -> Result<H256, ChainCommunicationError> { |
||||
Ok(self.contract.updater().call().await?.into()) |
||||
} |
||||
|
||||
async fn state(&self) -> Result<State, ChainCommunicationError> { |
||||
let state = self.contract.state().call().await?; |
||||
match state { |
||||
0 => Ok(State::Waiting), |
||||
1 => Ok(State::Failed), |
||||
_ => unreachable!(), |
||||
} |
||||
} |
||||
|
||||
async fn current_root(&self) -> Result<H256, ChainCommunicationError> { |
||||
Ok(self.contract.current().call().await?.into()) |
||||
} |
||||
|
||||
async fn update(&self, update: &SignedUpdate) -> Result<TxOutcome, ChainCommunicationError> { |
||||
Ok(self |
||||
.contract |
||||
.update( |
||||
update.update.previous_root.to_fixed_bytes(), |
||||
update.update.new_root.to_fixed_bytes(), |
||||
update.signature.to_vec(), |
||||
) |
||||
.send() |
||||
.await? |
||||
.await? |
||||
.into()) |
||||
} |
||||
|
||||
async fn double_update( |
||||
&self, |
||||
left: &SignedUpdate, |
||||
right: &SignedUpdate, |
||||
) -> Result<TxOutcome, ChainCommunicationError> { |
||||
Ok(self |
||||
.contract |
||||
.double_update( |
||||
[ |
||||
left.update.previous_root.to_fixed_bytes(), |
||||
right.update.previous_root.to_fixed_bytes(), |
||||
], |
||||
[ |
||||
left.update.new_root.to_fixed_bytes(), |
||||
right.update.new_root.to_fixed_bytes(), |
||||
], |
||||
left.signature.to_vec(), |
||||
right.signature.to_vec(), |
||||
) |
||||
.send() |
||||
.await? |
||||
.await? |
||||
.into()) |
||||
} |
||||
} |
||||
|
||||
#[async_trait] |
||||
impl<M> Home for HomeContract<M> |
||||
where |
||||
M: ethers_providers::Middleware + 'static, |
||||
{ |
||||
async fn lookup_message( |
||||
&self, |
||||
destination: u32, |
||||
sequence: u32, |
||||
) -> Result<Option<Vec<u8>>, ChainCommunicationError> { |
||||
let filters = self |
||||
.contract |
||||
.dispatch_filter() |
||||
.topic1(U256::from(destination)) |
||||
.topic2(U256::from(sequence)) |
||||
.query() |
||||
.await?; |
||||
|
||||
Ok(filters.into_iter().next().map(|f| f.message.clone())) |
||||
} |
||||
|
||||
async fn sequences(&self, destination: u32) -> Result<u32, ChainCommunicationError> { |
||||
Ok(self.contract.sequences(destination).call().await?) |
||||
} |
||||
|
||||
async fn enqueue(&self, message: &Message) -> Result<TxOutcome, ChainCommunicationError> { |
||||
Ok(self |
||||
.contract |
||||
.enqueue( |
||||
message.destination, |
||||
message.recipient.to_fixed_bytes(), |
||||
message.body.clone(), |
||||
) |
||||
.send() |
||||
.await? |
||||
.await? |
||||
.into()) |
||||
} |
||||
|
||||
async fn improper_update( |
||||
&self, |
||||
update: &SignedUpdate, |
||||
) -> Result<TxOutcome, ChainCommunicationError> { |
||||
Ok(self |
||||
.contract |
||||
.improper_update( |
||||
update.update.previous_root.to_fixed_bytes(), |
||||
update.update.new_root.to_fixed_bytes(), |
||||
update.signature.to_vec(), |
||||
) |
||||
.send() |
||||
.await? |
||||
.await? |
||||
.into()) |
||||
} |
||||
|
||||
async fn produce_update(&self) -> Result<Update, ChainCommunicationError> { |
||||
let (a, b) = self.contract.suggest_update().call().await?; |
||||
Ok(Update { |
||||
origin_chain: self.origin_slip44(), |
||||
previous_root: a.into(), |
||||
new_root: b.into(), |
||||
}) |
||||
} |
||||
} |
@ -0,0 +1,43 @@ |
||||
mod abis; |
||||
mod settings; |
||||
|
||||
use ethers_providers::{Http, Provider}; |
||||
// use std::collections::HashMap;
|
||||
use std::{convert::TryFrom, sync::Arc}; |
||||
|
||||
use optics_core::traits::{Home, Replica}; |
||||
|
||||
#[derive(Debug)] |
||||
struct App { |
||||
home: Box<dyn Home>, |
||||
replicas: Vec<Box<dyn Replica>>, |
||||
} |
||||
|
||||
async fn _main(settings: settings::Settings) { |
||||
println!("{:?}", &settings); |
||||
|
||||
let home = { |
||||
let provider = Arc::new(Provider::<Http>::try_from(settings.home().url()).expect("!url")); |
||||
Box::new(abis::HomeContract::at( |
||||
0, |
||||
settings.home().address().into(), |
||||
provider, |
||||
)) |
||||
}; |
||||
|
||||
let app = App { |
||||
home, |
||||
replicas: vec![], |
||||
}; |
||||
println!("{:?}", &app); |
||||
} |
||||
|
||||
fn main() { |
||||
let settings = settings::Settings::new().expect("!config"); |
||||
|
||||
tokio::runtime::Builder::new_current_thread() |
||||
.enable_all() |
||||
.build() |
||||
.unwrap() |
||||
.block_on(_main(settings)) |
||||
} |
@ -0,0 +1,86 @@ |
||||
use config::{Config, ConfigError, Environment, File}; |
||||
use std::{collections::HashMap, env}; |
||||
|
||||
use ethers_core::types::{Address, H256}; |
||||
|
||||
#[derive(Debug, serde::Deserialize)] |
||||
#[serde(untagged)] |
||||
pub(crate) enum Ethereum { |
||||
Http { address: Address, http: String }, |
||||
Ws { address: Address, ws: String }, |
||||
} |
||||
|
||||
impl Ethereum { |
||||
pub fn url(&self) -> &str { |
||||
match self { |
||||
Self::Http { address: _, http } => &http, |
||||
Self::Ws { address: _, ws } => &ws, |
||||
} |
||||
} |
||||
|
||||
pub fn address(&self) -> Address { |
||||
match self { |
||||
Self::Http { address, http: _ } => *address, |
||||
Self::Ws { address, ws: _ } => *address, |
||||
} |
||||
} |
||||
} |
||||
|
||||
#[derive(Debug, serde::Deserialize)] |
||||
#[serde(tag = "chain")] |
||||
pub(crate) enum Home { |
||||
Ethereum(Ethereum), |
||||
} |
||||
|
||||
impl Home { |
||||
pub fn url(&self) -> &str { |
||||
match self { |
||||
Self::Ethereum(e) => e.url(), |
||||
} |
||||
} |
||||
|
||||
pub fn address(&self) -> H256 { |
||||
match self { |
||||
Self::Ethereum(e) => e.address().into(), |
||||
} |
||||
} |
||||
} |
||||
|
||||
#[derive(Debug, serde::Deserialize)] |
||||
#[serde(tag = "chain")] |
||||
pub(crate) enum Replica { |
||||
Ethereum(Ethereum), |
||||
} |
||||
|
||||
#[derive(Debug, serde::Deserialize)] |
||||
pub(crate) struct Settings { |
||||
home: Home, |
||||
replicas: HashMap<String, Replica>, |
||||
} |
||||
|
||||
impl Settings { |
||||
pub fn new() -> Result<Self, ConfigError> { |
||||
let mut s = Config::new(); |
||||
|
||||
s.merge(File::with_name("config/default"))?; |
||||
|
||||
let env = env::var("RUN_MODE").unwrap_or_else(|_| "development".into()); |
||||
s.merge(File::with_name(&format!("config/{}", env)).required(false))?; |
||||
|
||||
// Add in settings from the environment (with a prefix of OPTRELAY)
|
||||
// Eg.. `OPTRELAY_DEBUG=1 would set the `debug` key
|
||||
s.merge(Environment::with_prefix("OPTRELAY"))?; |
||||
|
||||
s.try_into() |
||||
} |
||||
} |
||||
|
||||
impl Settings { |
||||
pub fn home(&self) -> &Home { |
||||
&self.home |
||||
} |
||||
|
||||
pub fn replicas(&self) -> &HashMap<String, Replica> { |
||||
&self.replicas |
||||
} |
||||
} |
@ -0,0 +1,7 @@ |
||||
#!/bin/zsh |
||||
|
||||
cd ../../solidity && \ |
||||
npm i && \ |
||||
npm run compile && \ |
||||
cat artifacts/contracts/Replica.sol/ProcessingReplica.json| jq .abi > ../rust/optics-base/src/abis/ProcessingReplica.abi.json && \ |
||||
cat artifacts/contracts/Home.sol/Home.json| jq .abi > ../rust/optics-base/src/abis/Home.abi.json |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,37 @@ |
||||
use async_trait::async_trait; |
||||
|
||||
use crate::{ |
||||
traits::{ChainCommunicationError, Common, TxOutcome}, |
||||
Message, SignedUpdate, Update, |
||||
}; |
||||
|
||||
/// Interface for the Home chain contract. Allows abstraction over different
|
||||
/// chains
|
||||
#[async_trait] |
||||
pub trait Home: Common { |
||||
/// Fetch the message to destination at the sequence number (or error).
|
||||
/// This should fetch events from the chain API
|
||||
async fn lookup_message( |
||||
&self, |
||||
destination: u32, |
||||
sequence: u32, |
||||
) -> Result<Option<Vec<u8>>, ChainCommunicationError>; |
||||
|
||||
/// Fetch the sequence
|
||||
async fn sequences(&self, destination: u32) -> Result<u32, ChainCommunicationError>; |
||||
|
||||
/// Queue a message.
|
||||
async fn enqueue(&self, message: &Message) -> Result<TxOutcome, ChainCommunicationError>; |
||||
|
||||
/// Submit an improper update for slashing
|
||||
async fn improper_update( |
||||
&self, |
||||
update: &SignedUpdate, |
||||
) -> Result<TxOutcome, ChainCommunicationError>; |
||||
|
||||
/// Create a valid update based on the chain's current state.
|
||||
/// This merely suggests an update. It does NOT ensure that no other valid
|
||||
/// update has been produced. The updater MUST take measures to prevent
|
||||
/// double-updating.
|
||||
async fn produce_update(&self) -> Result<Update, ChainCommunicationError>; |
||||
} |
@ -0,0 +1,107 @@ |
||||
/// Interface for home chain contract
|
||||
pub mod home; |
||||
|
||||
/// Interface for replica chain contract
|
||||
pub mod replica; |
||||
|
||||
use async_trait::async_trait; |
||||
use ethers_core::types::{TransactionReceipt, H256}; |
||||
use thiserror::Error; |
||||
|
||||
use crate::{utils::domain_hash, SignedUpdate}; |
||||
|
||||
pub use home::*; |
||||
pub use replica::*; |
||||
|
||||
/// Contract states
|
||||
#[derive(Debug)] |
||||
pub enum State { |
||||
/// Contract is active
|
||||
Waiting, |
||||
/// Contract has failed
|
||||
Failed, |
||||
} |
||||
|
||||
/// The result of a transaction
|
||||
#[derive(Debug)] |
||||
pub struct TxOutcome { |
||||
/// The txid
|
||||
pub txid: H256, |
||||
/// True if executed, false otherwise
|
||||
pub executed: bool, |
||||
} |
||||
|
||||
impl From<TransactionReceipt> for TxOutcome { |
||||
fn from(t: TransactionReceipt) -> Self { |
||||
Self { |
||||
txid: t.transaction_hash, |
||||
executed: t.status.unwrap().low_u32() == 1, |
||||
} |
||||
} |
||||
} |
||||
|
||||
#[derive(Debug, Error)] |
||||
/// Error type for chain communication
|
||||
pub enum ChainCommunicationError { |
||||
/// Provider Error
|
||||
#[error(transparent)] |
||||
ProviderError(#[from] ethers_providers::ProviderError), |
||||
/// Contract Error
|
||||
#[error(transparent)] |
||||
ContractError(Box<dyn std::error::Error>), |
||||
/// Custom error or contract error
|
||||
#[error(transparent)] |
||||
CustomError(#[from] Box<dyn std::error::Error>), |
||||
} |
||||
|
||||
impl<M> From<ethers_contract::ContractError<M>> for ChainCommunicationError |
||||
where |
||||
M: ethers_providers::Middleware + 'static, |
||||
{ |
||||
fn from(e: ethers_contract::ContractError<M>) -> Self { |
||||
Self::ContractError(Box::new(e)) |
||||
} |
||||
} |
||||
|
||||
// impl<M> From<ethers_contract::ContractError<M>> for ChainCommunicationError
|
||||
// where
|
||||
// M: ethers_providers::Middleware + 'static,
|
||||
// {
|
||||
// fn from(e: ethers_contract::ContractError<M>) -> Self {
|
||||
// Self::ContractError(Box::new(e))
|
||||
// }
|
||||
// }
|
||||
|
||||
/// Interface for attributes shared by Home and Replica
|
||||
#[async_trait] |
||||
pub trait Common: Sync + Send + std::fmt::Debug { |
||||
/// Get the status of a transaction
|
||||
async fn status(&self, txid: H256) -> Result<Option<TxOutcome>, ChainCommunicationError>; |
||||
|
||||
/// Return the slip44 ID
|
||||
fn origin_slip44(&self) -> u32; |
||||
|
||||
/// Return the domain hash
|
||||
fn domain_hash(&self) -> H256 { |
||||
domain_hash(self.origin_slip44()) |
||||
} |
||||
|
||||
/// Fetch the current updater value
|
||||
async fn updater(&self) -> Result<H256, ChainCommunicationError>; |
||||
|
||||
/// Fetch the current state.
|
||||
async fn state(&self) -> Result<State, ChainCommunicationError>; |
||||
|
||||
/// Fetch the current root
|
||||
async fn current_root(&self) -> Result<H256, ChainCommunicationError>; |
||||
|
||||
/// Submit a signed update for inclusion
|
||||
async fn update(&self, update: &SignedUpdate) -> Result<TxOutcome, ChainCommunicationError>; |
||||
|
||||
/// Submit a double update for slashing
|
||||
async fn double_update( |
||||
&self, |
||||
left: &SignedUpdate, |
||||
right: &SignedUpdate, |
||||
) -> Result<TxOutcome, ChainCommunicationError>; |
||||
} |
@ -0,0 +1,31 @@ |
||||
use async_trait::async_trait; |
||||
use ethers_core::types::{H256, U256}; |
||||
|
||||
use crate::{ |
||||
traits::{ChainCommunicationError, Common, TxOutcome}, |
||||
Message, |
||||
}; |
||||
|
||||
/// Interface for on-chain replicas
|
||||
#[async_trait] |
||||
pub trait Replica: Common { |
||||
/// Return the pending root and time, if any
|
||||
async fn pending(&self) -> Result<Option<(H256, U256)>, ChainCommunicationError>; |
||||
|
||||
/// Confirm the next pending root (after its timer has elapsed);
|
||||
async fn confirm(&self) -> Result<TxOutcome, ChainCommunicationError>; |
||||
|
||||
/// Fetch the previous root.
|
||||
async fn previous_root(&self) -> Result<H256, ChainCommunicationError>; |
||||
|
||||
/// Prove inclusion of some leaf in the replica
|
||||
async fn prove( |
||||
&self, |
||||
leaf: H256, |
||||
index: u32, |
||||
proof: [H256; 32], |
||||
) -> Result<TxOutcome, ChainCommunicationError>; |
||||
|
||||
/// Trigger processing of a message
|
||||
async fn process(&self, message: &Message) -> Result<TxOutcome, ChainCommunicationError>; |
||||
} |
@ -0,0 +1,12 @@ |
||||
use ethers_core::types::H256; |
||||
use sha3::{Digest, Keccak256}; |
||||
|
||||
pub(crate) fn domain_hash(origin_slip44_id: u32) -> H256 { |
||||
H256::from_slice( |
||||
Keccak256::new() |
||||
.chain(origin_slip44_id.to_be_bytes()) |
||||
.chain("OPTICS".as_bytes()) |
||||
.finalize() |
||||
.as_slice(), |
||||
) |
||||
} |
Loading…
Reference in new issue