Skip to content

Commit

Permalink
config and testing done
Browse files Browse the repository at this point in the history
  • Loading branch information
ManojJiSharma committed Jul 23, 2024
1 parent 7d76366 commit 7c663d6
Show file tree
Hide file tree
Showing 6 changed files with 326 additions and 2 deletions.
17 changes: 17 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ members = [
"chains/arbitrum/testing/rosetta-testing-arbitrum",
"rosetta-utils",
"chains/polygon/rosetta-testing-polygon",
"chains/binance",
]
resolver = "2"

Expand Down
20 changes: 20 additions & 0 deletions chains/binance/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[package]
name = "rosetta-testing-binance"
version = "0.1.0"
edition = "2021"
license = "MIT"
repository = "https://github.com/analog-labs/chain-connectors"
description = "binance rosetta test."

[dependencies]
alloy-sol-types = { version = "0.7" }
anyhow = "1.0"
ethers = { version = "2.0", default-features = true, features = ["abigen", "rustls", "ws"] }
ethers-solc = "2.0"
hex-literal = "0.4"
rosetta-client.workspace = true
rosetta-config-ethereum.workspace = true
rosetta-core.workspace = true
rosetta-server-ethereum.workspace = true
sha3 = "0.10"
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }
285 changes: 285 additions & 0 deletions chains/binance/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,285 @@
//! # Binance Rosetta Server Test Suite
//!
//! This module contains a test suite for an Ethereum Rosetta server implementation
//! specifically designed for interacting with the Binance network. The code includes
//! tests for network status, account management, and smart contract interaction.
//!
//! ## Features
//!
//! - Network status tests to ensure proper connection and consistency with the Binance network.
//! - Account tests, including faucet funding, balance retrieval, and error handling.
//! - Smart contract tests covering deployment, event emission, and view function calls.
//!
//! ## Dependencies
//!
//! - `anyhow`: For flexible error handling.
//! - `alloy_sol_types`: Custom types and macros for interacting with Solidity contracts.
//! - `ethers`: Ethereum library for interaction with Ethereum clients.
//! - `ethers_solc`: Integration for compiling Solidity code using the Solc compiler.
//! - `hex_literal`: Macro for creating byte array literals from hexadecimal strings.
//! - `rosetta_client`: Client library for Rosetta API interactions.
//! - `rosetta_config_ethereum`: Configuration for Ethereum Rosetta server.
//! - `rosetta_server_ethereum`: Custom client implementation for interacting with Ethereum.
//! - `sha3`: SHA-3 (Keccak) implementation for hashing.
//! - `tokio`: Asynchronous runtime for running async functions.
//!
//! ## Usage
//!
//! To run the tests, execute the following command:
//!
//! ```sh
//! cargo test --package rosetta-testing-binance --lib -- tests --nocapture
//! ```
//!
//! Note: The code assumes a local Binance RPC node running on `ws://127.0.0.1:8546`. Ensure
//! that this endpoint is configured correctly.

#[allow(clippy::ignored_unit_patterns, clippy::pub_underscore_fields)]
#[cfg(test)]
mod tests {
use alloy_sol_types::{sol, SolCall};
use anyhow::Result;
use ethers::types::H256;

use ethers_solc::{artifacts::Source, CompilerInput, EvmVersion, Solc};
use hex_literal::hex;
use rosetta_client::Wallet;
use rosetta_config_ethereum::{AtBlock, CallResult};
use rosetta_core::BlockchainClient;
use rosetta_server_ethereum::MaybeWsEthereumClient;
use sha3::Digest;
use std::{collections::BTreeMap, future::Future, path::Path};

/// Binance rpc url
const BINANCE_RPC_WS_URL: &str = "ws://127.0.0.1:8546";

sol! {
interface TestContract {
event AnEvent();
function emitEvent() external;

function identity(bool a) external view returns (bool);
}
}

/// Run the test in another thread while sending txs to force binance to mine new blocks
/// # Panic
/// Panics if the future panics
async fn run_test<Fut: Future<Output = ()> + Send + 'static>(future: Fut) {
// Guarantee that only one test is incrementing blocks at a time
static LOCK: tokio::sync::Mutex<()> = tokio::sync::Mutex::const_new(());

// Run the test in another thread
let test_handler: tokio::task::JoinHandle<()> = tokio::spawn(future);

// Acquire Lock
let guard = LOCK.lock().await;

// Check if the test is finished after acquiring the lock
if test_handler.is_finished() {
// Release lock
drop(guard);

// Now is safe to panic
if let Err(err) = test_handler.await {
std::panic::resume_unwind(err.into_panic());
}
return;
}

// Now is safe to panic
if let Err(err) = test_handler.await {
// Resume the panic on the main task
std::panic::resume_unwind(err.into_panic());
}
}

