mirror of
https://github.com/fluencelabs/examples
synced 2024-12-04 19:20:17 +00:00
feat(eth): Use rust-web3 to serialize and serialize ETH calls (#417)
This commit is contained in:
parent
b537cdf345
commit
8999fcec85
@ -0,0 +1,24 @@
|
|||||||
|
[package]
|
||||||
|
name = "eth-rpc"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "eth-rpc"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
web3 = { version = "0.18.0", features = [], default-features = false }
|
||||||
|
#async-std = "1.12.0" # async-std does not support wasm32-wasi
|
||||||
|
serde_json = "1.0.91"
|
||||||
|
serde = "1.0.152"
|
||||||
|
jsonrpc-core = "18.0.0"
|
||||||
|
tokio = { version = "1.24.1", default-features = false, features = ["rt"] }
|
||||||
|
eyre = "0.6.8"
|
||||||
|
|
||||||
|
marine-rs-sdk = "0.7.1"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
marine-rs-sdk-test = "0.8.1"
|
@ -0,0 +1,16 @@
|
|||||||
|
# Build eth-rpc.wasm
|
||||||
|
```shell
|
||||||
|
marine build --release
|
||||||
|
```
|
||||||
|
|
||||||
|
# Build curl-adapter.wasm
|
||||||
|
```shell
|
||||||
|
cd ../curl-adapter
|
||||||
|
marine build --release
|
||||||
|
```
|
||||||
|
|
||||||
|
# Run tests
|
||||||
|
```shell
|
||||||
|
./test.sh
|
||||||
|
```
|
||||||
|
## It works!~
|
@ -0,0 +1,87 @@
|
|||||||
|
data RPCResponse:
|
||||||
|
value: string
|
||||||
|
success: bool
|
||||||
|
error: ?string
|
||||||
|
|
||||||
|
data RPCResult:
|
||||||
|
stdout: RPCResponse -- JSON-RPC result
|
||||||
|
stderr: string -- curl error and other non json-rpc errors
|
||||||
|
|
||||||
|
data Web3Balance:
|
||||||
|
balance: ?string -- hex string bignum
|
||||||
|
success: bool
|
||||||
|
error: ?string
|
||||||
|
|
||||||
|
data Web3GasUsed:
|
||||||
|
gas_used: ?string -- hex string
|
||||||
|
success: bool
|
||||||
|
error: ?string
|
||||||
|
|
||||||
|
data Web3EthCall:
|
||||||
|
result: ?string -- hex string
|
||||||
|
success: bool
|
||||||
|
error: ?string
|
||||||
|
|
||||||
|
service ParseToWeb3Balances("json"):
|
||||||
|
parse(s:string) -> Web3Balance
|
||||||
|
|
||||||
|
-- e.g., https://docs.infura.io/infura/networks/ethereum/json-rpc-methods/eth_getbalance
|
||||||
|
service Web3Services("service-id"):
|
||||||
|
call_eth_get_balance() -> RPCResult
|
||||||
|
call_eth_estimate_gas() -> RPCResult
|
||||||
|
call_eth_call() -> RPCResult
|
||||||
|
|
||||||
|
-- rpc_params: account id, blockheight: ususally "latest"
|
||||||
|
-- or we create a data struct and serialize it in aqua to []string
|
||||||
|
func eth_ getBalance(peerid: string, service_id: string, uri: string, rpc_params: Vec<String>, nonce: u32) -> Web3Balance:
|
||||||
|
result: *Web3Balance
|
||||||
|
on peerid:
|
||||||
|
Web3Services service_id
|
||||||
|
res <- Web3Services.call_eth_get_balance(uri, rpc_params, nonce)
|
||||||
|
if res.stdout.success==true:
|
||||||
|
result.balance <- res.stdout.value
|
||||||
|
result.success <<- true
|
||||||
|
else:
|
||||||
|
result.success <<- false
|
||||||
|
result.error <<- res.stdout.value
|
||||||
|
<- result[0]
|
||||||
|
|
||||||
|
-- here the data struct approach seems to make even more sense as we need the transaction call object:
|
||||||
|
-- data TObject:
|
||||||
|
-- from: ?[]u8 -- optional 20 bytes, address tx is sent from
|
||||||
|
-- to: []u8 -- 20 bytes to address
|
||||||
|
-- gas: ?string -- gas provided for execution of method haxadecimal
|
||||||
|
-- gasPrice: ?string -- gasPrice used, hexadecimal
|
||||||
|
-- maxFeesPerGase: ?string -- maximum fee in wei
|
||||||
|
-- value: ?string -- value sent with tx, hexadecimal
|
||||||
|
-- data: ?string -- hash of method signature and encoded params
|
||||||
|
--func eth_estimateGas(peerid: string, service_id: string, uri: string, t_obj: TObject, nonce: u32) -> Web3GasUsed:
|
||||||
|
-- the "easy" way: Vec<String>
|
||||||
|
func eth_estimateGas(peerid: string, service_id: string, uri: string, rpc_params: []string, nonce: u32) -> Web3GasUsed:
|
||||||
|
result: *Web3Gas
|
||||||
|
on peerid:
|
||||||
|
Web3Services service_id
|
||||||
|
res <- Web3Services.call_eth_estimate_gas(uri, rpc_params, nonce)
|
||||||
|
if res.stdout.success==true:
|
||||||
|
result.gas_used <- res.stdout.value
|
||||||
|
result.success <<- true
|
||||||
|
else:
|
||||||
|
result.success <<- false
|
||||||
|
result.error <<- res.stdout.value
|
||||||
|
<- result
|
||||||
|
|
||||||
|
-- also a big intake object, e.g. https://docs.infura.io/infura/networks/ethereum/json-rpc-methods/eth_call
|
||||||
|
-- easy way -- client serializes to []string
|
||||||
|
func eth_call(peerid: string, service_id: string, uri: string, rpc_params: []string, nonce: u32) -> Web3EthCall:
|
||||||
|
result: *Web3EthCall
|
||||||
|
on peerid:
|
||||||
|
Web3Services service_id
|
||||||
|
res <- Web3Services.call_eth_call(uri, rpc_params, nonce)
|
||||||
|
if res.stdout.success==true:
|
||||||
|
result.result <- res.stdout.value
|
||||||
|
result.success <<- true
|
||||||
|
else:
|
||||||
|
result.success <<- false
|
||||||
|
result.error <<- res.stdout.error ---not sure if this is how Web3 does it if there is a Revert error, e.g. https://docs.infura.io/infura/networks/ethereum/json-rpc-methods/eth_call
|
||||||
|
<- result
|
||||||
|
|
@ -0,0 +1,66 @@
|
|||||||
|
|
||||||
|
-- https://www.jsonrpc.org/specification
|
||||||
|
data RPCError:
|
||||||
|
code: i32
|
||||||
|
message: string
|
||||||
|
data: ?string
|
||||||
|
|
||||||
|
data RPCResponse:
|
||||||
|
jsonrpc: string
|
||||||
|
result: string
|
||||||
|
error: ?RPCError
|
||||||
|
id: u32
|
||||||
|
|
||||||
|
data RPCResponse2:
|
||||||
|
value: string
|
||||||
|
success: bool
|
||||||
|
error: ?string
|
||||||
|
|
||||||
|
data RPCResult:
|
||||||
|
stdout: RPCResponse -- JSON-RPC result
|
||||||
|
stderr: string -- curl error and other non json-rpc errors
|
||||||
|
|
||||||
|
|
||||||
|
data RPCResult2:
|
||||||
|
stdout: RPCResponse2 -- JSON-RPC result
|
||||||
|
stderr: string -- curl error and other non json-rpc errors
|
||||||
|
|
||||||
|
|
||||||
|
data Web3Accounts:
|
||||||
|
accounts: []string
|
||||||
|
|
||||||
|
service ParseToAccounts("json"):
|
||||||
|
parse(s:string) -> Web3Accounts
|
||||||
|
|
||||||
|
service Web3Services("service-id"):
|
||||||
|
call_get_accounts() -> [][]u8
|
||||||
|
call_get_accounts_json() -> RPCResult
|
||||||
|
call_get_accounts_json_2() -> RPCResult2
|
||||||
|
|
||||||
|
|
||||||
|
-- the bytestring return which allows you to do nothing until you convert
|
||||||
|
-- the bytes using another service to be deployed and a pita to sort through
|
||||||
|
-- error types
|
||||||
|
func get_accounts(peerid: string, service_id: string) -> [][]u8:
|
||||||
|
on peerid:
|
||||||
|
Web3Services service_id
|
||||||
|
res <- Web3Services.call_get_accounts()
|
||||||
|
<- res
|
||||||
|
|
||||||
|
func get_accounts_jstring(peerid: string, service_id: string) -> Web3Accounts:
|
||||||
|
on peerid:
|
||||||
|
Web3Services service_id
|
||||||
|
res <- Web3Services.call_get_accounts_json()
|
||||||
|
-- if not error ...
|
||||||
|
-- if not rpc error
|
||||||
|
accounts <- ParseToAccounts.parse(res.stdout.result)
|
||||||
|
<- accounts
|
||||||
|
|
||||||
|
func get_accounts_jstring_2(peerid: string, service_id: string) -> Web3Accounts:
|
||||||
|
on peerid:
|
||||||
|
Web3Services service_id
|
||||||
|
res <- Web3Services.call_get_accounts_json_2()
|
||||||
|
-- if not error ....
|
||||||
|
if res.stdout.success == true:
|
||||||
|
accounts <- ParseToAccounts.parse(res.stdout.value)
|
||||||
|
<- accounts
|
@ -0,0 +1,4 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "nightly-2022-12-06"
|
||||||
|
targets = [ "x86_64-unknown-linux-gnu" ]
|
||||||
|
components = [ "rustfmt" ]
|
@ -0,0 +1,120 @@
|
|||||||
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use jsonrpc_core::types::request::Call;
|
||||||
|
use jsonrpc_core::Output;
|
||||||
|
use serde_json::json;
|
||||||
|
use serde_json::Value;
|
||||||
|
use web3::futures::future::BoxFuture;
|
||||||
|
use web3::{RequestId, Transport};
|
||||||
|
|
||||||
|
use crate::curl_request;
|
||||||
|
|
||||||
|
pub type FutResult = BoxFuture<'static, web3::error::Result<Value>>;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct CurlTransport {
|
||||||
|
pub uri: String,
|
||||||
|
id: Arc<AtomicUsize>,
|
||||||
|
}
|
||||||
|
impl CurlTransport {
|
||||||
|
pub fn new(uri: String) -> Self {
|
||||||
|
Self {
|
||||||
|
uri,
|
||||||
|
id: Arc::new(AtomicUsize::new(0)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next_id(&self) -> RequestId {
|
||||||
|
self.id.fetch_add(1, Ordering::AcqRel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Transport for CurlTransport {
|
||||||
|
type Out = FutResult;
|
||||||
|
|
||||||
|
fn prepare(&self, method: &str, params: Vec<Value>) -> (RequestId, Call) {
|
||||||
|
let id = self.next_id();
|
||||||
|
let request = web3::helpers::build_request(id, method, params.clone());
|
||||||
|
(id, request)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send(&self, _: RequestId, call: Call) -> Self::Out {
|
||||||
|
if let Call::MethodCall(call) = call {
|
||||||
|
/*
|
||||||
|
curl --request POST \
|
||||||
|
--url $uri \
|
||||||
|
--header 'accept: application/json' \
|
||||||
|
--header 'content-type: application/json' \
|
||||||
|
--data '
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "eth_accounts"
|
||||||
|
}
|
||||||
|
'
|
||||||
|
*/
|
||||||
|
let uri = self.uri.clone();
|
||||||
|
Box::pin(async move {
|
||||||
|
let json = json!(call).to_string();
|
||||||
|
let args = vec![
|
||||||
|
"--request",
|
||||||
|
"POST",
|
||||||
|
"--url",
|
||||||
|
&uri,
|
||||||
|
"--header",
|
||||||
|
"accept: application/json",
|
||||||
|
"--header",
|
||||||
|
"content-type: application/json",
|
||||||
|
"--data",
|
||||||
|
json.as_str(),
|
||||||
|
];
|
||||||
|
let args = args.into_iter().map(|s| s.to_string()).collect();
|
||||||
|
let response = curl_request(args);
|
||||||
|
|
||||||
|
/*
|
||||||
|
println!(
|
||||||
|
"response is: \nstdout: {:?}\nstderr: {:?}",
|
||||||
|
String::from_utf8(response.stdout.clone()),
|
||||||
|
String::from_utf8(response.stderr.clone())
|
||||||
|
);
|
||||||
|
|
||||||
|
println!("slice: {:?}", serde_json::from_value::<Output>(serde_json::from_slice(response.stdout.as_slice())?));
|
||||||
|
*/
|
||||||
|
|
||||||
|
// FIX: if there's a bad uri, the panic kicks in here.
|
||||||
|
|
||||||
|
let response: Output =
|
||||||
|
serde_json::from_value(serde_json::from_slice(response.stdout.as_slice())?)?;
|
||||||
|
|
||||||
|
let result = match response {
|
||||||
|
Output::Success(jsonrpc_core::types::Success { result, .. }) => result,
|
||||||
|
|
||||||
|
// no sure if that's enough vs the complete jsonrpc error msg
|
||||||
|
Output::Failure(jsonrpc_core::types::Failure { error, .. }) => {
|
||||||
|
serde_json::to_value(error.message).unwrap()
|
||||||
|
} /*
|
||||||
|
Output::Failure(failure) => panic!(
|
||||||
|
"JSON RPC response was a failure {}",
|
||||||
|
json!(failure).to_string()
|
||||||
|
),
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
Output::Failure(failure) => {
|
||||||
|
let err = jsonrpc_core::types::error::Error.parse_error()
|
||||||
|
}
|
||||||
|
|
||||||
|
format!("JSON RPC response was a failure {}",
|
||||||
|
json!(failure).to_string()),
|
||||||
|
*/
|
||||||
|
};
|
||||||
|
|
||||||
|
// println!("parsed result is {}", result.to_string());
|
||||||
|
Ok(result)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
// Box::pin(async { Ok(json!(["0x407d73d8a49eeb85d32cf465507dd71d507100c1"])) })
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,103 @@
|
|||||||
|
use marine_rs_sdk::marine;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_json::Value;
|
||||||
|
use tokio::runtime::Builder;
|
||||||
|
use web3::Transport;
|
||||||
|
|
||||||
|
use crate::curl_transport::CurlTransport;
|
||||||
|
use crate::values::JsonString;
|
||||||
|
|
||||||
|
#[marine]
|
||||||
|
pub fn eth_call(uri: String, method: &str, json_args: Vec<String>) -> JsonString {
|
||||||
|
let result: eyre::Result<Value> = try {
|
||||||
|
let rt = Builder::new_current_thread().build()?;
|
||||||
|
|
||||||
|
let args: Result<Vec<Value>, _> = json_args
|
||||||
|
.into_iter()
|
||||||
|
.map(|a| serde_json::from_str(&a))
|
||||||
|
.collect();
|
||||||
|
let transport = CurlTransport::new(uri);
|
||||||
|
let result = rt.block_on(transport.execute(method, args?))?;
|
||||||
|
|
||||||
|
result
|
||||||
|
};
|
||||||
|
|
||||||
|
result.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[marine]
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
|
pub struct RPCResult {
|
||||||
|
provider_name: String,
|
||||||
|
stdout: String,
|
||||||
|
stderr: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn eth_call_2(uri: String, method: &str, json_args: Vec<String>) -> JsonString {
|
||||||
|
let result: eyre::Result<Value> = try {
|
||||||
|
let rt = Builder::new_current_thread().build()?;
|
||||||
|
|
||||||
|
let args: Result<Vec<Value>, _> = json_args
|
||||||
|
.into_iter()
|
||||||
|
.map(|a| serde_json::from_str(&a))
|
||||||
|
.collect();
|
||||||
|
let transport = CurlTransport::new(uri);
|
||||||
|
let result = rt.block_on(transport.execute(method, args?))?;
|
||||||
|
|
||||||
|
result
|
||||||
|
};
|
||||||
|
|
||||||
|
result.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use marine_rs_sdk_test::marine_test;
|
||||||
|
|
||||||
|
#[marine_test(
|
||||||
|
config_path = "../tests_artifacts/Config.toml",
|
||||||
|
modules_dir = "../tests_artifacts"
|
||||||
|
)]
|
||||||
|
fn get_accounts_bad_uri(rpc: marine_test_env::eth_rpc::ModuleInterface) {
|
||||||
|
let uri: String = "http://bad_uri.to".into();
|
||||||
|
let method: String = "eth_accounts".into();
|
||||||
|
let json_args: Vec<String> = vec![];
|
||||||
|
|
||||||
|
let accounts = rpc.eth_call(uri, method, json_args);
|
||||||
|
println!("bad uri call: {:?}", accounts);
|
||||||
|
// println!("accounts: {:?}", accounts);
|
||||||
|
// assert_eq!(accounts.len(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[marine_test(
|
||||||
|
config_path = "../tests_artifacts/Config.toml",
|
||||||
|
modules_dir = "../tests_artifacts"
|
||||||
|
)]
|
||||||
|
fn get_accounts_bad_method(rpc: marine_test_env::eth_rpc::ModuleInterface) {
|
||||||
|
let uri: String = std::fs::read_to_string("./infura_uri.txt").unwrap();
|
||||||
|
let method: String = "eth_getAccounts".into();
|
||||||
|
let json_args: Vec<String> = vec![];
|
||||||
|
|
||||||
|
let accounts = rpc.eth_call(uri, method, json_args);
|
||||||
|
println!("bad method: {:?}", accounts);
|
||||||
|
|
||||||
|
// println!("accounts: {:?}", accounts);
|
||||||
|
// assert_eq!(accounts.len(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[marine_test(
|
||||||
|
config_path = "../tests_artifacts/Config.toml",
|
||||||
|
modules_dir = "../tests_artifacts"
|
||||||
|
)]
|
||||||
|
fn get_accounts_good_uri(rpc: marine_test_env::eth_rpc::ModuleInterface) {
|
||||||
|
let uri: String = std::fs::read_to_string("./infura_uri.txt").unwrap();
|
||||||
|
let method: String = "eth_accounts".into();
|
||||||
|
let json_args: Vec<String> = vec![];
|
||||||
|
|
||||||
|
let accounts = rpc.eth_call(uri, method, json_args);
|
||||||
|
println!("all good: {:?}", accounts);
|
||||||
|
|
||||||
|
// println!("accounts: {:?}", accounts);
|
||||||
|
// assert_eq!(accounts.len(), 0);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,104 @@
|
|||||||
|
#![feature(try_blocks)]
|
||||||
|
|
||||||
|
use marine_rs_sdk::module_manifest;
|
||||||
|
use marine_rs_sdk::{marine, MountedBinaryResult};
|
||||||
|
use tokio::runtime::Builder;
|
||||||
|
use web3::api::Eth;
|
||||||
|
use web3::helpers::CallFuture;
|
||||||
|
use web3::types::Address;
|
||||||
|
use web3::Web3;
|
||||||
|
|
||||||
|
use crate::curl_transport::{CurlTransport, FutResult};
|
||||||
|
|
||||||
|
pub mod curl_transport;
|
||||||
|
pub mod eth_call;
|
||||||
|
pub mod typed;
|
||||||
|
pub mod values;
|
||||||
|
|
||||||
|
module_manifest!();
|
||||||
|
|
||||||
|
pub fn main() {}
|
||||||
|
|
||||||
|
// #[tokio::main(flavor = "current_thread")]
|
||||||
|
// flavor idea comes from https://github.com/rjzak/tokio-echo-test/blob/main/src/main.rs#L42
|
||||||
|
// but seems to require additional tokio futures
|
||||||
|
pub fn get_accounts(uri: String) -> web3::error::Result<Vec<Vec<u8>>> {
|
||||||
|
let rt = Builder::new_current_thread().build()?;
|
||||||
|
|
||||||
|
let web3 = Web3::new(CurlTransport::new(uri));
|
||||||
|
|
||||||
|
let eth = web3.eth();
|
||||||
|
println!("Calling accounts.");
|
||||||
|
let accounts: CallFuture<Vec<Address>, FutResult> = eth.accounts();
|
||||||
|
let accounts: web3::Result<Vec<Address>> = rt.block_on(accounts);
|
||||||
|
println!("Accounts: {:?}", accounts);
|
||||||
|
|
||||||
|
Ok(accounts?
|
||||||
|
.into_iter()
|
||||||
|
.map(|a: Address| a.as_bytes().to_vec())
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn web3_call<
|
||||||
|
Out: serde::de::DeserializeOwned,
|
||||||
|
F: FnOnce(Eth<CurlTransport>) -> CallFuture<Out, FutResult>,
|
||||||
|
>(
|
||||||
|
uri: String,
|
||||||
|
call: F,
|
||||||
|
) -> web3::error::Result<Out> {
|
||||||
|
let rt = Builder::new_current_thread()
|
||||||
|
.build()
|
||||||
|
.expect("error starting tokio runtime");
|
||||||
|
|
||||||
|
let web3 = Web3::new(CurlTransport::new(uri));
|
||||||
|
|
||||||
|
let result: CallFuture<Out, FutResult> = call(web3.eth());
|
||||||
|
let result: web3::error::Result<Out> = rt.block_on(result);
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
#[marine]
|
||||||
|
pub fn call_get_accounts(uri: String) -> Vec<Vec<u8>> {
|
||||||
|
get_accounts(uri).expect("error calling main")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[marine]
|
||||||
|
#[link(wasm_import_module = "curl_adapter")]
|
||||||
|
extern "C" {
|
||||||
|
pub fn curl_request(cmd: Vec<String>) -> MountedBinaryResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use marine_rs_sdk_test::marine_test;
|
||||||
|
// use web3::types::Address;
|
||||||
|
|
||||||
|
#[marine_test(
|
||||||
|
config_path = "../tests_artifacts/Config.toml",
|
||||||
|
modules_dir = "../tests_artifacts"
|
||||||
|
)]
|
||||||
|
fn get_accounts(rpc: marine_test_env::eth_rpc::ModuleInterface) {
|
||||||
|
let uri: String = std::fs::read_to_string("./infura_uri.txt").unwrap();
|
||||||
|
let accounts = rpc.call_get_accounts(uri);
|
||||||
|
// let addr: Address = "0x407d73d8a49eeb85d32cf465507dd71d507100c1"
|
||||||
|
// .parse()
|
||||||
|
// .unwrap();
|
||||||
|
// assert_eq!(accounts, vec![addr.as_bytes().to_vec()]);
|
||||||
|
assert_eq!(accounts.len(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[marine_test(
|
||||||
|
config_path = "../tests_artifacts/Config.toml",
|
||||||
|
modules_dir = "../tests_artifacts"
|
||||||
|
)]
|
||||||
|
fn get_accounts_generic(rpc: marine_test_env::eth_rpc::ModuleInterface) {
|
||||||
|
let uri: String = std::fs::read_to_string("./infura_uri.txt").unwrap();
|
||||||
|
let method: String = "eth_accounts".into();
|
||||||
|
let json_args: Vec<String> = vec![];
|
||||||
|
|
||||||
|
let accounts = rpc.eth_call(uri, method, json_args);
|
||||||
|
println!("accounts: {:?}", accounts);
|
||||||
|
// assert_eq!(accounts.len(), 0);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,263 @@
|
|||||||
|
#![allow(unused)]
|
||||||
|
|
||||||
|
use marine_rs_sdk::marine;
|
||||||
|
use web3::types::{BlockId, BlockNumber, Bytes, CallRequest};
|
||||||
|
|
||||||
|
use crate::values::{BytesValue, JsonString, U64Value};
|
||||||
|
use crate::web3_call;
|
||||||
|
|
||||||
|
/// Get list of available accounts.
|
||||||
|
#[marine]
|
||||||
|
pub fn accounts(uri: String) -> Vec<JsonString> {
|
||||||
|
web3_call(uri, |w| w.accounts())
|
||||||
|
.into_iter()
|
||||||
|
.map(|a| {
|
||||||
|
let json = serde_json::to_value(&a).map_err(eyre::Report::from);
|
||||||
|
JsonString::from(json)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get current block number
|
||||||
|
#[marine]
|
||||||
|
pub fn block_number(uri: String) -> U64Value {
|
||||||
|
web3_call(uri, |w| w.block_number()).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call a constant method of contract without changing the state of the blockchain.
|
||||||
|
#[marine]
|
||||||
|
pub fn call(uri: String, req: String, block: u64) -> BytesValue {
|
||||||
|
let result: eyre::Result<Bytes> = try {
|
||||||
|
let req: CallRequest = serde_json::from_str(&req)?;
|
||||||
|
web3_call(uri, move |w| w.call(req, Some(BlockId::Number(BlockNumber::Number(block.into())))))?
|
||||||
|
};
|
||||||
|
|
||||||
|
result.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get coinbase address
|
||||||
|
// #[marine]
|
||||||
|
pub fn coinbase(uri: String) -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compile LLL
|
||||||
|
// #[marine]
|
||||||
|
pub fn compile_lll(uri: String, code: String) -> Vec<u8> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compile Solidity
|
||||||
|
// #[marine]
|
||||||
|
pub fn compile_solidity(uri: String, code: String) -> Vec<u8> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compile Serpent
|
||||||
|
// #[marine]
|
||||||
|
pub fn compile_serpent(uri: String, code: String) -> Vec<u8> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Call a contract without changing the state of the blockchain to estimate gas usage.
|
||||||
|
// #[marine]
|
||||||
|
pub fn estimate_gas(uri: String, req: String, block: String) -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get current recommended gas price
|
||||||
|
// #[marine]
|
||||||
|
pub fn gas_price(uri: String) -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a collection of historical gas information. This can be used for evaluating the max_fee_per_gas
|
||||||
|
/// and max_priority_fee_per_gas to send the future transactions.
|
||||||
|
// #[marine]
|
||||||
|
pub fn fee_history(
|
||||||
|
uri: String,
|
||||||
|
block_count: String,
|
||||||
|
newest_block: String,
|
||||||
|
reward_percentiles: Vec<f64>,
|
||||||
|
) -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get balance of given address
|
||||||
|
// #[marine]
|
||||||
|
pub fn balance(uri: String, address: String, block: String) -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get all logs matching a given filter object
|
||||||
|
// #[marine]
|
||||||
|
pub fn logs(uri: String, filter: String) -> Vec<String> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get block details with transaction hashes.
|
||||||
|
// #[marine]
|
||||||
|
pub fn block(uri: String, block: String) -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get block details with full transaction objects.
|
||||||
|
// #[marine]
|
||||||
|
pub fn block_with_txs(uri: String, block: String) -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get number of transactions in block
|
||||||
|
// #[marine]
|
||||||
|
pub fn block_transaction_count(uri: String, block: String) -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get code under given address
|
||||||
|
// #[marine]
|
||||||
|
pub fn code(uri: String, address: String, block: String) -> Vec<u8> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get supported compilers
|
||||||
|
// #[marine]
|
||||||
|
pub fn compilers(uri: String) -> Vec<String> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get chain id
|
||||||
|
// #[marine]
|
||||||
|
pub fn chain_id(uri: String) -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get available user accounts. This method is only available in the browser. With MetaMask,
|
||||||
|
/// this will cause the popup that prompts the user to allow or deny access to their accounts
|
||||||
|
/// to your app.
|
||||||
|
// #[marine]
|
||||||
|
pub fn request_accounts(uri: String) -> Vec<String> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get storage entry
|
||||||
|
// #[marine]
|
||||||
|
pub fn storage(uri: String, address: String, idx: String, block: String) -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get nonce
|
||||||
|
// #[marine]
|
||||||
|
pub fn transaction_count(uri: String, address: String, block: String) -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get transaction
|
||||||
|
// #[marine]
|
||||||
|
pub fn transaction(uri: String, id: String) -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get transaction receipt
|
||||||
|
// #[marine]
|
||||||
|
pub fn transaction_receipt(uri: String, hash: String) -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get uncle header by block ID and uncle index.
|
||||||
|
///
|
||||||
|
/// This method is meant for TurboGeth compatiblity,
|
||||||
|
/// which is missing transaction hashes in the response.
|
||||||
|
// #[marine]
|
||||||
|
pub fn uncle_header(uri: String, block: String, index: String) -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get uncle by block ID and uncle index -- transactions only has hashes.
|
||||||
|
// #[marine]
|
||||||
|
pub fn uncle(uri: String, block: String, index: String) -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get uncle count in block
|
||||||
|
// #[marine]
|
||||||
|
pub fn uncle_count(uri: String, block: String) -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get work package
|
||||||
|
// #[marine]
|
||||||
|
pub fn work(uri: String) -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get hash rate
|
||||||
|
// #[marine]
|
||||||
|
pub fn hashrate(uri: String) -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get mining status
|
||||||
|
// #[marine]
|
||||||
|
pub fn mining(uri: String) -> bool {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start new block filter
|
||||||
|
// #[marine]
|
||||||
|
pub fn new_block_filter(uri: String) -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start new pending transaction filter
|
||||||
|
// #[marine]
|
||||||
|
pub fn new_pending_transaction_filter(uri: String) -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start new pending transaction filter
|
||||||
|
// #[marine]
|
||||||
|
pub fn protocol_version(uri: String) -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sends a rlp-encoded signed transaction
|
||||||
|
// #[marine]
|
||||||
|
pub fn send_raw_transaction(uri: String, rlp: String) -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sends a transaction transaction
|
||||||
|
// #[marine]
|
||||||
|
pub fn send_transaction(uri: String, tx: String) -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Signs a hash of given data
|
||||||
|
// #[marine]
|
||||||
|
pub fn sign(uri: String, address: String, data: String) -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Submit hashrate of external miner
|
||||||
|
// #[marine]
|
||||||
|
pub fn submit_hashrate(uri: String, rate: String, id: String) -> bool {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Submit work of external miner
|
||||||
|
// #[marine]
|
||||||
|
pub fn submit_work(uri: String, nonce: String, pow_hash: String, mix_hash: String) -> bool {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get syncing status
|
||||||
|
// #[marine]
|
||||||
|
pub fn syncing(uri: String) -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the account- and storage-values of the specified account including the Merkle-proof.
|
||||||
|
// #[marine]
|
||||||
|
pub fn proof(uri: String, address: String, keys: String, block: String) -> String {
|
||||||
|
todo!()
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
use marine_rs_sdk::marine;
|
||||||
|
use serde_json::Value;
|
||||||
|
use web3::types::Bytes;
|
||||||
|
use web3::types::U64;
|
||||||
|
|
||||||
|
#[marine]
|
||||||
|
pub struct JsonString {
|
||||||
|
value: String,
|
||||||
|
success: bool,
|
||||||
|
error: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<eyre::Result<Value>> for JsonString {
|
||||||
|
fn from(value: eyre::Result<Value>) -> Self {
|
||||||
|
match value {
|
||||||
|
Ok(value) => JsonString {
|
||||||
|
value: value.to_string(),
|
||||||
|
success: true,
|
||||||
|
error: String::new(),
|
||||||
|
},
|
||||||
|
Err(err) => JsonString {
|
||||||
|
value: String::new(),
|
||||||
|
success: false,
|
||||||
|
error: format!("{}\n{:?}", err, err),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[marine]
|
||||||
|
pub struct U64Value {
|
||||||
|
value: u64,
|
||||||
|
success: bool,
|
||||||
|
error: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<web3::error::Result<U64>> for U64Value {
|
||||||
|
fn from(value: web3::error::Result<U64>) -> Self {
|
||||||
|
match value {
|
||||||
|
Ok(value) => U64Value {
|
||||||
|
value: value.as_u64(),
|
||||||
|
success: true,
|
||||||
|
error: String::new(),
|
||||||
|
},
|
||||||
|
Err(err) => U64Value {
|
||||||
|
value: u64::default(),
|
||||||
|
success: false,
|
||||||
|
error: format!("{}\n{:?}", err, err),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[marine]
|
||||||
|
pub struct BytesValue {
|
||||||
|
value: Vec<u8>,
|
||||||
|
success: bool,
|
||||||
|
error: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<eyre::Result<Bytes>> for BytesValue {
|
||||||
|
fn from(value: eyre::Result<Bytes>) -> Self {
|
||||||
|
match value {
|
||||||
|
Ok(value) => BytesValue {
|
||||||
|
value: value.0,
|
||||||
|
success: true,
|
||||||
|
error: String::new(),
|
||||||
|
},
|
||||||
|
Err(err) => BytesValue {
|
||||||
|
value: vec![],
|
||||||
|
success: false,
|
||||||
|
error: format!("{}\n{:?}", err, err),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
aqua-examples/decentralized-blockchain-gateway/wasm-modules/eth-rpc/test.sh
Executable file
25
aqua-examples/decentralized-blockchain-gateway/wasm-modules/eth-rpc/test.sh
Executable file
@ -0,0 +1,25 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -o nounset
|
||||||
|
set -o errexit
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
mkdir -p tests_artifacts
|
||||||
|
|
||||||
|
# build eth-rpc.wasm
|
||||||
|
marine build --release
|
||||||
|
cp ./target/wasm32-wasi/release/eth-rpc.wasm tests_artifacts/
|
||||||
|
|
||||||
|
# build curl-adapter.wasm
|
||||||
|
(cd ../curl-adapter; marine build --release)
|
||||||
|
cp ../curl-adapter/target/wasm32-wasi/release/curl_adapter.wasm tests_artifacts/
|
||||||
|
|
||||||
|
#if [[ ! -f "tests_artifacts/sqlite3.wasm" ]]; then
|
||||||
|
# # download SQLite 3
|
||||||
|
# curl -L https://github.com/fluencelabs/sqlite/releases/download/v0.15.0_w/sqlite3.wasm -o tests_artifacts/sqlite3.wasm
|
||||||
|
#fi
|
||||||
|
|
||||||
|
# run tests
|
||||||
|
cargo nextest run --release --no-fail-fast --nocapture
|
@ -0,0 +1,23 @@
|
|||||||
|
modules_dir = "."
|
||||||
|
|
||||||
|
#[[module]]
|
||||||
|
#name = "sqlite3"
|
||||||
|
#mem_pages_count = 100
|
||||||
|
#logger_enabled = false
|
||||||
|
|
||||||
|
#[module.wasi]
|
||||||
|
#preopened_files = ["/tmp"]
|
||||||
|
#mapped_dirs = { "tmp" = "." }
|
||||||
|
|
||||||
|
[[module]]
|
||||||
|
name = "curl_adapter"
|
||||||
|
max_heap_size = "2 MiB"
|
||||||
|
logger_enabled = true
|
||||||
|
|
||||||
|
[module.mounted_binaries]
|
||||||
|
curl = "/usr/bin/curl"
|
||||||
|
|
||||||
|
[[module]]
|
||||||
|
name = "eth-rpc"
|
||||||
|
|
||||||
|
logger_enabled = true
|
Binary file not shown.
@ -0,0 +1,29 @@
|
|||||||
|
# Fluence decentralized RPC Workshop
|
||||||
|
|
||||||
|
ETHDenver 2/27/2023
|
||||||
|
|
||||||
|
|
||||||
|
##Outline
|
||||||
|
|
||||||
|
## Intended bounty user experience
|
||||||
|
|
||||||
|
* embedd a the already created Fluence client peer into your dApp
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Deployment and code
|
||||||
|
|
||||||
|
* deploy eth-rpc service to each KRAS (?) peer at least once
|
||||||
|
* create Registry instance for deployed services
|
||||||
|
* create Aqua scaffold to interct with registry and modules
|
||||||
|
* create Fluence JS reference client to embed into dApp
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
Fluence decentralized RPC (fRPC) is a ready to use ???
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user