Add RPC eth_chainId for querying the current blockchain chain ID (#6329)

* Add RPC eth_chainId for querying the current blockchain chain ID

Currently although we can use `net_version` RPC call to get the
current network ID, there's no RPC for querying the chain ID. This
makes it impossible to determine the current actual blockchain using
the RPC. An ETH/ETC client can accidentally connect to an ETC/ETH RPC
endpoint without knowing it unless it tries to sign a transaction or
it fetch a transaction that is known to have signed with a chain
ID. This has since caused trouble for application developers, such as
MetaMask, to add multi-chain support.

The same RPC endpoint is also about to be merged for ETC's
go-ethereum: https://github.com/ethereumproject/go-ethereum/pull/336

* Add eth_chainId to js's web3 interface

* Add a mocked test for eth_chainId

* Add chainId in js's jsonrpc interfaces

* Change return type for eth_chainId to `Option<u64>`

* Change name eth_chainId to parity_chainId

* Wrong test name and missed var for rpc_parity_chain_id test

* Use U256 to return chainId and fix for master

u64 returns decimal integer, and there seems to be no type called
U64. So here I use U256 to return the hex integer.

* Fix chainID test

Before EIP155 fork number, chainID should be null.

* Change both parity_chainId and transaction::chainId to use U64

This makes it consistent that all chain ids returned are hex string.

* Fix wrong U64 serialization
This commit is contained in:
Wei Tang 2017-09-26 20:17:07 +08:00 committed by Gav Wood
parent 59d946bf00
commit d8af9f4e7b
9 changed files with 68 additions and 14 deletions

View File

@ -53,6 +53,11 @@ export default class Parity {
); );
} }
chainId () {
return this._transport
.execute('parity_chainId');
}
chainStatus () { chainStatus () {
return this._transport return this._transport
.execute('parity_chainStatus') .execute('parity_chainStatus')

View File

@ -56,6 +56,16 @@ export default {
} }
}, },
chainId: {
desc: 'Returns the current chain ID used for tranaction signing.',
params: [],
returns: {
type: Quantity,
desc: 'The current blockchain chain ID',
example: '0x1'
}
},
chainStatus: { chainStatus: {
section: SECTION_NET, section: SECTION_NET,
desc: 'Returns the information on warp sync blocks', desc: 'Returns the information on warp sync blocks',

View File

@ -39,7 +39,7 @@ use v1::helpers::light_fetch::LightFetch;
use v1::metadata::Metadata; use v1::metadata::Metadata;
use v1::traits::Parity; use v1::traits::Parity;
use v1::types::{ use v1::types::{
Bytes, U256, H160, H256, H512, CallRequest, Bytes, U256, U64, H160, H256, H512, CallRequest,
Peers, Transaction, RpcSettings, Histogram, Peers, Transaction, RpcSettings, Histogram,
TransactionStats, LocalTransactionStatus, TransactionStats, LocalTransactionStatus,
BlockNumber, ConsensusCapability, VersionInfo, BlockNumber, ConsensusCapability, VersionInfo,
@ -50,6 +50,7 @@ use Host;
/// Parity implementation for light client. /// Parity implementation for light client.
pub struct ParityClient { pub struct ParityClient {
client: Arc<LightChainClient>,
light_dispatch: Arc<LightDispatcher>, light_dispatch: Arc<LightDispatcher>,
accounts: Arc<AccountProvider>, accounts: Arc<AccountProvider>,
logger: Arc<RotatingLogger>, logger: Arc<RotatingLogger>,
@ -84,6 +85,7 @@ impl ParityClient {
dapps_address, dapps_address,
ws_address, ws_address,
eip86_transition: client.eip86_transition(), eip86_transition: client.eip86_transition(),
client: client,
} }
} }
@ -321,6 +323,10 @@ impl Parity for ParityClient {
Err(errors::light_unimplemented(None)) Err(errors::light_unimplemented(None))
} }
fn chain_id(&self) -> Result<Option<U64>, Error> {
Ok(self.client.signing_chain_id().map(U64::from))
}
fn chain(&self) -> Result<String, Error> { fn chain(&self) -> Result<String, Error> {
Ok(self.settings.chain.clone()) Ok(self.settings.chain.clone())
} }

View File

@ -44,7 +44,7 @@ use v1::helpers::accounts::unwrap_provider;
use v1::metadata::Metadata; use v1::metadata::Metadata;
use v1::traits::Parity; use v1::traits::Parity;
use v1::types::{ use v1::types::{
Bytes, U256, H160, H256, H512, CallRequest, Bytes, U256, U64, H160, H256, H512, CallRequest,
Peers, Transaction, RpcSettings, Histogram, Peers, Transaction, RpcSettings, Histogram,
TransactionStats, LocalTransactionStatus, TransactionStats, LocalTransactionStatus,
BlockNumber, ConsensusCapability, VersionInfo, BlockNumber, ConsensusCapability, VersionInfo,
@ -201,6 +201,10 @@ impl<C, M, U> Parity for ParityClient<C, M, U> where
Ok(self.settings.chain.clone()) Ok(self.settings.chain.clone())
} }
fn chain_id(&self) -> Result<Option<U64>, Error> {
Ok(self.client.signing_chain_id().map(U64::from))
}
fn chain(&self) -> Result<String, Error> { fn chain(&self) -> Result<String, Error> {
Ok(self.client.spec_name()) Ok(self.client.spec_name())
} }

View File

@ -221,6 +221,17 @@ fn rpc_parity_extra_data() {
assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
} }
#[test]
fn rpc_parity_chain_id() {
let deps = Dependencies::new();
let io = deps.default_client();
let request = r#"{"jsonrpc": "2.0", "method": "parity_chainId", "params": [], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
#[test] #[test]
fn rpc_parity_default_extra_data() { fn rpc_parity_default_extra_data() {
use util::misc; use util::misc;

View File

@ -24,7 +24,7 @@ use futures::BoxFuture;
use node_health::Health; use node_health::Health;
use v1::types::{ use v1::types::{
H160, H256, H512, U256, Bytes, CallRequest, H160, H256, H512, U256, U64, Bytes, CallRequest,
Peers, Transaction, RpcSettings, Histogram, Peers, Transaction, RpcSettings, Histogram,
TransactionStats, LocalTransactionStatus, TransactionStats, LocalTransactionStatus,
BlockNumber, ConsensusCapability, VersionInfo, BlockNumber, ConsensusCapability, VersionInfo,
@ -172,6 +172,12 @@ build_rpc_trait! {
#[rpc(name = "parity_mode")] #[rpc(name = "parity_mode")]
fn mode(&self) -> Result<String, Error>; fn mode(&self) -> Result<String, Error>;
/// Returns the chain ID used for transaction signing at the
/// current best block. An empty string is returned if not
/// available.
#[rpc(name = "parity_chainId")]
fn chain_id(&self) -> Result<Option<U64>, Error>;
/// Get the chain name. Returns one of: "foundation", "kovan", &c. of a filename. /// Get the chain name. Returns one of: "foundation", "kovan", &c. of a filename.
#[rpc(name = "parity_chain")] #[rpc(name = "parity_chain")]
fn chain(&self) -> Result<String, Error>; fn chain(&self) -> Result<String, Error>;

View File

@ -76,7 +76,7 @@ pub use self::trace_filter::TraceFilter;
pub use self::transaction::{Transaction, RichRawTransaction, LocalTransactionStatus}; pub use self::transaction::{Transaction, RichRawTransaction, LocalTransactionStatus};
pub use self::transaction_request::TransactionRequest; pub use self::transaction_request::TransactionRequest;
pub use self::transaction_condition::TransactionCondition; pub use self::transaction_condition::TransactionCondition;
pub use self::uint::{U128, U256}; pub use self::uint::{U128, U256, U64};
pub use self::work::Work; pub use self::work::Work;
// TODO [ToDr] Refactor to a proper type Vec of enums? // TODO [ToDr] Refactor to a proper type Vec of enums?

View File

@ -20,7 +20,7 @@ use ethcore::miner;
use ethcore::{contract_address, CreateContractAddress}; use ethcore::{contract_address, CreateContractAddress};
use ethcore::transaction::{LocalizedTransaction, Action, PendingTransaction, SignedTransaction}; use ethcore::transaction::{LocalizedTransaction, Action, PendingTransaction, SignedTransaction};
use v1::helpers::errors; use v1::helpers::errors;
use v1::types::{Bytes, H160, H256, U256, H512, TransactionCondition}; use v1::types::{Bytes, H160, H256, U256, H512, U64, TransactionCondition};
/// Transaction /// Transaction
#[derive(Debug, Default, Clone, PartialEq, Serialize)] #[derive(Debug, Default, Clone, PartialEq, Serialize)]
@ -60,7 +60,7 @@ pub struct Transaction {
pub public_key: Option<H512>, pub public_key: Option<H512>,
/// The network id of the transaction, if any. /// The network id of the transaction, if any.
#[serde(rename="chainId")] #[serde(rename="chainId")]
pub chain_id: Option<u64>, pub chain_id: Option<U64>,
/// The standardised V field of the signature (0 or 1). /// The standardised V field of the signature (0 or 1).
#[serde(rename="standardV")] #[serde(rename="standardV")]
pub standard_v: U256, pub standard_v: U256,
@ -196,7 +196,7 @@ impl Transaction {
}, },
raw: ::rlp::encode(&t.signed).into_vec().into(), raw: ::rlp::encode(&t.signed).into_vec().into(),
public_key: t.recover_public().ok().map(Into::into), public_key: t.recover_public().ok().map(Into::into),
chain_id: t.chain_id(), chain_id: t.chain_id().map(U64::from),
standard_v: t.standard_v().into(), standard_v: t.standard_v().into(),
v: t.original_v().into(), v: t.original_v().into(),
r: signature.r().into(), r: signature.r().into(),
@ -230,7 +230,7 @@ impl Transaction {
}, },
raw: ::rlp::encode(&t).into_vec().into(), raw: ::rlp::encode(&t).into_vec().into(),
public_key: t.public_key().map(Into::into), public_key: t.public_key().map(Into::into),
chain_id: t.chain_id(), chain_id: t.chain_id().map(U64::from),
standard_v: t.standard_v().into(), standard_v: t.standard_v().into(),
v: t.original_v().into(), v: t.original_v().into(),
r: signature.r().into(), r: signature.r().into(),

View File

@ -59,12 +59,6 @@ macro_rules! impl_uint {
} }
} }
impl serde::Serialize for $name {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
serializer.serialize_str(&format!("0x{}", self.0.to_hex()))
}
}
impl<'a> serde::Deserialize<'a> for $name { impl<'a> serde::Deserialize<'a> for $name {
fn deserialize<D>(deserializer: D) -> Result<$name, D::Error> fn deserialize<D>(deserializer: D) -> Result<$name, D::Error>
where D: serde::Deserializer<'a> { where D: serde::Deserializer<'a> {
@ -104,7 +98,25 @@ macro_rules! impl_uint {
impl_uint!(U128, EthU128, 2); impl_uint!(U128, EthU128, 2);
impl_uint!(U256, EthU256, 4); impl_uint!(U256, EthU256, 4);
impl_uint!(U64, u64, 1);
impl serde::Serialize for U128 {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
serializer.serialize_str(&format!("0x{}", self.0.to_hex()))
}
}
impl serde::Serialize for U256 {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
serializer.serialize_str(&format!("0x{}", self.0.to_hex()))
}
}
impl serde::Serialize for U64 {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
serializer.serialize_str(&format!("0x{:x}", self.0))
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {