RPC for confirming with token
This commit is contained in:
		
							parent
							
								
									6397556cbb
								
							
						
					
					
						commit
						c028f106b1
					
				@ -401,6 +401,28 @@ impl AccountProvider {
 | 
			
		||||
		Ok((signature, new_token))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Decrypts a message with given token. Returns a token to use in next operation for this account.
 | 
			
		||||
	pub fn decrypt_with_token(&self, account: Address, token: AccountToken, shared_mac: &[u8], message: &[u8])
 | 
			
		||||
		-> Result<(Vec<u8>, AccountToken), Error>
 | 
			
		||||
	{
 | 
			
		||||
		let is_std_password = try!(self.sstore.test_password(&account, &token));
 | 
			
		||||
 | 
			
		||||
		let new_token = random_string(16);
 | 
			
		||||
		let message = if is_std_password {
 | 
			
		||||
			// Insert to transient store
 | 
			
		||||
			try!(self.sstore.copy_account(&self.transient_sstore, &account, &token, &new_token));
 | 
			
		||||
			// decrypt
 | 
			
		||||
			try!(self.sstore.decrypt(&account, &token, shared_mac, message))
 | 
			
		||||
		} else {
 | 
			
		||||
			// check transient store
 | 
			
		||||
			try!(self.transient_sstore.change_password(&account, &token, &new_token));
 | 
			
		||||
			// and decrypt
 | 
			
		||||
			try!(self.transient_sstore.decrypt(&account, &token, shared_mac, message))
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		Ok((message, new_token))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Decrypts a message. If password is not provided the account must be unlocked.
 | 
			
		||||
	pub fn decrypt(&self, account: Address, password: Option<String>, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error> {
 | 
			
		||||
		let password = try!(password.map(Ok).unwrap_or_else(|| self.password(&account)));
 | 
			
		||||
@ -477,8 +499,8 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
	#[test]
 | 
			
		||||
	fn should_sign_and_return_token() {
 | 
			
		||||
	let kp = Random.generate().unwrap();
 | 
			
		||||
		// given
 | 
			
		||||
		let kp = Random.generate().unwrap();
 | 
			
		||||
		let ap = AccountProvider::transient_provider();
 | 
			
		||||
		assert!(ap.insert_account(kp.secret().clone(), "test").is_ok());
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -14,6 +14,7 @@
 | 
			
		||||
// You should have received a copy of the GNU General Public License
 | 
			
		||||
// along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 | 
			
		||||
use std::fmt::Debug;
 | 
			
		||||
use rlp;
 | 
			
		||||
use util::{Address, H256, U256, Uint, Bytes};
 | 
			
		||||
use util::bytes::ToPretty;
 | 
			
		||||
@ -37,46 +38,101 @@ use v1::types::{
 | 
			
		||||
 | 
			
		||||
pub const DEFAULT_MAC: [u8; 2] = [0, 0];
 | 
			
		||||
 | 
			
		||||
pub fn execute<C, M>(client: &C, miner: &M, accounts: &AccountProvider, payload: ConfirmationPayload, pass: Option<String>) -> Result<ConfirmationResponse, Error>
 | 
			
		||||
type AccountToken = String;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, PartialEq)]
 | 
			
		||||
pub enum SignWith {
 | 
			
		||||
	Nothing,
 | 
			
		||||
	Password(String),
 | 
			
		||||
	Token(AccountToken),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub enum WithToken<T: Debug> {
 | 
			
		||||
	No(T),
 | 
			
		||||
	Yes(T, AccountToken),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Debug> WithToken<T> {
 | 
			
		||||
	pub fn map<S, F>(self, f: F) -> WithToken<S> where
 | 
			
		||||
		S: Debug,
 | 
			
		||||
		F: FnOnce(T) -> S,
 | 
			
		||||
	{
 | 
			
		||||
		match self {
 | 
			
		||||
			WithToken::No(v) => WithToken::No(f(v)),
 | 
			
		||||
			WithToken::Yes(v, token) => WithToken::Yes(f(v), token),
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pub fn into_value(self) -> T {
 | 
			
		||||
		match self {
 | 
			
		||||
			WithToken::No(v) => v,
 | 
			
		||||
			WithToken::Yes(v, ..) => v,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: Debug> From<(T, AccountToken)> for WithToken<T> {
 | 
			
		||||
	fn from(tuple: (T, AccountToken)) -> Self {
 | 
			
		||||
		WithToken::Yes(tuple.0, tuple.1)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn execute<C, M>(client: &C, miner: &M, accounts: &AccountProvider, payload: ConfirmationPayload, pass: SignWith) -> Result<WithToken<ConfirmationResponse>, Error>
 | 
			
		||||
	where C: MiningBlockChainClient, M: MinerService
 | 
			
		||||
{
 | 
			
		||||
	match payload {
 | 
			
		||||
		ConfirmationPayload::SendTransaction(request) => {
 | 
			
		||||
			sign_and_dispatch(client, miner, accounts, request, pass)
 | 
			
		||||
				.map(RpcH256::from)
 | 
			
		||||
				.map(ConfirmationResponse::SendTransaction)
 | 
			
		||||
				.map(|result| result
 | 
			
		||||
					.map(RpcH256::from)
 | 
			
		||||
					.map(ConfirmationResponse::SendTransaction)
 | 
			
		||||
				)
 | 
			
		||||
		},
 | 
			
		||||
		ConfirmationPayload::SignTransaction(request) => {
 | 
			
		||||
			sign_no_dispatch(client, miner, accounts, request, pass)
 | 
			
		||||
				.map(RpcRichRawTransaction::from)
 | 
			
		||||
				.map(ConfirmationResponse::SignTransaction)
 | 
			
		||||
				.map(|result| result
 | 
			
		||||
					.map(RpcRichRawTransaction::from)
 | 
			
		||||
					.map(ConfirmationResponse::SignTransaction)
 | 
			
		||||
				)
 | 
			
		||||
		},
 | 
			
		||||
		ConfirmationPayload::Signature(address, hash) => {
 | 
			
		||||
			signature(accounts, address, hash, pass)
 | 
			
		||||
				.map(RpcH520::from)
 | 
			
		||||
				.map(ConfirmationResponse::Signature)
 | 
			
		||||
				.map(|result| result
 | 
			
		||||
					.map(RpcH520::from)
 | 
			
		||||
					.map(ConfirmationResponse::Signature)
 | 
			
		||||
				)
 | 
			
		||||
		},
 | 
			
		||||
		ConfirmationPayload::Decrypt(address, data) => {
 | 
			
		||||
			decrypt(accounts, address, data, pass)
 | 
			
		||||
				.map(RpcBytes)
 | 
			
		||||
				.map(ConfirmationResponse::Decrypt)
 | 
			
		||||
				.map(|result| result
 | 
			
		||||
					.map(RpcBytes)
 | 
			
		||||
					.map(ConfirmationResponse::Decrypt)
 | 
			
		||||
				)
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn signature(accounts: &AccountProvider, address: Address, hash: H256, password: Option<String>) -> Result<Signature, Error> {
 | 
			
		||||
	accounts.sign(address, password.clone(), hash).map_err(|e| match password {
 | 
			
		||||
		Some(_) => errors::from_password_error(e),
 | 
			
		||||
		None => errors::from_signing_error(e),
 | 
			
		||||
fn signature(accounts: &AccountProvider, address: Address, hash: H256, password: SignWith) -> Result<WithToken<Signature>, Error> {
 | 
			
		||||
	match password.clone() {
 | 
			
		||||
		SignWith::Nothing => accounts.sign(address, None, hash).map(WithToken::No),
 | 
			
		||||
		SignWith::Password(pass) => accounts.sign(address, Some(pass), hash).map(WithToken::No),
 | 
			
		||||
		SignWith::Token(token) => accounts.sign_with_token(address, token, hash).map(Into::into),
 | 
			
		||||
	}.map_err(|e| match password {
 | 
			
		||||
		SignWith::Nothing => errors::from_signing_error(e),
 | 
			
		||||
		_ => errors::from_password_error(e),
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn decrypt(accounts: &AccountProvider, address: Address, msg: Bytes, password: Option<String>) -> Result<Bytes, Error> {
 | 
			
		||||
	accounts.decrypt(address, password.clone(), &DEFAULT_MAC, &msg)
 | 
			
		||||
		.map_err(|e| match password {
 | 
			
		||||
			Some(_) => errors::from_password_error(e),
 | 
			
		||||
			None => errors::from_signing_error(e),
 | 
			
		||||
		})
 | 
			
		||||
fn decrypt(accounts: &AccountProvider, address: Address, msg: Bytes, password: SignWith) -> Result<WithToken<Bytes>, Error> {
 | 
			
		||||
	match password.clone() {
 | 
			
		||||
		SignWith::Nothing => accounts.decrypt(address, None, &DEFAULT_MAC, &msg).map(WithToken::No),
 | 
			
		||||
		SignWith::Password(pass) => accounts.decrypt(address, Some(pass), &DEFAULT_MAC, &msg).map(WithToken::No),
 | 
			
		||||
		SignWith::Token(token) => accounts.decrypt_with_token(address, token, &DEFAULT_MAC, &msg).map(Into::into),
 | 
			
		||||
	}.map_err(|e| match password {
 | 
			
		||||
		SignWith::Nothing => errors::from_signing_error(e),
 | 
			
		||||
		_ => errors::from_password_error(e),
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn dispatch_transaction<C, M>(client: &C, miner: &M, signed_transaction: SignedTransaction) -> Result<H256, Error>
 | 
			
		||||
@ -88,7 +144,7 @@ pub fn dispatch_transaction<C, M>(client: &C, miner: &M, signed_transaction: Sig
 | 
			
		||||
		.map(|_| hash)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn sign_no_dispatch<C, M>(client: &C, miner: &M, accounts: &AccountProvider, filled: FilledTransactionRequest, password: Option<String>) -> Result<SignedTransaction, Error>
 | 
			
		||||
pub fn sign_no_dispatch<C, M>(client: &C, miner: &M, accounts: &AccountProvider, filled: FilledTransactionRequest, password: SignWith) -> Result<WithToken<SignedTransaction>, Error>
 | 
			
		||||
	where C: MiningBlockChainClient, M: MinerService {
 | 
			
		||||
 | 
			
		||||
	let network_id = client.signing_network_id();
 | 
			
		||||
@ -110,20 +166,32 @@ pub fn sign_no_dispatch<C, M>(client: &C, miner: &M, accounts: &AccountProvider,
 | 
			
		||||
 | 
			
		||||
		let hash = t.hash(network_id);
 | 
			
		||||
		let signature = try!(signature(accounts, address, hash, password));
 | 
			
		||||
		t.with_signature(signature, network_id)
 | 
			
		||||
		signature.map(|sig| {
 | 
			
		||||
			t.with_signature(sig, network_id)
 | 
			
		||||
		})
 | 
			
		||||
	};
 | 
			
		||||
	Ok(signed_transaction)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn sign_and_dispatch<C, M>(client: &C, miner: &M, accounts: &AccountProvider, filled: FilledTransactionRequest, password: Option<String>) -> Result<H256, Error>
 | 
			
		||||
pub fn sign_and_dispatch<C, M>(client: &C, miner: &M, accounts: &AccountProvider, filled: FilledTransactionRequest, password: SignWith) -> Result<WithToken<H256>, Error>
 | 
			
		||||
	where C: MiningBlockChainClient, M: MinerService
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
	let network_id = client.signing_network_id();
 | 
			
		||||
	let signed_transaction = try!(sign_no_dispatch(client, miner, accounts, filled, password));
 | 
			
		||||
 | 
			
		||||
	let (signed_transaction, token) = match signed_transaction {
 | 
			
		||||
		WithToken::No(signed_transaction) => (signed_transaction, None),
 | 
			
		||||
		WithToken::Yes(signed_transaction, token) => (signed_transaction, Some(token)),
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	trace!(target: "miner", "send_transaction: dispatching tx: {} for network ID {:?}", rlp::encode(&signed_transaction).to_vec().pretty(), network_id);
 | 
			
		||||
	dispatch_transaction(&*client, &*miner, signed_transaction)
 | 
			
		||||
	dispatch_transaction(&*client, &*miner, signed_transaction).map(|hash| {
 | 
			
		||||
		match token {
 | 
			
		||||
			Some(ref token) => WithToken::Yes(hash, token.clone()),
 | 
			
		||||
			None => WithToken::No(hash),
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn fill_optional_fields<C, M>(request: TransactionRequest, client: &C, miner: &M) -> FilledTransactionRequest
 | 
			
		||||
 | 
			
		||||
@ -114,7 +114,7 @@ impl<C: 'static, M: 'static> Personal for PersonalClient<C, M> where C: MiningBl
 | 
			
		||||
			&*miner,
 | 
			
		||||
			&*accounts,
 | 
			
		||||
			request,
 | 
			
		||||
			Some(password)
 | 
			
		||||
		).map(Into::into)
 | 
			
		||||
			dispatch::SignWith::Password(password)
 | 
			
		||||
		).map(|v| v.into_value().into())
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -26,7 +26,7 @@ use ethcore::miner::MinerService;
 | 
			
		||||
 | 
			
		||||
use jsonrpc_core::Error;
 | 
			
		||||
use v1::traits::Signer;
 | 
			
		||||
use v1::types::{TransactionModification, ConfirmationRequest, ConfirmationResponse, U256, Bytes};
 | 
			
		||||
use v1::types::{TransactionModification, ConfirmationRequest, ConfirmationResponse, ConfirmationResponseWithToken, U256, Bytes};
 | 
			
		||||
use v1::helpers::{errors, SignerService, SigningQueue, ConfirmationPayload};
 | 
			
		||||
use v1::helpers::dispatch::{self, dispatch_transaction};
 | 
			
		||||
 | 
			
		||||
@ -60,6 +60,35 @@ impl<C: 'static, M: 'static> SignerClient<C, M> where C: MiningBlockChainClient,
 | 
			
		||||
		take_weak!(self.client).keep_alive();
 | 
			
		||||
		Ok(())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fn confirm_internal<F>(&self, id: U256, modification: TransactionModification, f: F) -> Result<ConfirmationResponse, Error> where
 | 
			
		||||
		F: FnOnce(&C, &M, &AccountProvider, ConfirmationPayload) -> Result<ConfirmationResponse, Error>,
 | 
			
		||||
	{
 | 
			
		||||
		try!(self.active());
 | 
			
		||||
 | 
			
		||||
		let id = id.into();
 | 
			
		||||
		let accounts = take_weak!(self.accounts);
 | 
			
		||||
		let signer = take_weak!(self.signer);
 | 
			
		||||
		let client = take_weak!(self.client);
 | 
			
		||||
		let miner = take_weak!(self.miner);
 | 
			
		||||
 | 
			
		||||
		signer.peek(&id).map(|confirmation| {
 | 
			
		||||
			let mut payload = confirmation.payload.clone();
 | 
			
		||||
			// Modify payload
 | 
			
		||||
			match (&mut payload, modification.gas_price) {
 | 
			
		||||
				(&mut ConfirmationPayload::SendTransaction(ref mut request), Some(gas_price)) => {
 | 
			
		||||
					request.gas_price = gas_price.into();
 | 
			
		||||
				},
 | 
			
		||||
				_ => {},
 | 
			
		||||
			}
 | 
			
		||||
			let result = f(&*client, &*miner, &*accounts, payload);
 | 
			
		||||
			// Execute
 | 
			
		||||
			if let Ok(ref response) = result {
 | 
			
		||||
				signer.request_confirmed(id, Ok(response.clone()));
 | 
			
		||||
			}
 | 
			
		||||
			result
 | 
			
		||||
		}).unwrap_or_else(|| Err(errors::invalid_params("Unknown RequestID", id)))
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<C: 'static, M: 'static> Signer for SignerClient<C, M> where C: MiningBlockChainClient, M: MinerService {
 | 
			
		||||
@ -78,30 +107,14 @@ impl<C: 'static, M: 'static> Signer for SignerClient<C, M> where C: MiningBlockC
 | 
			
		||||
	// TODO [ToDr] TransactionModification is redundant for some calls
 | 
			
		||||
	// might be better to replace it in future
 | 
			
		||||
	fn confirm_request(&self, id: U256, modification: TransactionModification, pass: String) -> Result<ConfirmationResponse, Error> {
 | 
			
		||||
		try!(self.active());
 | 
			
		||||
		self.confirm_internal(id, modification, move |client, miner, accounts, payload| {
 | 
			
		||||
			dispatch::execute(client, miner, accounts, payload, dispatch::SignWith::Password(pass))
 | 
			
		||||
				.map(|v| v.into_value())
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
		let id = id.into();
 | 
			
		||||
		let accounts = take_weak!(self.accounts);
 | 
			
		||||
		let signer = take_weak!(self.signer);
 | 
			
		||||
		let client = take_weak!(self.client);
 | 
			
		||||
		let miner = take_weak!(self.miner);
 | 
			
		||||
 | 
			
		||||
		signer.peek(&id).map(|confirmation| {
 | 
			
		||||
			let mut payload = confirmation.payload.clone();
 | 
			
		||||
			// Modify payload
 | 
			
		||||
			match (&mut payload, modification.gas_price) {
 | 
			
		||||
				(&mut ConfirmationPayload::SendTransaction(ref mut request), Some(gas_price)) => {
 | 
			
		||||
					request.gas_price = gas_price.into();
 | 
			
		||||
				},
 | 
			
		||||
				_ => {},
 | 
			
		||||
			}
 | 
			
		||||
			// Execute
 | 
			
		||||
			let result = dispatch::execute(&*client, &*miner, &*accounts, payload, Some(pass));
 | 
			
		||||
			if let Ok(ref response) = result {
 | 
			
		||||
				signer.request_confirmed(id, Ok(response.clone()));
 | 
			
		||||
			}
 | 
			
		||||
			result
 | 
			
		||||
		}).unwrap_or_else(|| Err(errors::invalid_params("Unknown RequestID", id)))
 | 
			
		||||
	fn confirm_request_with_token(&self, id: U256, modification: TransactionModification, token: String) -> Result<ConfirmationResponseWithToken, Error> {
 | 
			
		||||
		unimplemented!()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fn confirm_request_raw(&self, id: U256, bytes: Bytes) -> Result<ConfirmationResponse, Error> {
 | 
			
		||||
 | 
			
		||||
@ -99,7 +99,9 @@ impl<C, M> SigningQueueClient<C, M> where
 | 
			
		||||
 | 
			
		||||
		let sender = payload.sender();
 | 
			
		||||
		if accounts.is_unlocked(sender) {
 | 
			
		||||
			return dispatch::execute(&*client, &*miner, &*accounts, payload, None).map(DispatchResult::Value);
 | 
			
		||||
			return dispatch::execute(&*client, &*miner, &*accounts, payload, dispatch::SignWith::Nothing)
 | 
			
		||||
				.map(|v| v.into_value())
 | 
			
		||||
				.map(DispatchResult::Value);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		take_weak!(self.signer).add_request(payload)
 | 
			
		||||
 | 
			
		||||
@ -75,7 +75,8 @@ impl<C, M> SigningUnsafeClient<C, M> where
 | 
			
		||||
		let accounts = take_weak!(self.accounts);
 | 
			
		||||
 | 
			
		||||
		let payload = dispatch::from_rpc(payload, &*client, &*miner);
 | 
			
		||||
		dispatch::execute(&*client, &*miner, &*accounts, payload, None)
 | 
			
		||||
		dispatch::execute(&*client, &*miner, &*accounts, payload, dispatch::SignWith::Nothing)
 | 
			
		||||
			.map(|v| v.into_value())
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -209,6 +209,52 @@ fn should_confirm_transaction_and_dispatch() {
 | 
			
		||||
	assert_eq!(tester.miner.imported_transactions.lock().len(), 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn should_confirm_transaction_with_token() {
 | 
			
		||||
	// given
 | 
			
		||||
	let tester = signer_tester();
 | 
			
		||||
	let address = tester.accounts.new_account("test").unwrap();
 | 
			
		||||
	let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap();
 | 
			
		||||
	tester.signer.add_request(ConfirmationPayload::SendTransaction(FilledTransactionRequest {
 | 
			
		||||
		from: address,
 | 
			
		||||
		to: Some(recipient),
 | 
			
		||||
		gas_price: U256::from(10_000),
 | 
			
		||||
		gas: U256::from(10_000_000),
 | 
			
		||||
		value: U256::from(1),
 | 
			
		||||
		data: vec![],
 | 
			
		||||
		nonce: None,
 | 
			
		||||
	})).unwrap();
 | 
			
		||||
 | 
			
		||||
	let t = Transaction {
 | 
			
		||||
		nonce: U256::zero(),
 | 
			
		||||
		gas_price: U256::from(0x1000),
 | 
			
		||||
		gas: U256::from(10_000_000),
 | 
			
		||||
		action: Action::Call(recipient),
 | 
			
		||||
		value: U256::from(0x1),
 | 
			
		||||
		data: vec![]
 | 
			
		||||
	};
 | 
			
		||||
	let (signature, token) = tester.accounts.sign_with_token(address, "test".into(), t.hash(None)).unwrap();
 | 
			
		||||
	let t = t.with_signature(signature, None);
 | 
			
		||||
 | 
			
		||||
	assert_eq!(tester.signer.requests().len(), 1);
 | 
			
		||||
 | 
			
		||||
	// when
 | 
			
		||||
	let request = r#"{
 | 
			
		||||
		"jsonrpc":"2.0",
 | 
			
		||||
		"method":"signer_confirmRequestWithToken",
 | 
			
		||||
		"params":["0x1", {"gasPrice":"0x1000"}, ""#.to_owned() + &token + r#""],
 | 
			
		||||
		"id":1
 | 
			
		||||
	}"#;
 | 
			
		||||
	let response = r#"{"jsonrpc":"2.0","result":{"result":""#.to_owned() +
 | 
			
		||||
		format!("0x{:?}", t.hash()).as_ref() +
 | 
			
		||||
		r#""token":""},"id":1}"#;
 | 
			
		||||
 | 
			
		||||
	// then
 | 
			
		||||
	assert_eq!(tester.io.handle_request_sync(&request), Some(response.to_owned()));
 | 
			
		||||
	assert_eq!(tester.signer.requests().len(), 0);
 | 
			
		||||
	assert_eq!(tester.miner.imported_transactions.lock().len(), 1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn should_confirm_transaction_with_rlp() {
 | 
			
		||||
	// given
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,7 @@
 | 
			
		||||
use jsonrpc_core::Error;
 | 
			
		||||
 | 
			
		||||
use v1::helpers::auto_args::Wrap;
 | 
			
		||||
use v1::types::{U256, Bytes, TransactionModification, ConfirmationRequest, ConfirmationResponse};
 | 
			
		||||
use v1::types::{U256, Bytes, TransactionModification, ConfirmationRequest, ConfirmationResponse, ConfirmationResponseWithToken};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
build_rpc_trait! {
 | 
			
		||||
@ -33,6 +33,10 @@ build_rpc_trait! {
 | 
			
		||||
		#[rpc(name = "signer_confirmRequest")]
 | 
			
		||||
		fn confirm_request(&self, U256, TransactionModification, String) -> Result<ConfirmationResponse, Error>;
 | 
			
		||||
 | 
			
		||||
		/// Confirm specific request with token.
 | 
			
		||||
		#[rpc(name = "signer_confirmRequestWithToken")]
 | 
			
		||||
		fn confirm_request_with_token(&self, U256, TransactionModification, String) -> Result<ConfirmationResponseWithToken, Error>;
 | 
			
		||||
 | 
			
		||||
		/// Confirm specific request with already signed data.
 | 
			
		||||
		#[rpc(name = "signer_confirmRequestRaw")]
 | 
			
		||||
		fn confirm_request_raw(&self, U256, Bytes) -> Result<ConfirmationResponse, Error>;
 | 
			
		||||
 | 
			
		||||
@ -101,6 +101,15 @@ impl Serialize for ConfirmationResponse {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Confirmation response with additional token for further requests
 | 
			
		||||
#[derive(Debug, Clone, PartialEq, Serialize)]
 | 
			
		||||
pub struct ConfirmationResponseWithToken {
 | 
			
		||||
	/// Actual response
 | 
			
		||||
	pub result: ConfirmationResponse,
 | 
			
		||||
	/// New token
 | 
			
		||||
	pub token: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Confirmation payload, i.e. the thing to be confirmed
 | 
			
		||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize)]
 | 
			
		||||
pub enum ConfirmationPayload {
 | 
			
		||||
@ -247,5 +256,21 @@ mod tests {
 | 
			
		||||
			gas_price: None,
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	#[test]
 | 
			
		||||
	fn should_serialize_confirmation_response_with_token() {
 | 
			
		||||
		// given
 | 
			
		||||
		let response = ConfirmationResponseWithToken {
 | 
			
		||||
			result: ConfirmationResponse::SendTransaction(H256::default()),
 | 
			
		||||
			token: "test-token".into(),
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		// when
 | 
			
		||||
		let res = serde_json::to_string(&response);
 | 
			
		||||
		let expected = r#"{"result":"0x0000000000000000000000000000000000000000","token":"test-token"}"#;
 | 
			
		||||
 | 
			
		||||
		// then
 | 
			
		||||
		assert_eq!(res.unwrap(), expected.to_owned());
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -38,7 +38,9 @@ pub use self::bytes::Bytes;
 | 
			
		||||
pub use self::block::{RichBlock, Block, BlockTransactions};
 | 
			
		||||
pub use self::block_number::BlockNumber;
 | 
			
		||||
pub use self::call_request::CallRequest;
 | 
			
		||||
pub use self::confirmations::{ConfirmationPayload, ConfirmationRequest, ConfirmationResponse, TransactionModification, SignRequest, DecryptRequest, Either};
 | 
			
		||||
pub use self::confirmations::{
 | 
			
		||||
	ConfirmationPayload, ConfirmationRequest, ConfirmationResponse, ConfirmationResponseWithToken, TransactionModification, SignRequest, DecryptRequest, Either
 | 
			
		||||
};
 | 
			
		||||
pub use self::filter::{Filter, FilterChanges};
 | 
			
		||||
pub use self::hash::{H64, H160, H256, H512, H520, H2048};
 | 
			
		||||
pub use self::index::Index;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user