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

add private network to network enum #7

Merged
merged 7 commits into from
Aug 28, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 6 additions & 6 deletions corebc-blockindex/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,21 +53,21 @@ impl Client {
let mut res: Transaction = serde_json::from_value(response.clone())?;
res.from = response["vin"][0]["addresses"][0].to_string().replace('\"', "");
res.to = response["vout"][0]["addresses"][0].to_string().replace('\"', "");
res.status = response["ethereumSpecific"]["status"]
res.status = response["corecoinSpecific"]["status"]
.as_u64()
.ok_or_else(|| BlockindexError::Builder("status".to_string()))?;
res.nonce = response["ethereumSpecific"]["nonce"]
res.nonce = response["corecoinSpecific"]["nonce"]
.as_u64()
.ok_or_else(|| BlockindexError::Builder("nonce".to_string()))?;
res.energy_limit = response["ethereumSpecific"]["energyLimit"]
res.energy_limit = response["corecoinSpecific"]["energyLimit"]
.as_u64()
.ok_or_else(|| BlockindexError::Builder("energyLimit".to_string()))?;
res.energy_used = response["ethereumSpecific"]["energyUsed"]
res.energy_used = response["corecoinSpecific"]["energyUsed"]
.as_u64()
.ok_or_else(|| BlockindexError::Builder("energyUsed".to_string()))?;
res.energy_price =
response["ethereumSpecific"]["energyPrice"].to_string().replace('\"', "");
res.data = response["ethereumSpecific"]["data"].to_string().replace('\"', "");
response["corecoinSpecific"]["energyPrice"].to_string().replace('\"', "");
res.data = response["corecoinSpecific"]["data"].to_string().replace('\"', "");

Ok(res)
}
Expand Down
4 changes: 2 additions & 2 deletions corebc-blockindex/tests/it/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ async fn get_balance_history_success() {
let account = &"ab30f0091ce7386584b85eecf92f75157582179887ce".parse().unwrap();
run_with_client(Network::Devin, |client| async move {
let history = client.get_balance_history(account, None).await;
assert_eq!(history.unwrap().len(), 2);
assert_eq!(history.unwrap().len(), 4);
})
.await
}
Expand All @@ -92,7 +92,7 @@ async fn get_balance_history_empty() {
run_with_client(Network::Devin, |client| async move {
let history = client
.get_balance_history(
&"ae57dde1a47041fc3c570c0318a713128ced55fd2ada".parse().unwrap(),
&"ab720000000000000000000000000000000000000000".parse().unwrap(),
None,
)
.await;
Expand Down
4 changes: 2 additions & 2 deletions corebc-contract/tests/it/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use corebc_core::{
utils::AnvilInstance,
};
use corebc_providers::{Http, Middleware, Provider};
use corebc_ylem::Solc;
use corebc_ylem::Ylem;
use std::{convert::TryFrom, sync::Arc, time::Duration};

// Note: The `EthEvent` derive macro implements the necessary conversion between `Tokens` and
Expand All @@ -25,7 +25,7 @@ pub struct ValueChanged {
#[track_caller]
pub fn compile_contract(name: &str, filename: &str) -> (Abi, Bytes) {
let path = format!("./tests/solidity-contracts/{filename}");
let compiled = Solc::default().compile_source(&path).unwrap();
let compiled = Ylem::default().compile_source(&path).unwrap();
let contract = compiled.get(&path, name).expect("could not find contract");
let (abi, bin, _) = contract.into_parts_or_default();
(abi, bin)
Expand Down
177 changes: 106 additions & 71 deletions corebc-core/src/types/network.rs
Original file line number Diff line number Diff line change
@@ -1,36 +1,18 @@
use super::{U128, U256, U512, U64};
use serde::{Deserialize, Serialize, Serializer};
use std::{
convert::{TryFrom, TryInto},
fmt,
time::Duration,
};
use strum::{AsRefStr, EnumCount, EnumIter, EnumString, EnumVariantNames};

// compatibility re-export
#[doc(hidden)]
pub use num_enum::{TryFromPrimitive, TryFromPrimitiveError};
#[doc(hidden)]
pub type ParseNetworkError = TryFromPrimitiveError<Network>;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::{convert::TryFrom, fmt, str::FromStr, time::Duration};
use strum::{EnumCount, EnumIter, EnumVariantNames};

#[derive(Debug)]
pub struct ParseNetworkError {
pub number: u64,
}

// When adding a new network:
// 1. add new variant to the Network enum;
// 2. add extra information in the last `impl` block (explorer URLs, block time) when applicable;
// 3. (optional) add aliases:
// - Strum (in kebab-case): `#[strum(to_string = "<main>", serialize = "<aliasX>", ...)]`
// `to_string = "<main>"` must be present and will be used in `Display`, `Serialize`
// and `FromStr`, while `serialize = "<aliasX>"` will be appended to `FromStr`.
// More info: <https://docs.rs/strum/latest/strum/additional_attributes/index.html#attributes-on-variants>
// - Serde (in snake_case): `#[serde(alias = "<aliasX>", ...)]`
// Aliases are appended to the `Deserialize` implementation.
// More info: <https://serde.rs/variant-attrs.html>
// - Add a test at the bottom of the file

// We don't derive Serialize because it is manually implemented using AsRef<str> and it would
// break a lot of things since Serialize is `kebab-case` vs Deserialize `snake_case`.
// This means that the Network type is not "round-trippable", because the Serialize and Deserialize
// implementations do not use the same case style.

Comment on lines -29 to -33
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you find out why is this the case? It doesn't really make sense to me to make the network type not "round-trippable"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure about that, but thats what I found about this
gakonst/ethers-rs#2038 (comment)
gakonst/ethers-rs#2268 (comment)

https://github.com/gakonst/ethers-rs/pull/2270/files
Seems like it was hot-fixed and tests for "round-trippable" were added

/// An Ethereum EIP-155 Network.
#[derive(
Clone,
Expand All @@ -41,30 +23,19 @@ pub type ParseNetworkError = TryFromPrimitiveError<Network>;
PartialOrd,
Ord,
Hash,
AsRefStr, // AsRef<str>, fmt::Display and serde::Serialize
EnumVariantNames, // Network::VARIANTS
EnumString, // FromStr, TryFrom<&str>
EnumIter, // Network::iter
EnumCount, // Network::COUNT
TryFromPrimitive, // TryFrom<u64>
Deserialize,
EnumCount, /* Network::COUNT */
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "kebab-case")]
#[repr(u64)]
pub enum Network {
#[strum(to_string = "mainnet", serialize = "xcblive")]
#[serde(alias = "xcblive")]
Mainnet = 1,
#[strum(to_string = "devin", serialize = "xablive")]
#[serde(alias = "xablive")]
Devin = 3,
Private(u64),
}

// === impl Network ===

// This must be implemented manually so we avoid a conflict with `TryFromPrimitive` where it treats
// the `#[default]` attribute as its own `#[num_enum(default)]`
impl Default for Network {
fn default() -> Self {
Self::Mainnet
Expand All @@ -75,7 +46,11 @@ macro_rules! impl_into_numeric {
($($ty:ty)+) => {$(
impl From<Network> for $ty {
fn from(network: Network) -> Self {
u64::from(network).into()
match network {
Network::Mainnet => (1 as u64).into(),
Network::Devin =>(3 as u64).into(),
Network::Private(n) => (n as u64).into(),
}
}
}
)+};
Expand All @@ -88,7 +63,11 @@ macro_rules! impl_try_from_numeric {
type Error = ParseNetworkError;

fn try_from(value: $native) -> Result<Self, Self::Error> {
(value as u64).try_into()
match value as u64 {
1 => Ok(Network::Mainnet),
3 => Ok(Network::Devin),
n => Ok(Network::Private(n)),
}
}
}
)+
Expand All @@ -99,11 +78,13 @@ macro_rules! impl_try_from_numeric {

fn try_from(value: $primitive) -> Result<Self, Self::Error> {
if value.bits() > 64 {
// `TryFromPrimitiveError` only has a `number` field which has the same type
// as the `#[repr(_)]` attribute on the enum.
return Err(ParseNetworkError { number: value.low_u64() })
}
value.low_u64().try_into()
match value.low_u64() {
1 => Ok(Network::Mainnet),
3 => Ok(Network::Devin),
n => Ok(Network::Private(n)),
}
}
}
)*
Expand All @@ -112,7 +93,11 @@ macro_rules! impl_try_from_numeric {

impl From<Network> for u64 {
fn from(network: Network) -> Self {
network as u64
match network {
Network::Mainnet => 1,
Network::Devin => 3,
Network::Private(n) => n,
}
}
}

Expand All @@ -122,15 +107,31 @@ impl TryFrom<U64> for Network {
type Error = ParseNetworkError;

fn try_from(value: U64) -> Result<Self, Self::Error> {
value.low_u64().try_into()
match value.low_u64() {
1 => Ok(Network::Mainnet),
3 => Ok(Network::Devin),
n => Ok(Network::Private(n)),
}
}
}

impl_try_from_numeric!(u8 u16 u32 usize; U128 U256 U512);
impl TryFrom<&str> for Network {
type Error = ParseNetworkError;

fn try_from(value: &str) -> Result<Self, Self::Error> {
Ok(Network::from(value.to_string()))
}
}

impl_try_from_numeric!(u8 u16 u32 u64 usize; U128 U256 U512);

impl fmt::Display for Network {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.pad(self.as_ref())
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Network::Mainnet => write!(f, "mainnet"),
Network::Devin => write!(f, "devin"),
Network::Private(id) => write!(f, "private-{}", id),
}
}
}

Expand All @@ -139,7 +140,42 @@ impl Serialize for Network {
where
S: Serializer,
{
s.serialize_str(self.as_ref())
s.serialize_str(format!("{}", self).as_str())
}
}

impl<'de> Deserialize<'de> for Network {
fn deserialize<D>(deserializer: D) -> Result<Network, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Ok(Network::from(s))
}
}

impl From<String> for Network {
fn from(s: String) -> Network {
match s.as_str() {
"mainnet" => Network::Mainnet,
"devin" => Network::Devin,
unknown => {
if let ["private", id_str] = unknown.split('-').collect::<Vec<_>>().as_slice() {
if let Ok(id) = id_str.parse::<u64>() {
return Network::Private(id)
}
}
panic!("Unknown network: {}", unknown);
}
}
}
}

impl FromStr for Network {
type Err = ParseNetworkError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Network::from(s.to_string()))
}
}

