RPC for confirming with token
This commit is contained in:
parent
6397556cbb
commit
c028f106b1
@ -401,6 +401,28 @@ impl AccountProvider {
|
|||||||
Ok((signature, new_token))
|
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.
|
/// 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> {
|
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)));
|
let password = try!(password.map(Ok).unwrap_or_else(|| self.password(&account)));
|
||||||
@ -477,8 +499,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_sign_and_return_token() {
|
fn should_sign_and_return_token() {
|
||||||
let kp = Random.generate().unwrap();
|
|
||||||
// given
|
// given
|
||||||
|
let kp = Random.generate().unwrap();
|
||||||
let ap = AccountProvider::transient_provider();
|
let ap = AccountProvider::transient_provider();
|
||||||
assert!(ap.insert_account(kp.secret().clone(), "test").is_ok());
|
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
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use std::fmt::Debug;
|
||||||
use rlp;
|
use rlp;
|
||||||
use util::{Address, H256, U256, Uint, Bytes};
|
use util::{Address, H256, U256, Uint, Bytes};
|
||||||
use util::bytes::ToPretty;
|
use util::bytes::ToPretty;
|
||||||
@ -37,46 +38,101 @@ use v1::types::{
|
|||||||
|
|
||||||
pub const DEFAULT_MAC: [u8; 2] = [0, 0];
|
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
|
where C: MiningBlockChainClient, M: MinerService
|
||||||
{
|
{
|
||||||
match payload {
|
match payload {
|
||||||
ConfirmationPayload::SendTransaction(request) => {
|
ConfirmationPayload::SendTransaction(request) => {
|
||||||
sign_and_dispatch(client, miner, accounts, request, pass)
|
sign_and_dispatch(client, miner, accounts, request, pass)
|
||||||
.map(RpcH256::from)
|
.map(|result| result
|
||||||
.map(ConfirmationResponse::SendTransaction)
|
.map(RpcH256::from)
|
||||||
|
.map(ConfirmationResponse::SendTransaction)
|
||||||
|
)
|
||||||
},
|
},
|
||||||
ConfirmationPayload::SignTransaction(request) => {
|
ConfirmationPayload::SignTransaction(request) => {
|
||||||
sign_no_dispatch(client, miner, accounts, request, pass)
|
sign_no_dispatch(client, miner, accounts, request, pass)
|
||||||
.map(RpcRichRawTransaction::from)
|
.map(|result| result
|
||||||
.map(ConfirmationResponse::SignTransaction)
|
.map(RpcRichRawTransaction::from)
|
||||||
|
.map(ConfirmationResponse::SignTransaction)
|
||||||
|
)
|
||||||
},
|
},
|
||||||
ConfirmationPayload::Signature(address, hash) => {
|
ConfirmationPayload::Signature(address, hash) => {
|
||||||
signature(accounts, address, hash, pass)
|
signature(accounts, address, hash, pass)
|
||||||
.map(RpcH520::from)
|
.map(|result| result
|
||||||
.map(ConfirmationResponse::Signature)
|
.map(RpcH520::from)
|
||||||
|
.map(ConfirmationResponse::Signature)
|
||||||
|
)
|
||||||
},
|
},
|
||||||
ConfirmationPayload::Decrypt(address, data) => {
|
ConfirmationPayload::Decrypt(address, data) => {
|
||||||
decrypt(accounts, address, data, pass)
|
decrypt(accounts, address, data, pass)
|
||||||
.map(RpcBytes)
|
.map(|result| result
|
||||||
.map(ConfirmationResponse::Decrypt)
|
.map(RpcBytes)
|
||||||
|
.map(ConfirmationResponse::Decrypt)
|
||||||
|
)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn signature(accounts: &AccountProvider, address: Address, hash: H256, password: Option<String>) -> Result<Signature, Error> {
|
fn signature(accounts: &AccountProvider, address: Address, hash: H256, password: SignWith) -> Result<WithToken<Signature>, Error> {
|
||||||
accounts.sign(address, password.clone(), hash).map_err(|e| match password {
|
match password.clone() {
|
||||||
Some(_) => errors::from_password_error(e),
|
SignWith::Nothing => accounts.sign(address, None, hash).map(WithToken::No),
|
||||||
None => errors::from_signing_error(e),
|
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> {
|
fn decrypt(accounts: &AccountProvider, address: Address, msg: Bytes, password: SignWith) -> Result<WithToken<Bytes>, Error> {
|
||||||
accounts.decrypt(address, password.clone(), &DEFAULT_MAC, &msg)
|
match password.clone() {
|
||||||
.map_err(|e| match password {
|
SignWith::Nothing => accounts.decrypt(address, None, &DEFAULT_MAC, &msg).map(WithToken::No),
|
||||||
Some(_) => errors::from_password_error(e),
|
SignWith::Password(pass) => accounts.decrypt(address, Some(pass), &DEFAULT_MAC, &msg).map(WithToken::No),
|
||||||
None => errors::from_signing_error(e),
|
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>
|
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)
|
.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 {
|
where C: MiningBlockChainClient, M: MinerService {
|
||||||
|
|
||||||
let network_id = client.signing_network_id();
|
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 hash = t.hash(network_id);
|
||||||
let signature = try!(signature(accounts, address, hash, password));
|
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)
|
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
|
where C: MiningBlockChainClient, M: MinerService
|
||||||
{
|
{
|
||||||
|
|
||||||
let network_id = client.signing_network_id();
|
let network_id = client.signing_network_id();
|
||||||
let signed_transaction = try!(sign_no_dispatch(client, miner, accounts, filled, password));
|
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);
|
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
|
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,
|
&*miner,
|
||||||
&*accounts,
|
&*accounts,
|
||||||
request,
|
request,
|
||||||
Some(password)
|
dispatch::SignWith::Password(password)
|
||||||
).map(Into::into)
|
).map(|v| v.into_value().into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ use ethcore::miner::MinerService;
|
|||||||
|
|
||||||
use jsonrpc_core::Error;
|
use jsonrpc_core::Error;
|
||||||
use v1::traits::Signer;
|
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::{errors, SignerService, SigningQueue, ConfirmationPayload};
|
||||||
use v1::helpers::dispatch::{self, dispatch_transaction};
|
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();
|
take_weak!(self.client).keep_alive();
|
||||||
Ok(())
|
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 {
|
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
|
// TODO [ToDr] TransactionModification is redundant for some calls
|
||||||
// might be better to replace it in future
|
// might be better to replace it in future
|
||||||
fn confirm_request(&self, id: U256, modification: TransactionModification, pass: String) -> Result<ConfirmationResponse, Error> {
|
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();
|
fn confirm_request_with_token(&self, id: U256, modification: TransactionModification, token: String) -> Result<ConfirmationResponseWithToken, Error> {
|
||||||
let accounts = take_weak!(self.accounts);
|
unimplemented!()
|
||||||
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_raw(&self, id: U256, bytes: Bytes) -> Result<ConfirmationResponse, Error> {
|
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();
|
let sender = payload.sender();
|
||||||
if accounts.is_unlocked(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)
|
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 accounts = take_weak!(self.accounts);
|
||||||
|
|
||||||
let payload = dispatch::from_rpc(payload, &*client, &*miner);
|
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);
|
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]
|
#[test]
|
||||||
fn should_confirm_transaction_with_rlp() {
|
fn should_confirm_transaction_with_rlp() {
|
||||||
// given
|
// given
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
use jsonrpc_core::Error;
|
use jsonrpc_core::Error;
|
||||||
|
|
||||||
use v1::helpers::auto_args::Wrap;
|
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! {
|
build_rpc_trait! {
|
||||||
@ -33,6 +33,10 @@ build_rpc_trait! {
|
|||||||
#[rpc(name = "signer_confirmRequest")]
|
#[rpc(name = "signer_confirmRequest")]
|
||||||
fn confirm_request(&self, U256, TransactionModification, String) -> Result<ConfirmationResponse, Error>;
|
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.
|
/// Confirm specific request with already signed data.
|
||||||
#[rpc(name = "signer_confirmRequestRaw")]
|
#[rpc(name = "signer_confirmRequestRaw")]
|
||||||
fn confirm_request_raw(&self, U256, Bytes) -> Result<ConfirmationResponse, Error>;
|
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
|
/// Confirmation payload, i.e. the thing to be confirmed
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize)]
|
||||||
pub enum ConfirmationPayload {
|
pub enum ConfirmationPayload {
|
||||||
@ -247,5 +256,21 @@ mod tests {
|
|||||||
gas_price: None,
|
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::{RichBlock, Block, BlockTransactions};
|
||||||
pub use self::block_number::BlockNumber;
|
pub use self::block_number::BlockNumber;
|
||||||
pub use self::call_request::CallRequest;
|
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::filter::{Filter, FilterChanges};
|
||||||
pub use self::hash::{H64, H160, H256, H512, H520, H2048};
|
pub use self::hash::{H64, H160, H256, H512, H520, H2048};
|
||||||
pub use self::index::Index;
|
pub use self::index::Index;
|
||||||
|
Loading…
Reference in New Issue
Block a user