#[tokio::test]
async fn network_status() {
run_test(async move {
let client = MaybeWsEthereumClient::new("binance", "dev", BINANCE_RPC_WS_URL, None)
.await
.expect("Error creating client");
// Check if the genesis is consistent
let genesis_block = client.genesis_block();
assert_eq!(genesis_block.index, 0);

// Check if the current block is consistent
let current_block = client.current_block().await.unwrap();
if current_block.index > 0 {
assert_ne!(current_block.hash, genesis_block.hash);
} else {
assert_eq!(current_block.hash, genesis_block.hash);
}

// Check if the finalized block is consistent
let finalized_block = client.finalized_block().await.unwrap();
assert!(finalized_block.index >= genesis_block.index);
})
.await;
}

#[tokio::test]
async fn test_account() {
run_test(async move {
let client = MaybeWsEthereumClient::new("binance", "dev", BINANCE_RPC_WS_URL, None)
.await
.expect("Error creating BinanceClient");
let wallet =
Wallet::from_config(client.config().clone(), BINANCE_RPC_WS_URL, None, None)
.await
.unwrap();
let value = 10 * u128::pow(10, client.config().currency_decimals);
let _ = wallet.faucet(value).await;
let amount = wallet.balance().await.unwrap();
assert_eq!(amount, value);
})
.await;
}

#[tokio::test]
async fn test_construction() {
run_test(async move {
let client = MaybeWsEthereumClient::new("binance", "dev", BINANCE_RPC_WS_URL, None)
.await
.expect("Error creating BinanceClient");
let faucet = 100 * u128::pow(10, client.config().currency_decimals);
let value = u128::pow(10, client.config().currency_decimals);
let alice =
Wallet::from_config(client.config().clone(), BINANCE_RPC_WS_URL, None, None)
.await
.unwrap();
let bob = Wallet::from_config(client.config().clone(), BINANCE_RPC_WS_URL, None, None)
.await
.unwrap();
assert_ne!(alice.public_key(), bob.public_key());

// Alice and bob have no balance
let balance = alice.balance().await.unwrap();
assert_eq!(balance, 0);
let balance = bob.balance().await.unwrap();
assert_eq!(balance, 0);

// Transfer faucets to alice
alice.faucet(faucet).await.unwrap();
let balance = alice.balance().await.unwrap();
assert_eq!(balance, faucet);

// Alice transfers to bob
alice.transfer(bob.account(), value, None, None).await.unwrap();
let amount = bob.balance().await.unwrap();
assert_eq!(amount, value);
})
.await;
}

fn compile_snippet(source: &str) -> Result<Vec<u8>> {
let solc = Solc::default();
let source = format!("contract Contract {{ {source} }}");
let mut sources = BTreeMap::new();
sources.insert(Path::new("contract.sol").into(), Source::new(source));
let input = CompilerInput::with_sources(sources)[0]
.clone()
.evm_version(EvmVersion::Homestead);
let output = solc.compile_exact(&input)?;
let file = output.contracts.get("contract.sol").unwrap();
let contract = file.get("Contract").unwrap();
let bytecode = contract
.evm
.as_ref()
.unwrap()
.bytecode
.as_ref()
.unwrap()
.object
.as_bytes()
.unwrap()
.to_vec();
Ok(bytecode)
}

#[tokio::test]
async fn test_smart_contract() {
run_test(async move {
let client = MaybeWsEthereumClient::new("binance", "dev", BINANCE_RPC_WS_URL, None)
.await
.expect("Error creating BinanceClient");
let faucet = 10 * u128::pow(10, client.config().currency_decimals);
let wallet =
Wallet::from_config(client.config().clone(), BINANCE_RPC_WS_URL, None, None)
.await
.unwrap();
wallet.faucet(faucet).await.unwrap();

let bytes = compile_snippet(
r"
event AnEvent();
function emitEvent() public {
emit AnEvent();
}
",
)
.unwrap();
let tx_hash = wallet.eth_deploy_contract(bytes).await.unwrap().tx_hash().0;
let receipt = wallet.eth_transaction_receipt(tx_hash).await.unwrap().unwrap();
let contract_address = receipt.contract_address.unwrap();
let tx_hash = {
let call = TestContract::emitEventCall {};
wallet
.eth_send_call(contract_address.0, call.abi_encode(), 0, None, None)
.await
.unwrap()
.tx_hash()
.0
};
let receipt = wallet.eth_transaction_receipt(tx_hash).await.unwrap().unwrap();
assert_eq!(receipt.logs.len(), 1);
let topic = receipt.logs[0].topics[0];
let expected = H256(sha3::Keccak256::digest("AnEvent()").into());
assert_eq!(topic, expected);
})
.await;
}

#[tokio::test]
async fn test_smart_contract_view() {
run_test(async move {
let client = MaybeWsEthereumClient::new("binance", "dev", BINANCE_RPC_WS_URL, None)
.await
.expect("Error creating BinanceClient");
let faucet = 10 * u128::pow(10, client.config().currency_decimals);
let wallet =
Wallet::from_config(client.config().clone(), BINANCE_RPC_WS_URL, None, None)
.await
.unwrap();
wallet.faucet(faucet).await.unwrap();
let bytes = compile_snippet(
r"
function identity(bool a) public view returns (bool) {
return a;
}
",
)
.unwrap();
let tx_hash = wallet.eth_deploy_contract(bytes).await.unwrap().tx_hash().0;
let receipt = wallet.eth_transaction_receipt(tx_hash).await.unwrap().unwrap();
let contract_address = receipt.contract_address.unwrap();

let response = {
let call = TestContract::identityCall { a: true };
wallet
.eth_view_call(contract_address.0, call.abi_encode(), AtBlock::Latest)
.await
.unwrap()
};
assert_eq!(
response,
CallResult::Success(
hex!("0000000000000000000000000000000000000000000000000000000000000001")
.to_vec()
)
);
})
.await;
}
}
2 changes: 1 addition & 1 deletion chains/ethereum/config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ pub fn arbitrum_config(network: &str) -> anyhow::Result<BlockchainConfig> {
pub fn binance_config(network: &str) -> anyhow::Result<BlockchainConfig> {
// All available networks in binance are listed here:
let (network, bip44_id, is_dev) = match network {
"dev" => ("dev", 1337, true),
"dev" => ("dev", 1, true),
"testnet" => ("testnet", 97, true),
"mainnet" => ("mainnet", 56, false),
_ => anyhow::bail!("unsupported network: {}", network),
Expand Down
3 changes: 2 additions & 1 deletion scripts/check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ if [[ "${RUN_TESTS}" == "1" ]]; then
--exclude rosetta-server-ethereum \
--exclude rosetta-server-polkadot \
--exclude rosetta-client \
--exclude rosetta-testing-arbitrum
--exclude rosetta-testing-arbitrum \
--exclude rosetta-testing-binance
# cargo test --locked --all-features --workspace
exec_cmd 'cleanup docker' "${SCRIPT_DIR}/reset_docker.sh"
fi
Expand Down

0 comments on commit 7c663d6

Please sign in to comment.