From cc3748f0340a19abf2fbc63ec803d10c3327909a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= <tomusdrw@users.noreply.github.com>
Date: Fri, 11 Jan 2019 11:43:58 +0100
Subject: [PATCH] Hex-encode block number. (#1389)

---
 substrate/core/rpc/src/chain/mod.rs           | 10 +--
 substrate/core/rpc/src/chain/number.rs        | 68 +++++++++++++++++++
 substrate/core/rpc/src/chain/tests.rs         | 12 ++--
 substrate/core/rpc/src/lib.rs                 |  1 +
 .../core/sr-primitives/src/generic/header.rs  | 43 ++++++++++--
 5 files changed, 121 insertions(+), 13 deletions(-)
 create mode 100644 substrate/core/rpc/src/chain/number.rs

diff --git a/substrate/core/rpc/src/chain/mod.rs b/substrate/core/rpc/src/chain/mod.rs
index 7675f42e221..ab6f0d710d1 100644
--- a/substrate/core/rpc/src/chain/mod.rs
+++ b/substrate/core/rpc/src/chain/mod.rs
@@ -33,6 +33,7 @@ use subscriptions::Subscriptions;
 mod error;
 #[cfg(test)]
 mod tests;
+mod number;
 
 use self::error::Result;
 
@@ -56,7 +57,7 @@ build_rpc_trait! {
 		///
 		/// By default returns latest block hash.
 		#[rpc(name = "chain_getBlockHash", alias = ["chain_getHead", ])]
-		fn block_hash(&self, Trailing<Number>) -> Result<Option<Hash>>;
+		fn block_hash(&self, Trailing<number::NumberOrHex<Number>>) -> Result<Option<Hash>>;
 
 		/// Get hash of the last finalised block in the canon chain.
 		#[rpc(name = "chain_getFinalisedHead")]
@@ -172,10 +173,11 @@ impl<B, E, Block, RA> ChainApi<NumberFor<Block>, Block::Hash, Block::Header, Sig
 		Ok(self.client.block(&BlockId::Hash(hash))?)
 	}
 
-	fn block_hash(&self, number: Trailing<NumberFor<Block>>) -> Result<Option<Block::Hash>> {
-		Ok(match number.into() {
+	fn block_hash(&self, number: Trailing<number::NumberOrHex<NumberFor<Block>>>) -> Result<Option<Block::Hash>> {
+		let number: Option<number::NumberOrHex<NumberFor<Block>>> = number.into();
+		Ok(match number {
 			None => Some(self.client.info()?.chain.best_hash),
-			Some(number) => self.client.header(&BlockId::number(number))?.map(|h| h.hash()),
+			Some(num_or_hex) => self.client.header(&BlockId::number(num_or_hex.to_number()?))?.map(|h| h.hash()),
 		})
 	}
 
diff --git a/substrate/core/rpc/src/chain/number.rs b/substrate/core/rpc/src/chain/number.rs
new file mode 100644
index 00000000000..7a300313e31
--- /dev/null
+++ b/substrate/core/rpc/src/chain/number.rs
@@ -0,0 +1,68 @@
+// Copyright 2017-2019 Parity Technologies (UK) Ltd.
+// This file is part of Substrate.
+
+// Substrate is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Substrate is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Substrate.  If not, see <http://www.gnu.org/licenses/>.
+
+use primitives::U256;
+use runtime_primitives::traits;
+
+/// RPC Block number type
+///
+/// We allow two representations of the block number as input.
+/// Either we deserialize to the type that is specified in the block type
+/// or we attempt to parse given hex value.
+/// We do that for consistency with the returned type, default generic header
+/// serializes block number as hex to avoid overflows in JavaScript.
+#[derive(Deserialize)]
+#[serde(untagged)]
+pub enum NumberOrHex<Number> {
+	/// The original header number type of block.
+	Number(Number),
+	/// Hex representation of the block number.
+	Hex(U256),
+}
+
+impl<Number: traits::As<u64>> NumberOrHex<Number> {
+	/// Attempts to convert into concrete block number.
+	///
+	/// Fails in case hex number is too big.
+	pub fn to_number(self) -> Result<Number, String> {
+		match self {
+			NumberOrHex::Number(n) => Ok(n),
+			NumberOrHex::Hex(h) => {
+				// TODO [ToDr] this only supports `u64` since `BlockNumber` is `As<u64>` we could possibly go with `u128`. (#1377)
+				let l = h.low_u64();
+				if U256::from(l) != h {
+					Err(format!("`{}` does not fit into the block number type.", h))
+				} else {
+					Ok(traits::As::sa(l))
+				}
+			},
+		}
+	}
+}
+
+#[cfg(test)]
+impl From<u64> for NumberOrHex<u64> {
+	fn from(n: u64) -> Self {
+		NumberOrHex::Number(n)
+	}
+}
+
+#[cfg(test)]
+impl<Number> From<U256> for NumberOrHex<Number> {
+	fn from(n: U256) -> Self {
+		NumberOrHex::Hex(n)
+	}
+}
diff --git a/substrate/core/rpc/src/chain/tests.rs b/substrate/core/rpc/src/chain/tests.rs
index b3f9ee93e27..5dcd2d0e21c 100644
--- a/substrate/core/rpc/src/chain/tests.rs
+++ b/substrate/core/rpc/src/chain/tests.rs
@@ -129,12 +129,12 @@ fn should_return_block_hash() {
 
 
 	assert_matches!(
-		client.block_hash(Some(0u64).into()),
+		client.block_hash(Some(0u64.into()).into()),
 		Ok(Some(ref x)) if x == &client.client.genesis_hash()
 	);
 
 	assert_matches!(
-		client.block_hash(Some(1u64).into()),
+		client.block_hash(Some(1u64.into()).into()),
 		Ok(None)
 	);
 
@@ -142,11 +142,15 @@ fn should_return_block_hash() {
 	client.client.import(BlockOrigin::Own, block.clone()).unwrap();
 
 	assert_matches!(
-		client.block_hash(Some(0u64).into()),
+		client.block_hash(Some(0u64.into()).into()),
 		Ok(Some(ref x)) if x == &client.client.genesis_hash()
 	);
 	assert_matches!(
-		client.block_hash(Some(1u64).into()),
+		client.block_hash(Some(1u64.into()).into()),
+		Ok(Some(ref x)) if x == &block.hash()
+	);
+	assert_matches!(
+		client.block_hash(Some(::primitives::U256::from(1u64).into()).into()),
 		Ok(Some(ref x)) if x == &block.hash()
 	);
 }
diff --git a/substrate/core/rpc/src/lib.rs b/substrate/core/rpc/src/lib.rs
index 90caaaf291f..91eef709a4e 100644
--- a/substrate/core/rpc/src/lib.rs
+++ b/substrate/core/rpc/src/lib.rs
@@ -38,6 +38,7 @@ extern crate error_chain;
 extern crate jsonrpc_macros;
 #[macro_use]
 extern crate log;
+#[macro_use]
 extern crate serde_derive;
 
 #[cfg(test)]
diff --git a/substrate/core/sr-primitives/src/generic/header.rs b/substrate/core/sr-primitives/src/generic/header.rs
index 3fed9e600dc..863dc5a6a86 100644
--- a/substrate/core/sr-primitives/src/generic/header.rs
+++ b/substrate/core/sr-primitives/src/generic/header.rs
@@ -26,10 +26,11 @@ use generic::Digest;
 #[cfg_attr(feature = "std", derive(Debug, Serialize))]
 #[cfg_attr(feature = "std", serde(rename_all = "camelCase"))]
 #[cfg_attr(feature = "std", serde(deny_unknown_fields))]
-pub struct Header<Number, Hash: HashT, DigestItem> {
+pub struct Header<Number: Copy + Into<u128>, Hash: HashT, DigestItem> {
 	/// The parent hash.
 	pub parent_hash: <Hash as HashT>::Output,
 	/// The block number.
+	#[cfg_attr(feature = "std", serde(serialize_with = "serialize_number"))]
 	pub number: Number,
 	/// The state trie merkle root
 	pub state_root: <Hash as HashT>::Output,
@@ -39,8 +40,17 @@ pub struct Header<Number, Hash: HashT, DigestItem> {
 	pub digest: Digest<DigestItem>,
 }
 
+#[cfg(feature = "std")]
+pub fn serialize_number<S, T: Copy + Into<u128>>(val: &T, s: S) -> Result<S::Ok, S::Error> where S: ::serde::Serializer {
+	use substrate_primitives::uint::U256;
+	let v: u128 = (*val).into();
+	let lower = U256::from(v as u64);
+	let upper = U256::from(v.rotate_left(64) as u64) << 64;
+	::serde::Serialize::serialize(&(upper + lower), s)
+}
+
 impl<Number, Hash, DigestItem> Decode for Header<Number, Hash, DigestItem> where
-	Number: HasCompact,
+	Number: HasCompact + Copy + Into<u128>,
 	Hash: HashT,
 	Hash::Output: Decode,
 	DigestItem: DigestItemT + Decode,
@@ -57,7 +67,7 @@ impl<Number, Hash, DigestItem> Decode for Header<Number, Hash, DigestItem> where
 }
 
 impl<Number, Hash, DigestItem> Encode for Header<Number, Hash, DigestItem> where
-	Number: HasCompact + Copy,
+	Number: HasCompact + Copy + Into<u128>,
 	Hash: HashT,
 	Hash::Output: Encode,
 	DigestItem: DigestItemT + Encode,
@@ -72,7 +82,7 @@ impl<Number, Hash, DigestItem> Encode for Header<Number, Hash, DigestItem> where
 }
 
 impl<Number, Hash, DigestItem> traits::Header for Header<Number, Hash, DigestItem> where
-	Number: Member + MaybeSerializeDebug + ::rstd::hash::Hash + Copy + MaybeDisplay + SimpleArithmetic + Codec,
+	Number: Member + MaybeSerializeDebug + ::rstd::hash::Hash + MaybeDisplay + SimpleArithmetic + Codec + Copy + Into<u128>,
 	Hash: HashT,
 	DigestItem: DigestItemT<Hash = Hash::Output> + Codec,
 	Hash::Output: Default + ::rstd::hash::Hash + Copy + Member + MaybeSerializeDebugButNotDeserialize + MaybeDisplay + SimpleBitOps + Codec,
@@ -116,7 +126,7 @@ impl<Number, Hash, DigestItem> traits::Header for Header<Number, Hash, DigestIte
 }
 
 impl<Number, Hash, DigestItem> Header<Number, Hash, DigestItem> where
-	Number: Member + ::rstd::hash::Hash + Copy + MaybeDisplay + SimpleArithmetic + Codec,
+	Number: Member + ::rstd::hash::Hash + Copy + MaybeDisplay + SimpleArithmetic + Codec + Into<u128>,
 	Hash: HashT,
 	DigestItem: DigestItemT + Codec,
 	Hash::Output: Default + ::rstd::hash::Hash + Copy + Member + MaybeDisplay + SimpleBitOps + Codec,
@@ -127,3 +137,26 @@ impl<Number, Hash, DigestItem> Header<Number, Hash, DigestItem> where
 		Hash::hash_of(self)
 	}
 }
+
+#[cfg(all(test, feature = "std"))]
+mod tests {
+	use super::*;
+
+	#[test]
+	fn should_serialize_numbers() {
+		fn serialize(num: u128) -> String {
+			let mut v = vec![];
+			{
+				let mut ser = ::serde_json::Serializer::new(::std::io::Cursor::new(&mut v));
+				serialize_number(&num, &mut ser).unwrap();
+			}
+			String::from_utf8(v).unwrap()
+		}
+
+		assert_eq!(serialize(0), "\"0x0\"".to_owned());
+		assert_eq!(serialize(1), "\"0x1\"".to_owned());
+		assert_eq!(serialize(u64::max_value() as u128), "\"0xffffffffffffffff\"".to_owned());
+		assert_eq!(serialize(u64::max_value() as u128 + 1), "\"0x10000000000000000\"".to_owned());
+	}
+
+}
-- 
GitLab