Expand Down Expand Up @@ -171,7 +207,7 @@ impl Network {
use Network::*;

let ms = match self {
Mainnet | Devin => 7_000,
Mainnet | Devin | Private(_) => 7_000,
};

Some(Duration::from_millis(ms))
Expand All @@ -192,7 +228,7 @@ impl Network {

match self {
// Known EIP-1559 networks
Mainnet | Devin => false,
Mainnet | Devin | Private(_) => false,
}
}

Expand Down Expand Up @@ -220,6 +256,7 @@ impl Network {
let urls = match self {
Mainnet => ("https://blockindex.net/api/v2", "https://blockindex.net"),
Devin => ("https://devin.blockindex.net/api/v2", "https://devin.blockindex.net"),
Private(_) => ("", ""),
};

Some(urls)
Expand All @@ -246,7 +283,6 @@ mod tests {
for network in Network::iter() {
let network_string = network.to_string();
assert_eq!(network_string, format!("{network}"));
assert_eq!(network_string.as_str(), network.as_ref());
assert_eq!(serde_json::to_string(&network).unwrap(), format!("\"{network_string}\""));

assert_eq!(network_string.parse::<Network>().unwrap(), network);
Expand All @@ -257,28 +293,10 @@ mod tests {
fn roundtrip_serde() {
for network in Network::iter() {
let network_string = serde_json::to_string(&network).unwrap();
let network_string = network_string.replace('-', "_");
assert_eq!(serde_json::from_str::<'_, Network>(&network_string).unwrap(), network);
}
}

#[test]
// CORETODO: Needs anvil
fn aliases() {
use Network::*;

// kebab-case
const ALIASES: &[(Network, &[&str])] = &[(Mainnet, &["xcblive"]), (Devin, &["xablive"])];

for &(network, aliases) in ALIASES {
for &alias in aliases {
assert_eq!(alias.parse::<Network>().unwrap(), network);
let s = alias.to_string().replace('-', "_");
assert_eq!(serde_json::from_str::<Network>(&format!("\"{s}\"")).unwrap(), network);
}
}
}

#[test]
fn serde_to_string_match() {
for network in Network::iter() {
Expand All @@ -287,4 +305,21 @@ mod tests {
assert_eq!(network_serde, network_string);
}
}

#[test]
fn u64_to_network() {
let mainnet = Network::try_from(1u64).expect("cannot parse mainnet network_id");
assert_eq!(mainnet, Network::Mainnet);

let devin = Network::try_from(3u64).expect("cannot parse devin network_id");
assert_eq!(devin, Network::Devin);

let private = Network::try_from(6u64).expect("cannot parse private network_id 6");
assert_eq!(private, Network::Private(6));

for network_id in 1..10u64 {
let network = Network::try_from(network_id).expect("cannot parse u64 as network_id");
assert_eq!(u64::from(network), network_id);
}
}
}
6 changes: 3 additions & 3 deletions corebc-core/src/types/signature.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Code adapted from: https://github.com/tomusdrw/rust-web3/blob/master/src/api/accounts.rs
use crate::{
types::{Address, H256, U256},
utils::{hash_message, to_ican, NetworkType},
types::{Address, Network, H256, U256},
utils::{hash_message, to_ican},
};
use elliptic_curve::{consts::U32, sec1::ToEncodedPoint};
use ethabi::ethereum_types::H160;
Expand Down Expand Up @@ -133,7 +133,7 @@ impl Signature {
bytes.copy_from_slice(&hash[12..]);
let addr = H160::from(bytes);
// CORETODO: Change the networktype logic
Ok(to_ican(&addr, &NetworkType::Mainnet))
Ok(to_ican(&addr, &Network::Mainnet))
}

/// Retrieves the recovery signature.
Expand Down