Supporting eth_sign in Signer (#1787)
* Making ConfirmationsQueue a bit more generic [WiP] * Generalizing cofirmations * New confirmations types - tests * Separating transaction type in queue. Closes #1310 * Handling sign requests * Speeding up tests * Renaming methods * eth_postSign * Bumping ui
This commit is contained in:
parent
087ebcf94e
commit
9fb5623569
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -924,7 +924,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-dapps"
|
name = "parity-dapps"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
source = "git+https://github.com/ethcore/parity-ui.git#7120546d08d4d9eb648e255c04935002223d362f"
|
source = "git+https://github.com/ethcore/parity-ui.git#697e860dedc45003909602a002e7743478ab173a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"aster 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -938,7 +938,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-dapps-home"
|
name = "parity-dapps-home"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
source = "git+https://github.com/ethcore/parity-ui.git#7120546d08d4d9eb648e255c04935002223d362f"
|
source = "git+https://github.com/ethcore/parity-ui.git#697e860dedc45003909602a002e7743478ab173a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"parity-dapps 0.6.0 (git+https://github.com/ethcore/parity-ui.git)",
|
"parity-dapps 0.6.0 (git+https://github.com/ethcore/parity-ui.git)",
|
||||||
]
|
]
|
||||||
@ -946,7 +946,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-dapps-signer"
|
name = "parity-dapps-signer"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
source = "git+https://github.com/ethcore/parity-ui.git#7120546d08d4d9eb648e255c04935002223d362f"
|
source = "git+https://github.com/ethcore/parity-ui.git#697e860dedc45003909602a002e7743478ab173a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"parity-dapps 0.6.0 (git+https://github.com/ethcore/parity-ui.git)",
|
"parity-dapps 0.6.0 (git+https://github.com/ethcore/parity-ui.git)",
|
||||||
]
|
]
|
||||||
@ -954,7 +954,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-dapps-status"
|
name = "parity-dapps-status"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
source = "git+https://github.com/ethcore/parity-ui.git#7120546d08d4d9eb648e255c04935002223d362f"
|
source = "git+https://github.com/ethcore/parity-ui.git#697e860dedc45003909602a002e7743478ab173a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"parity-dapps 0.6.0 (git+https://github.com/ethcore/parity-ui.git)",
|
"parity-dapps 0.6.0 (git+https://github.com/ethcore/parity-ui.git)",
|
||||||
]
|
]
|
||||||
@ -962,7 +962,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-dapps-wallet"
|
name = "parity-dapps-wallet"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
source = "git+https://github.com/ethcore/parity-ui.git#7120546d08d4d9eb648e255c04935002223d362f"
|
source = "git+https://github.com/ethcore/parity-ui.git#697e860dedc45003909602a002e7743478ab173a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"parity-dapps 0.6.0 (git+https://github.com/ethcore/parity-ui.git)",
|
"parity-dapps 0.6.0 (git+https://github.com/ethcore/parity-ui.git)",
|
||||||
]
|
]
|
||||||
|
@ -21,5 +21,5 @@ mod signing_queue;
|
|||||||
|
|
||||||
pub use self::poll_manager::PollManager;
|
pub use self::poll_manager::PollManager;
|
||||||
pub use self::poll_filter::PollFilter;
|
pub use self::poll_filter::PollFilter;
|
||||||
pub use self::requests::{TransactionRequest, TransactionConfirmation, CallRequest};
|
pub use self::requests::{TransactionRequest, FilledTransactionRequest, ConfirmationRequest, ConfirmationPayload, CallRequest};
|
||||||
pub use self::signing_queue::{ConfirmationsQueue, ConfirmationPromise, ConfirmationResult, SigningQueue, QueueEvent};
|
pub use self::signing_queue::{ConfirmationsQueue, ConfirmationPromise, ConfirmationResult, SigningQueue, QueueEvent};
|
||||||
|
@ -14,7 +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 util::{Address, U256};
|
use util::{Address, U256, Bytes, H256};
|
||||||
|
|
||||||
/// Transaction request coming from RPC
|
/// Transaction request coming from RPC
|
||||||
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash)]
|
||||||
@ -30,18 +30,42 @@ pub struct TransactionRequest {
|
|||||||
/// Value of transaction in wei
|
/// Value of transaction in wei
|
||||||
pub value: Option<U256>,
|
pub value: Option<U256>,
|
||||||
/// Additional data sent with transaction
|
/// Additional data sent with transaction
|
||||||
pub data: Option<Vec<u8>>,
|
pub data: Option<Bytes>,
|
||||||
/// Transaction's nonce
|
/// Transaction's nonce
|
||||||
pub nonce: Option<U256>,
|
pub nonce: Option<U256>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Transaction confirmation waiting in a queue
|
/// Transaction request coming from RPC with default values filled in.
|
||||||
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash)]
|
||||||
pub struct TransactionConfirmation {
|
pub struct FilledTransactionRequest {
|
||||||
/// Id of this confirmation
|
/// Sender
|
||||||
pub id: U256,
|
pub from: Address,
|
||||||
/// TransactionRequest
|
/// Recipient
|
||||||
pub transaction: TransactionRequest,
|
pub to: Option<Address>,
|
||||||
|
/// Gas Price
|
||||||
|
pub gas_price: U256,
|
||||||
|
/// Gas
|
||||||
|
pub gas: U256,
|
||||||
|
/// Value of transaction in wei
|
||||||
|
pub value: U256,
|
||||||
|
/// Additional data sent with transaction
|
||||||
|
pub data: Bytes,
|
||||||
|
/// Transaction's nonce
|
||||||
|
pub nonce: Option<U256>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<FilledTransactionRequest> for TransactionRequest {
|
||||||
|
fn from(r: FilledTransactionRequest) -> Self {
|
||||||
|
TransactionRequest {
|
||||||
|
from: r.from,
|
||||||
|
to: r.to,
|
||||||
|
gas_price: Some(r.gas_price),
|
||||||
|
gas: Some(r.gas),
|
||||||
|
value: Some(r.value),
|
||||||
|
data: Some(r.data),
|
||||||
|
nonce: r.nonce,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call request
|
/// Call request
|
||||||
@ -62,3 +86,21 @@ pub struct CallRequest {
|
|||||||
/// Nonce
|
/// Nonce
|
||||||
pub nonce: Option<U256>,
|
pub nonce: Option<U256>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Confirmation object
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
|
pub struct ConfirmationRequest {
|
||||||
|
/// Id of this confirmation
|
||||||
|
pub id: U256,
|
||||||
|
/// Payload to confirm
|
||||||
|
pub payload: ConfirmationPayload,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Payload to confirm in Trusted Signer
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||||
|
pub enum ConfirmationPayload {
|
||||||
|
/// Transaction
|
||||||
|
Transaction(FilledTransactionRequest),
|
||||||
|
/// Sign request
|
||||||
|
Sign(Address, H256),
|
||||||
|
}
|
||||||
|
@ -17,10 +17,10 @@
|
|||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::{Instant, Duration};
|
use std::time::{Instant, Duration};
|
||||||
use std::sync::{mpsc, Arc};
|
use std::sync::{mpsc, Arc};
|
||||||
use std::collections::HashMap;
|
use std::collections::BTreeMap;
|
||||||
use jsonrpc_core;
|
use jsonrpc_core;
|
||||||
use util::{Mutex, RwLock, U256};
|
use util::{Mutex, RwLock, U256};
|
||||||
use v1::helpers::{TransactionRequest, TransactionConfirmation};
|
use v1::helpers::{ConfirmationRequest, ConfirmationPayload};
|
||||||
|
|
||||||
/// Result that can be returned from JSON RPC.
|
/// Result that can be returned from JSON RPC.
|
||||||
pub type RpcResult = Result<jsonrpc_core::Value, jsonrpc_core::Error>;
|
pub type RpcResult = Result<jsonrpc_core::Value, jsonrpc_core::Error>;
|
||||||
@ -54,41 +54,41 @@ pub type QueueEventReceiver = mpsc::Receiver<QueueEvent>;
|
|||||||
pub trait SigningQueue: Send + Sync {
|
pub trait SigningQueue: Send + Sync {
|
||||||
/// Add new request to the queue.
|
/// Add new request to the queue.
|
||||||
/// Returns a `ConfirmationPromise` that can be used to await for resolution of given request.
|
/// Returns a `ConfirmationPromise` that can be used to await for resolution of given request.
|
||||||
fn add_request(&self, transaction: TransactionRequest) -> ConfirmationPromise;
|
fn add_request(&self, request: ConfirmationPayload) -> ConfirmationPromise;
|
||||||
|
|
||||||
/// Removes a request from the queue.
|
/// Removes a request from the queue.
|
||||||
/// Notifies possible token holders that transaction was rejected.
|
/// Notifies possible token holders that request was rejected.
|
||||||
fn request_rejected(&self, id: U256) -> Option<TransactionConfirmation>;
|
fn request_rejected(&self, id: U256) -> Option<ConfirmationRequest>;
|
||||||
|
|
||||||
/// Removes a request from the queue.
|
/// Removes a request from the queue.
|
||||||
/// Notifies possible token holders that transaction was confirmed and given hash was assigned.
|
/// Notifies possible token holders that request was confirmed and given hash was assigned.
|
||||||
fn request_confirmed(&self, id: U256, result: RpcResult) -> Option<TransactionConfirmation>;
|
fn request_confirmed(&self, id: U256, result: RpcResult) -> Option<ConfirmationRequest>;
|
||||||
|
|
||||||
/// Returns a request if it is contained in the queue.
|
/// Returns a request if it is contained in the queue.
|
||||||
fn peek(&self, id: &U256) -> Option<TransactionConfirmation>;
|
fn peek(&self, id: &U256) -> Option<ConfirmationRequest>;
|
||||||
|
|
||||||
/// Return copy of all the requests in the queue.
|
/// Return copy of all the requests in the queue.
|
||||||
fn requests(&self) -> Vec<TransactionConfirmation>;
|
fn requests(&self) -> Vec<ConfirmationRequest>;
|
||||||
|
|
||||||
/// Returns number of transactions awaiting confirmation.
|
/// Returns number of requests awaiting confirmation.
|
||||||
fn len(&self) -> usize;
|
fn len(&self) -> usize;
|
||||||
|
|
||||||
/// Returns true if there are no transactions awaiting confirmation.
|
/// Returns true if there are no requests awaiting confirmation.
|
||||||
fn is_empty(&self) -> bool;
|
fn is_empty(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
/// Result of a pending transaction.
|
/// Result of a pending confirmation request.
|
||||||
pub enum ConfirmationResult {
|
pub enum ConfirmationResult {
|
||||||
/// The transaction has not yet been confirmed nor rejected.
|
/// The request has not yet been confirmed nor rejected.
|
||||||
Waiting,
|
Waiting,
|
||||||
/// The transaction has been rejected.
|
/// The request has been rejected.
|
||||||
Rejected,
|
Rejected,
|
||||||
/// The transaction has been confirmed.
|
/// The request has been confirmed.
|
||||||
Confirmed(RpcResult),
|
Confirmed(RpcResult),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Time you need to confirm the transaction in UI.
|
/// Time you need to confirm the request in UI.
|
||||||
/// This is the amount of time token holder will wait before
|
/// This is the amount of time token holder will wait before
|
||||||
/// returning `None`.
|
/// returning `None`.
|
||||||
/// Unless we have a multi-threaded RPC this will lock
|
/// Unless we have a multi-threaded RPC this will lock
|
||||||
@ -100,12 +100,14 @@ const QUEUE_TIMEOUT_DURATION_SEC : u64 = 20;
|
|||||||
pub struct ConfirmationToken {
|
pub struct ConfirmationToken {
|
||||||
result: Arc<Mutex<ConfirmationResult>>,
|
result: Arc<Mutex<ConfirmationResult>>,
|
||||||
handle: thread::Thread,
|
handle: thread::Thread,
|
||||||
request: TransactionConfirmation,
|
request: ConfirmationRequest,
|
||||||
|
timeout: Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ConfirmationPromise {
|
pub struct ConfirmationPromise {
|
||||||
id: U256,
|
id: U256,
|
||||||
result: Arc<Mutex<ConfirmationResult>>,
|
result: Arc<Mutex<ConfirmationResult>>,
|
||||||
|
timeout: Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConfirmationToken {
|
impl ConfirmationToken {
|
||||||
@ -121,6 +123,7 @@ impl ConfirmationToken {
|
|||||||
ConfirmationPromise {
|
ConfirmationPromise {
|
||||||
id: self.request.id,
|
id: self.request.id,
|
||||||
result: self.result.clone(),
|
result: self.result.clone(),
|
||||||
|
timeout: self.timeout,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -134,8 +137,7 @@ impl ConfirmationPromise {
|
|||||||
/// Returns `None` if transaction was rejected or timeout reached.
|
/// Returns `None` if transaction was rejected or timeout reached.
|
||||||
/// Returns `Some(result)` if transaction was confirmed.
|
/// Returns `Some(result)` if transaction was confirmed.
|
||||||
pub fn wait_with_timeout(&self) -> Option<RpcResult> {
|
pub fn wait_with_timeout(&self) -> Option<RpcResult> {
|
||||||
let timeout = Duration::from_secs(QUEUE_TIMEOUT_DURATION_SEC);
|
let res = self.wait_until(Instant::now() + self.timeout);
|
||||||
let res = self.wait_until(Instant::now() + timeout);
|
|
||||||
match res {
|
match res {
|
||||||
ConfirmationResult::Confirmed(h) => Some(h),
|
ConfirmationResult::Confirmed(h) => Some(h),
|
||||||
ConfirmationResult::Rejected | ConfirmationResult::Waiting => None,
|
ConfirmationResult::Rejected | ConfirmationResult::Waiting => None,
|
||||||
@ -146,16 +148,16 @@ impl ConfirmationPromise {
|
|||||||
pub fn result(&self) -> ConfirmationResult { self.wait_until(Instant::now()) }
|
pub fn result(&self) -> ConfirmationResult { self.wait_until(Instant::now()) }
|
||||||
|
|
||||||
/// Blocks current thread and awaits for
|
/// Blocks current thread and awaits for
|
||||||
/// resolution of the transaction (rejected / confirmed)
|
/// resolution of the request (rejected / confirmed)
|
||||||
/// Returns `None` if transaction was rejected or timeout reached.
|
/// Returns `None` if request was rejected or timeout reached.
|
||||||
/// Returns `Some(result)` if transaction was confirmed.
|
/// Returns `Some(result)` if request was confirmed.
|
||||||
pub fn wait_until(&self, deadline: Instant) -> ConfirmationResult {
|
pub fn wait_until(&self, deadline: Instant) -> ConfirmationResult {
|
||||||
trace!(target: "own_tx", "Signer: Awaiting transaction confirmation... ({:?}).", self.id);
|
trace!(target: "own_tx", "Signer: Awaiting confirmation... ({:?}).", self.id);
|
||||||
loop {
|
loop {
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
// Check the result...
|
// Check the result...
|
||||||
match *self.result.lock() {
|
match *self.result.lock() {
|
||||||
// Waiting and deadline not yet passed continue looping.
|
// Waiting and deadline not yet passed continue looping.
|
||||||
ConfirmationResult::Waiting if now < deadline => {}
|
ConfirmationResult::Waiting if now < deadline => {}
|
||||||
// Anything else - return.
|
// Anything else - return.
|
||||||
ref a => return a.clone(),
|
ref a => return a.clone(),
|
||||||
@ -166,12 +168,13 @@ impl ConfirmationPromise {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Queue for all unconfirmed transactions.
|
/// Queue for all unconfirmed requests.
|
||||||
pub struct ConfirmationsQueue {
|
pub struct ConfirmationsQueue {
|
||||||
id: Mutex<U256>,
|
id: Mutex<U256>,
|
||||||
queue: RwLock<HashMap<U256, ConfirmationToken>>,
|
queue: RwLock<BTreeMap<U256, ConfirmationToken>>,
|
||||||
sender: Mutex<mpsc::Sender<QueueEvent>>,
|
sender: Mutex<mpsc::Sender<QueueEvent>>,
|
||||||
receiver: Mutex<Option<mpsc::Receiver<QueueEvent>>>,
|
receiver: Mutex<Option<mpsc::Receiver<QueueEvent>>>,
|
||||||
|
timeout: Duration,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ConfirmationsQueue {
|
impl Default for ConfirmationsQueue {
|
||||||
@ -180,14 +183,23 @@ impl Default for ConfirmationsQueue {
|
|||||||
|
|
||||||
ConfirmationsQueue {
|
ConfirmationsQueue {
|
||||||
id: Mutex::new(U256::from(0)),
|
id: Mutex::new(U256::from(0)),
|
||||||
queue: RwLock::new(HashMap::new()),
|
queue: RwLock::new(BTreeMap::new()),
|
||||||
sender: Mutex::new(send),
|
sender: Mutex::new(send),
|
||||||
receiver: Mutex::new(Some(recv)),
|
receiver: Mutex::new(Some(recv)),
|
||||||
|
timeout: Duration::from_secs(QUEUE_TIMEOUT_DURATION_SEC),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConfirmationsQueue {
|
impl ConfirmationsQueue {
|
||||||
|
#[cfg(test)]
|
||||||
|
/// Creates new confirmations queue with specified timeout
|
||||||
|
pub fn with_timeout(timeout: Duration) -> Self {
|
||||||
|
let mut queue = Self::default();
|
||||||
|
queue.timeout = timeout;
|
||||||
|
queue
|
||||||
|
}
|
||||||
|
|
||||||
/// Blocks the thread and starts listening for notifications regarding all actions in the queue.
|
/// Blocks the thread and starts listening for notifications regarding all actions in the queue.
|
||||||
/// For each event, `listener` callback will be invoked.
|
/// For each event, `listener` callback will be invoked.
|
||||||
/// This method can be used only once (only single consumer of events can exist).
|
/// This method can be used only once (only single consumer of events can exist).
|
||||||
@ -221,9 +233,9 @@ impl ConfirmationsQueue {
|
|||||||
let _ = self.sender.lock().send(message);
|
let _ = self.sender.lock().send(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes transaction from this queue and notifies `ConfirmationPromise` holders about the result.
|
/// Removes requests from this queue and notifies `ConfirmationPromise` holders about the result.
|
||||||
/// Notifies also a receiver about that event.
|
/// Notifies also a receiver about that event.
|
||||||
fn remove(&self, id: U256, result: Option<RpcResult>) -> Option<TransactionConfirmation> {
|
fn remove(&self, id: U256, result: Option<RpcResult>) -> Option<ConfirmationRequest> {
|
||||||
let token = self.queue.write().remove(&id);
|
let token = self.queue.write().remove(&id);
|
||||||
|
|
||||||
if let Some(token) = token {
|
if let Some(token) = token {
|
||||||
@ -248,7 +260,7 @@ impl Drop for ConfirmationsQueue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SigningQueue for ConfirmationsQueue {
|
impl SigningQueue for ConfirmationsQueue {
|
||||||
fn add_request(&self, transaction: TransactionRequest) -> ConfirmationPromise {
|
fn add_request(&self, request: ConfirmationPayload) -> ConfirmationPromise {
|
||||||
// Increment id
|
// Increment id
|
||||||
let id = {
|
let id = {
|
||||||
let mut last_id = self.id.lock();
|
let mut last_id = self.id.lock();
|
||||||
@ -257,16 +269,19 @@ impl SigningQueue for ConfirmationsQueue {
|
|||||||
};
|
};
|
||||||
// Add request to queue
|
// Add request to queue
|
||||||
let res = {
|
let res = {
|
||||||
|
debug!(target: "own_tx", "Signer: New entry ({:?}) in confirmation queue.", id);
|
||||||
|
trace!(target: "own_tx", "Signer: ({:?}) : {:?}", id, request);
|
||||||
|
|
||||||
let mut queue = self.queue.write();
|
let mut queue = self.queue.write();
|
||||||
queue.insert(id, ConfirmationToken {
|
queue.insert(id, ConfirmationToken {
|
||||||
result: Arc::new(Mutex::new(ConfirmationResult::Waiting)),
|
result: Arc::new(Mutex::new(ConfirmationResult::Waiting)),
|
||||||
handle: thread::current(),
|
handle: thread::current(),
|
||||||
request: TransactionConfirmation {
|
request: ConfirmationRequest {
|
||||||
id: id,
|
id: id,
|
||||||
transaction: transaction,
|
payload: request,
|
||||||
},
|
},
|
||||||
|
timeout: self.timeout,
|
||||||
});
|
});
|
||||||
debug!(target: "own_tx", "Signer: New transaction ({:?}) in confirmation queue.", id);
|
|
||||||
queue.get(&id).map(|token| token.as_promise()).expect("Token was just inserted.")
|
queue.get(&id).map(|token| token.as_promise()).expect("Token was just inserted.")
|
||||||
};
|
};
|
||||||
// Notify listeners
|
// Notify listeners
|
||||||
@ -275,21 +290,21 @@ impl SigningQueue for ConfirmationsQueue {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn peek(&self, id: &U256) -> Option<TransactionConfirmation> {
|
fn peek(&self, id: &U256) -> Option<ConfirmationRequest> {
|
||||||
self.queue.read().get(id).map(|token| token.request.clone())
|
self.queue.read().get(id).map(|token| token.request.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn request_rejected(&self, id: U256) -> Option<TransactionConfirmation> {
|
fn request_rejected(&self, id: U256) -> Option<ConfirmationRequest> {
|
||||||
debug!(target: "own_tx", "Signer: Transaction rejected ({:?}).", id);
|
debug!(target: "own_tx", "Signer: Request rejected ({:?}).", id);
|
||||||
self.remove(id, None)
|
self.remove(id, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn request_confirmed(&self, id: U256, result: RpcResult) -> Option<TransactionConfirmation> {
|
fn request_confirmed(&self, id: U256, result: RpcResult) -> Option<ConfirmationRequest> {
|
||||||
debug!(target: "own_tx", "Signer: Transaction confirmed ({:?}).", id);
|
debug!(target: "own_tx", "Signer: Transaction confirmed ({:?}).", id);
|
||||||
self.remove(id, Some(result))
|
self.remove(id, Some(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn requests(&self) -> Vec<TransactionConfirmation> {
|
fn requests(&self) -> Vec<ConfirmationRequest> {
|
||||||
let queue = self.queue.read();
|
let queue = self.queue.read();
|
||||||
queue.values().map(|token| token.request.clone()).collect()
|
queue.values().map(|token| token.request.clone()).collect()
|
||||||
}
|
}
|
||||||
@ -312,20 +327,20 @@ mod test {
|
|||||||
use std::thread;
|
use std::thread;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use util::{Address, U256, H256, Mutex};
|
use util::{Address, U256, H256, Mutex};
|
||||||
use v1::helpers::{SigningQueue, ConfirmationsQueue, QueueEvent, TransactionRequest};
|
use v1::helpers::{SigningQueue, ConfirmationsQueue, QueueEvent, FilledTransactionRequest, ConfirmationPayload};
|
||||||
use v1::types::H256 as NH256;
|
use v1::types::H256 as NH256;
|
||||||
use jsonrpc_core::to_value;
|
use jsonrpc_core::to_value;
|
||||||
|
|
||||||
fn request() -> TransactionRequest {
|
fn request() -> ConfirmationPayload {
|
||||||
TransactionRequest {
|
ConfirmationPayload::Transaction(FilledTransactionRequest {
|
||||||
from: Address::from(1),
|
from: Address::from(1),
|
||||||
to: Some(Address::from(2)),
|
to: Some(Address::from(2)),
|
||||||
gas_price: None,
|
gas_price: 0.into(),
|
||||||
gas: None,
|
gas: 10_000.into(),
|
||||||
value: Some(U256::from(10_000_000)),
|
value: 10_000_000.into(),
|
||||||
data: None,
|
data: vec![],
|
||||||
nonce: None,
|
nonce: None,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -391,6 +406,6 @@ mod test {
|
|||||||
assert_eq!(all.len(), 1);
|
assert_eq!(all.len(), 1);
|
||||||
let el = all.get(0).unwrap();
|
let el = all.get(0).unwrap();
|
||||||
assert_eq!(el.id, U256::from(1));
|
assert_eq!(el.id, U256::from(1));
|
||||||
assert_eq!(el.transaction, request);
|
assert_eq!(el.payload, request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,24 +23,21 @@ use ethcore::client::MiningBlockChainClient;
|
|||||||
use util::{U256, Address, H256, Mutex};
|
use util::{U256, Address, H256, Mutex};
|
||||||
use transient_hashmap::TransientHashMap;
|
use transient_hashmap::TransientHashMap;
|
||||||
use ethcore::account_provider::AccountProvider;
|
use ethcore::account_provider::AccountProvider;
|
||||||
use v1::helpers::{SigningQueue, ConfirmationPromise, ConfirmationResult, ConfirmationsQueue, TransactionRequest as TRequest};
|
use v1::helpers::{SigningQueue, ConfirmationPromise, ConfirmationResult, ConfirmationsQueue, ConfirmationPayload, TransactionRequest as TRequest, FilledTransactionRequest as FilledRequest};
|
||||||
use v1::traits::EthSigning;
|
use v1::traits::EthSigning;
|
||||||
use v1::types::{TransactionRequest, H160 as RpcH160, H256 as RpcH256, H520 as RpcH520, U256 as RpcU256};
|
use v1::types::{TransactionRequest, H160 as RpcH160, H256 as RpcH256, H520 as RpcH520, U256 as RpcU256};
|
||||||
use v1::impls::{default_gas_price, sign_and_dispatch, transaction_rejected_error};
|
use v1::impls::{default_gas_price, sign_and_dispatch, transaction_rejected_error, signer_disabled_error};
|
||||||
|
|
||||||
fn fill_optional_fields<C, M>(request: &mut TRequest, client: &C, miner: &M)
|
fn fill_optional_fields<C, M>(request: TRequest, client: &C, miner: &M) -> FilledRequest
|
||||||
where C: MiningBlockChainClient, M: MinerService {
|
where C: MiningBlockChainClient, M: MinerService {
|
||||||
if request.value.is_none() {
|
FilledRequest {
|
||||||
request.value = Some(U256::from(0));
|
from: request.from,
|
||||||
}
|
to: request.to,
|
||||||
if request.gas.is_none() {
|
nonce: request.nonce,
|
||||||
request.gas = Some(miner.sensible_gas_limit());
|
gas_price: request.gas_price.unwrap_or_else(|| default_gas_price(client, miner)),
|
||||||
}
|
gas: request.gas.unwrap_or_else(|| miner.sensible_gas_limit()),
|
||||||
if request.gas_price.is_none() {
|
value: request.value.unwrap_or_else(|| 0.into()),
|
||||||
request.gas_price = Some(default_gas_price(client, miner));
|
data: request.data.unwrap_or_else(Vec::new),
|
||||||
}
|
|
||||||
if request.data.is_none() {
|
|
||||||
request.data = Some(Vec::new());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,10 +71,26 @@ impl<C, M> EthSigningQueueClient<C, M> where C: MiningBlockChainClient, M: Miner
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dispatch<F: FnOnce(ConfirmationPromise) -> Result<Value, Error>>(&self, params: Params, f: F) -> Result<Value, Error> {
|
fn dispatch_sign<F: FnOnce(ConfirmationPromise) -> Result<Value, Error>>(&self, params: Params, f: F) -> Result<Value, Error> {
|
||||||
|
from_params::<(RpcH160, RpcH256)>(params).and_then(|(address, msg)| {
|
||||||
|
let address: Address = address.into();
|
||||||
|
let msg: H256 = msg.into();
|
||||||
|
|
||||||
|
let accounts = take_weak!(self.accounts);
|
||||||
|
if accounts.is_unlocked(address) {
|
||||||
|
return to_value(&accounts.sign(address, msg).ok().map_or_else(RpcH520::default, Into::into));
|
||||||
|
}
|
||||||
|
|
||||||
|
let queue = take_weak!(self.queue);
|
||||||
|
let promise = queue.add_request(ConfirmationPayload::Sign(address, msg));
|
||||||
|
f(promise)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dispatch_transaction<F: FnOnce(ConfirmationPromise) -> Result<Value, Error>>(&self, params: Params, f: F) -> Result<Value, Error> {
|
||||||
from_params::<(TransactionRequest, )>(params)
|
from_params::<(TransactionRequest, )>(params)
|
||||||
.and_then(|(request, )| {
|
.and_then(|(request, )| {
|
||||||
let mut request: TRequest = request.into();
|
let request: TRequest = request.into();
|
||||||
let accounts = take_weak!(self.accounts);
|
let accounts = take_weak!(self.accounts);
|
||||||
let (client, miner) = (take_weak!(self.client), take_weak!(self.miner));
|
let (client, miner) = (take_weak!(self.client), take_weak!(self.miner));
|
||||||
|
|
||||||
@ -87,8 +100,8 @@ impl<C, M> EthSigningQueueClient<C, M> where C: MiningBlockChainClient, M: Miner
|
|||||||
}
|
}
|
||||||
|
|
||||||
let queue = take_weak!(self.queue);
|
let queue = take_weak!(self.queue);
|
||||||
fill_optional_fields(&mut request, &*client, &*miner);
|
let request = fill_optional_fields(request, &*client, &*miner);
|
||||||
let promise = queue.add_request(request);
|
let promise = queue.add_request(ConfirmationPayload::Transaction(request));
|
||||||
f(promise)
|
f(promise)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -98,23 +111,32 @@ impl<C, M> EthSigning for EthSigningQueueClient<C, M>
|
|||||||
where C: MiningBlockChainClient + 'static, M: MinerService + 'static
|
where C: MiningBlockChainClient + 'static, M: MinerService + 'static
|
||||||
{
|
{
|
||||||
|
|
||||||
fn sign(&self, _params: Params) -> Result<Value, Error> {
|
fn sign(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
warn!("Invoking eth_sign is not yet supported with signer enabled.");
|
self.dispatch_sign(params, |promise| {
|
||||||
// TODO [ToDr] Implement sign when rest of the signing queue is ready.
|
promise.wait_with_timeout().unwrap_or_else(|| to_value(&RpcH520::default()))
|
||||||
rpc_unimplemented!()
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn post_sign(&self, params: Params) -> Result<Value, Error> {
|
||||||
|
try!(self.active());
|
||||||
|
self.dispatch_sign(params, |promise| {
|
||||||
|
let id = promise.id();
|
||||||
|
self.pending.lock().insert(id, promise);
|
||||||
|
to_value(&RpcU256::from(id))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn send_transaction(&self, params: Params) -> Result<Value, Error> {
|
fn send_transaction(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
self.dispatch(params, |promise| {
|
self.dispatch_transaction(params, |promise| {
|
||||||
promise.wait_with_timeout().unwrap_or_else(|| to_value(&RpcH256::default()))
|
promise.wait_with_timeout().unwrap_or_else(|| to_value(&RpcH256::default()))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn post_transaction(&self, params: Params) -> Result<Value, Error> {
|
fn post_transaction(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
self.dispatch(params, |promise| {
|
self.dispatch_transaction(params, |promise| {
|
||||||
let id = promise.id();
|
let id = promise.id();
|
||||||
self.pending.lock().insert(id, promise);
|
self.pending.lock().insert(id, promise);
|
||||||
to_value(&RpcU256::from(id))
|
to_value(&RpcU256::from(id))
|
||||||
@ -193,13 +215,18 @@ impl<C, M> EthSigning for EthSigningUnsafeClient<C, M> where
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn post_sign(&self, _: Params) -> Result<Value, Error> {
|
||||||
|
// We don't support this in non-signer mode.
|
||||||
|
Err(signer_disabled_error())
|
||||||
|
}
|
||||||
|
|
||||||
fn post_transaction(&self, _: Params) -> Result<Value, Error> {
|
fn post_transaction(&self, _: Params) -> Result<Value, Error> {
|
||||||
// We don't support this in non-signer mode.
|
// We don't support this in non-signer mode.
|
||||||
Err(Error::invalid_params())
|
Err(signer_disabled_error())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_transaction(&self, _: Params) -> Result<Value, Error> {
|
fn check_transaction(&self, _: Params) -> Result<Value, Error> {
|
||||||
// We don't support this in non-signer mode.
|
// We don't support this in non-signer mode.
|
||||||
Err(Error::invalid_params())
|
Err(signer_disabled_error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ use ethcore::miner::MinerService;
|
|||||||
use v1::traits::Ethcore;
|
use v1::traits::Ethcore;
|
||||||
use v1::types::{Bytes, U256};
|
use v1::types::{Bytes, U256};
|
||||||
use v1::helpers::{SigningQueue, ConfirmationsQueue};
|
use v1::helpers::{SigningQueue, ConfirmationsQueue};
|
||||||
use v1::impls::error_codes;
|
use v1::impls::signer_disabled_error;
|
||||||
|
|
||||||
/// Ethcore implementation.
|
/// Ethcore implementation.
|
||||||
pub struct EthcoreClient<C, M> where
|
pub struct EthcoreClient<C, M> where
|
||||||
@ -152,11 +152,7 @@ impl<C, M> Ethcore for EthcoreClient<C, M> where M: MinerService + 'static, C: M
|
|||||||
fn unsigned_transactions_count(&self, _params: Params) -> Result<Value, Error> {
|
fn unsigned_transactions_count(&self, _params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
match self.confirmations_queue {
|
match self.confirmations_queue {
|
||||||
None => Err(Error {
|
None => Err(signer_disabled_error()),
|
||||||
code: ErrorCode::ServerError(error_codes::SIGNER_DISABLED),
|
|
||||||
message: "Trusted Signer is disabled. This API is not available.".into(),
|
|
||||||
data: None
|
|
||||||
}),
|
|
||||||
Some(ref queue) => to_value(&queue.len()),
|
Some(ref queue) => to_value(&queue.len()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ pub use self::traces::TracesClient;
|
|||||||
pub use self::rpc::RpcClient;
|
pub use self::rpc::RpcClient;
|
||||||
|
|
||||||
use v1::helpers::TransactionRequest;
|
use v1::helpers::TransactionRequest;
|
||||||
use v1::types::H256 as NH256;
|
use v1::types::{H256 as RpcH256, H520 as RpcH520};
|
||||||
use ethcore::error::Error as EthcoreError;
|
use ethcore::error::Error as EthcoreError;
|
||||||
use ethcore::miner::MinerService;
|
use ethcore::miner::MinerService;
|
||||||
use ethcore::client::MiningBlockChainClient;
|
use ethcore::client::MiningBlockChainClient;
|
||||||
@ -80,7 +80,7 @@ mod error_codes {
|
|||||||
|
|
||||||
fn dispatch_transaction<C, M>(client: &C, miner: &M, signed_transaction: SignedTransaction) -> Result<Value, Error>
|
fn dispatch_transaction<C, M>(client: &C, miner: &M, signed_transaction: SignedTransaction) -> Result<Value, Error>
|
||||||
where C: MiningBlockChainClient, M: MinerService {
|
where C: MiningBlockChainClient, M: MinerService {
|
||||||
let hash = NH256::from(signed_transaction.hash());
|
let hash = RpcH256::from(signed_transaction.hash());
|
||||||
|
|
||||||
let import = miner.import_own_transaction(client, signed_transaction);
|
let import = miner.import_own_transaction(client, signed_transaction);
|
||||||
|
|
||||||
@ -89,6 +89,12 @@ fn dispatch_transaction<C, M>(client: &C, miner: &M, signed_transaction: SignedT
|
|||||||
.and_then(|_| to_value(&hash))
|
.and_then(|_| to_value(&hash))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn signature_with_password(accounts: &AccountProvider, address: Address, hash: H256, pass: String) -> Result<Value, Error> {
|
||||||
|
accounts.sign_with_password(address, pass, hash)
|
||||||
|
.map_err(password_error)
|
||||||
|
.and_then(|hash| to_value(&RpcH520::from(hash)))
|
||||||
|
}
|
||||||
|
|
||||||
fn prepare_transaction<C, M>(client: &C, miner: &M, request: TransactionRequest) -> Transaction where C: MiningBlockChainClient, M: MinerService {
|
fn prepare_transaction<C, M>(client: &C, miner: &M, request: TransactionRequest) -> Transaction where C: MiningBlockChainClient, M: MinerService {
|
||||||
Transaction {
|
Transaction {
|
||||||
nonce: request.nonce
|
nonce: request.nonce
|
||||||
@ -105,9 +111,10 @@ fn prepare_transaction<C, M>(client: &C, miner: &M, request: TransactionRequest)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unlock_sign_and_dispatch<C, M>(client: &C, miner: &M, request: TransactionRequest, account_provider: &AccountProvider, address: Address, password: String) -> Result<Value, Error>
|
fn unlock_sign_and_dispatch<C, M>(client: &C, miner: &M, request: TransactionRequest, account_provider: &AccountProvider, password: String) -> Result<Value, Error>
|
||||||
where C: MiningBlockChainClient, M: MinerService {
|
where C: MiningBlockChainClient, M: MinerService {
|
||||||
|
|
||||||
|
let address = request.from;
|
||||||
let signed_transaction = {
|
let signed_transaction = {
|
||||||
let t = prepare_transaction(client, miner, request);
|
let t = prepare_transaction(client, miner, request);
|
||||||
let hash = t.hash();
|
let hash = t.hash();
|
||||||
@ -140,6 +147,14 @@ fn default_gas_price<C, M>(client: &C, miner: &M) -> U256 where C: MiningBlockCh
|
|||||||
.unwrap_or_else(|_| miner.sensible_gas_price())
|
.unwrap_or_else(|_| miner.sensible_gas_price())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn signer_disabled_error() -> Error {
|
||||||
|
Error {
|
||||||
|
code: ErrorCode::ServerError(error_codes::SIGNER_DISABLED),
|
||||||
|
message: "Trusted Signer is disabled. This API is not available.".into(),
|
||||||
|
data: None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn signing_error(error: AccountError) -> Error {
|
fn signing_error(error: AccountError) -> Error {
|
||||||
Error {
|
Error {
|
||||||
code: ErrorCode::ServerError(error_codes::ACCOUNT_LOCKED),
|
code: ErrorCode::ServerError(error_codes::ACCOUNT_LOCKED),
|
||||||
|
@ -105,10 +105,9 @@ impl<C: 'static, M: 'static> Personal for PersonalClient<C, M> where C: MiningBl
|
|||||||
from_params::<(TransactionRequest, String)>(params)
|
from_params::<(TransactionRequest, String)>(params)
|
||||||
.and_then(|(request, password)| {
|
.and_then(|(request, password)| {
|
||||||
let request: TRequest = request.into();
|
let request: TRequest = request.into();
|
||||||
let sender = request.from;
|
|
||||||
let accounts = take_weak!(self.accounts);
|
let accounts = take_weak!(self.accounts);
|
||||||
|
|
||||||
unlock_sign_and_dispatch(&*take_weak!(self.client), &*take_weak!(self.miner), request, &*accounts, sender, password)
|
unlock_sign_and_dispatch(&*take_weak!(self.client), &*take_weak!(self.miner), request, &*accounts, password)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,9 +22,9 @@ use ethcore::account_provider::AccountProvider;
|
|||||||
use ethcore::client::MiningBlockChainClient;
|
use ethcore::client::MiningBlockChainClient;
|
||||||
use ethcore::miner::MinerService;
|
use ethcore::miner::MinerService;
|
||||||
use v1::traits::PersonalSigner;
|
use v1::traits::PersonalSigner;
|
||||||
use v1::types::{TransactionModification, TransactionConfirmation, U256};
|
use v1::types::{TransactionModification, ConfirmationRequest, U256};
|
||||||
use v1::impls::unlock_sign_and_dispatch;
|
use v1::impls::{unlock_sign_and_dispatch, signature_with_password};
|
||||||
use v1::helpers::{SigningQueue, ConfirmationsQueue};
|
use v1::helpers::{SigningQueue, ConfirmationsQueue, ConfirmationPayload};
|
||||||
|
|
||||||
/// Transactions confirmation (personal) rpc implementation.
|
/// Transactions confirmation (personal) rpc implementation.
|
||||||
pub struct SignerClient<C, M> where C: MiningBlockChainClient, M: MinerService {
|
pub struct SignerClient<C, M> where C: MiningBlockChainClient, M: MinerService {
|
||||||
@ -55,14 +55,16 @@ impl<C: 'static, M: 'static> SignerClient<C, M> where C: MiningBlockChainClient,
|
|||||||
|
|
||||||
impl<C: 'static, M: 'static> PersonalSigner for SignerClient<C, M> where C: MiningBlockChainClient, M: MinerService {
|
impl<C: 'static, M: 'static> PersonalSigner for SignerClient<C, M> where C: MiningBlockChainClient, M: MinerService {
|
||||||
|
|
||||||
fn transactions_to_confirm(&self, _params: Params) -> Result<Value, Error> {
|
fn requests_to_confirm(&self, _params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
let queue = take_weak!(self.queue);
|
let queue = take_weak!(self.queue);
|
||||||
to_value(&queue.requests().into_iter().map(From::from).collect::<Vec<TransactionConfirmation>>())
|
to_value(&queue.requests().into_iter().map(From::from).collect::<Vec<ConfirmationRequest>>())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn confirm_transaction(&self, params: Params) -> Result<Value, Error> {
|
fn confirm_request(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
|
// TODO [ToDr] TransactionModification is redundant for some calls
|
||||||
|
// might be better to replace it in future
|
||||||
from_params::<(U256, TransactionModification, String)>(params).and_then(
|
from_params::<(U256, TransactionModification, String)>(params).and_then(
|
||||||
|(id, modification, pass)| {
|
|(id, modification, pass)| {
|
||||||
let id = id.into();
|
let id = id.into();
|
||||||
@ -70,17 +72,23 @@ impl<C: 'static, M: 'static> PersonalSigner for SignerClient<C, M> where C: Mini
|
|||||||
let queue = take_weak!(self.queue);
|
let queue = take_weak!(self.queue);
|
||||||
let client = take_weak!(self.client);
|
let client = take_weak!(self.client);
|
||||||
let miner = take_weak!(self.miner);
|
let miner = take_weak!(self.miner);
|
||||||
queue.peek(&id).map(|confirmation| {
|
|
||||||
let mut request = confirmation.transaction;
|
|
||||||
// apply modification
|
|
||||||
if let Some(gas_price) = modification.gas_price {
|
|
||||||
request.gas_price = Some(gas_price.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
let sender = request.from;
|
queue.peek(&id).map(|confirmation| {
|
||||||
let result = unlock_sign_and_dispatch(&*client, &*miner, request, &*accounts, sender, pass);
|
let result = match confirmation.payload {
|
||||||
if let Ok(ref hash) = result {
|
ConfirmationPayload::Transaction(mut request) => {
|
||||||
queue.request_confirmed(id, Ok(hash.clone()));
|
// apply modification
|
||||||
|
if let Some(gas_price) = modification.gas_price {
|
||||||
|
request.gas_price = gas_price.into();
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock_sign_and_dispatch(&*client, &*miner, request.into(), &*accounts, pass)
|
||||||
|
},
|
||||||
|
ConfirmationPayload::Sign(address, hash) => {
|
||||||
|
signature_with_password(&*accounts, address, hash, pass)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if let Ok(ref response) = result {
|
||||||
|
queue.request_confirmed(id, Ok(response.clone()));
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}).unwrap_or_else(|| Err(Error::invalid_params()))
|
}).unwrap_or_else(|| Err(Error::invalid_params()))
|
||||||
@ -88,7 +96,7 @@ impl<C: 'static, M: 'static> PersonalSigner for SignerClient<C, M> where C: Mini
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reject_transaction(&self, params: Params) -> Result<Value, Error> {
|
fn reject_request(&self, params: Params) -> Result<Value, Error> {
|
||||||
try!(self.active());
|
try!(self.active());
|
||||||
from_params::<(U256, )>(params).and_then(
|
from_params::<(U256, )>(params).and_then(
|
||||||
|(id, )| {
|
|(id, )| {
|
||||||
|
@ -16,13 +16,14 @@
|
|||||||
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::time::Duration;
|
||||||
use jsonrpc_core::IoHandler;
|
use jsonrpc_core::IoHandler;
|
||||||
use v1::impls::EthSigningQueueClient;
|
use v1::impls::EthSigningQueueClient;
|
||||||
use v1::traits::EthSigning;
|
use v1::traits::EthSigning;
|
||||||
use v1::helpers::{ConfirmationsQueue, SigningQueue};
|
use v1::helpers::{ConfirmationsQueue, SigningQueue};
|
||||||
use v1::tests::helpers::TestMinerService;
|
use v1::tests::helpers::TestMinerService;
|
||||||
use util::{Address, FixedHash};
|
use util::{Address, FixedHash};
|
||||||
use util::numbers::{Uint, U256};
|
use util::numbers::{Uint, U256, H256};
|
||||||
use ethcore::account_provider::AccountProvider;
|
use ethcore::account_provider::AccountProvider;
|
||||||
use ethcore::client::TestBlockChainClient;
|
use ethcore::client::TestBlockChainClient;
|
||||||
use ethcore::transaction::{Transaction, Action};
|
use ethcore::transaction::{Transaction, Action};
|
||||||
@ -37,7 +38,7 @@ struct EthSigningTester {
|
|||||||
|
|
||||||
impl Default for EthSigningTester {
|
impl Default for EthSigningTester {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let queue = Arc::new(ConfirmationsQueue::default());
|
let queue = Arc::new(ConfirmationsQueue::with_timeout(Duration::from_millis(1)));
|
||||||
let client = Arc::new(TestBlockChainClient::default());
|
let client = Arc::new(TestBlockChainClient::default());
|
||||||
let miner = Arc::new(TestMinerService::default());
|
let miner = Arc::new(TestMinerService::default());
|
||||||
let accounts = Arc::new(AccountProvider::transient_provider());
|
let accounts = Arc::new(AccountProvider::transient_provider());
|
||||||
@ -58,6 +59,78 @@ fn eth_signing() -> EthSigningTester {
|
|||||||
EthSigningTester::default()
|
EthSigningTester::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_add_sign_to_queue() {
|
||||||
|
// given
|
||||||
|
let tester = eth_signing();
|
||||||
|
let address = Address::random();
|
||||||
|
assert_eq!(tester.queue.requests().len(), 0);
|
||||||
|
|
||||||
|
// when
|
||||||
|
let request = r#"{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "eth_sign",
|
||||||
|
"params": [
|
||||||
|
""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"",
|
||||||
|
"0x0000000000000000000000000000000000000000000000000000000000000005"
|
||||||
|
],
|
||||||
|
"id": 1
|
||||||
|
}"#;
|
||||||
|
let response = r#"{"jsonrpc":"2.0","result":"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","id":1}"#;
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
|
||||||
|
assert_eq!(tester.queue.requests().len(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_post_sign_to_queue() {
|
||||||
|
// given
|
||||||
|
let tester = eth_signing();
|
||||||
|
let address = Address::random();
|
||||||
|
assert_eq!(tester.queue.requests().len(), 0);
|
||||||
|
|
||||||
|
// when
|
||||||
|
let request = r#"{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "eth_postSign",
|
||||||
|
"params": [
|
||||||
|
""#.to_owned() + format!("0x{:?}", address).as_ref() + r#"",
|
||||||
|
"0x0000000000000000000000000000000000000000000000000000000000000005"
|
||||||
|
],
|
||||||
|
"id": 1
|
||||||
|
}"#;
|
||||||
|
let response = r#"{"jsonrpc":"2.0","result":"0x01","id":1}"#;
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
|
||||||
|
assert_eq!(tester.queue.requests().len(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_sign_if_account_is_unlocked() {
|
||||||
|
// given
|
||||||
|
let tester = eth_signing();
|
||||||
|
let hash: H256 = 5.into();
|
||||||
|
let acc = tester.accounts.new_account("test").unwrap();
|
||||||
|
tester.accounts.unlock_account_permanently(acc, "test".into()).unwrap();
|
||||||
|
|
||||||
|
let signature = tester.accounts.sign(acc, hash).unwrap();
|
||||||
|
|
||||||
|
// when
|
||||||
|
let request = r#"{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "eth_sign",
|
||||||
|
"params": [
|
||||||
|
""#.to_owned() + format!("0x{:?}", acc).as_ref() + r#"",
|
||||||
|
""# + format!("0x{:?}", hash).as_ref() + r#""
|
||||||
|
],
|
||||||
|
"id": 1
|
||||||
|
}"#;
|
||||||
|
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + format!("0x{:?}", signature).as_ref() + r#"","id":1}"#;
|
||||||
|
assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
|
||||||
|
assert_eq!(tester.queue.requests().len(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_add_transaction_to_queue() {
|
fn should_add_transaction_to_queue() {
|
||||||
@ -81,7 +154,6 @@ fn should_add_transaction_to_queue() {
|
|||||||
}"#;
|
}"#;
|
||||||
let response = r#"{"jsonrpc":"2.0","result":"0x0000000000000000000000000000000000000000000000000000000000000000","id":1}"#;
|
let response = r#"{"jsonrpc":"2.0","result":"0x0000000000000000000000000000000000000000000000000000000000000000","id":1}"#;
|
||||||
|
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
|
assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
|
||||||
assert_eq!(tester.queue.requests().len(), 1);
|
assert_eq!(tester.queue.requests().len(), 1);
|
||||||
|
@ -23,7 +23,7 @@ use ethcore::client::TestBlockChainClient;
|
|||||||
use ethcore::transaction::{Transaction, Action};
|
use ethcore::transaction::{Transaction, Action};
|
||||||
use v1::{SignerClient, PersonalSigner};
|
use v1::{SignerClient, PersonalSigner};
|
||||||
use v1::tests::helpers::TestMinerService;
|
use v1::tests::helpers::TestMinerService;
|
||||||
use v1::helpers::{SigningQueue, ConfirmationsQueue, TransactionRequest};
|
use v1::helpers::{SigningQueue, ConfirmationsQueue, FilledTransactionRequest, ConfirmationPayload};
|
||||||
|
|
||||||
struct PersonalSignerTester {
|
struct PersonalSignerTester {
|
||||||
queue: Arc<ConfirmationsQueue>,
|
queue: Arc<ConfirmationsQueue>,
|
||||||
@ -68,22 +68,28 @@ fn signer_tester() -> PersonalSignerTester {
|
|||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn should_return_list_of_transactions_in_queue() {
|
fn should_return_list_of_items_to_confirm() {
|
||||||
// given
|
// given
|
||||||
let tester = signer_tester();
|
let tester = signer_tester();
|
||||||
tester.queue.add_request(TransactionRequest {
|
tester.queue.add_request(ConfirmationPayload::Transaction(FilledTransactionRequest {
|
||||||
from: Address::from(1),
|
from: Address::from(1),
|
||||||
to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
|
to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
|
||||||
gas_price: Some(U256::from(10_000)),
|
gas_price: U256::from(10_000),
|
||||||
gas: Some(U256::from(10_000_000)),
|
gas: U256::from(10_000_000),
|
||||||
value: Some(U256::from(1)),
|
value: U256::from(1),
|
||||||
data: None,
|
data: vec![],
|
||||||
nonce: None,
|
nonce: None,
|
||||||
});
|
}));
|
||||||
|
tester.queue.add_request(ConfirmationPayload::Sign(1.into(), 5.into()));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let request = r#"{"jsonrpc":"2.0","method":"personal_transactionsToConfirm","params":[],"id":1}"#;
|
let request = r#"{"jsonrpc":"2.0","method":"personal_requestsToConfirm","params":[],"id":1}"#;
|
||||||
let response = r#"{"jsonrpc":"2.0","result":[{"id":"0x01","transaction":{"data":null,"from":"0x0000000000000000000000000000000000000001","gas":"0x989680","gasPrice":"0x2710","nonce":null,"to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","value":"0x01"}}],"id":1}"#;
|
let response = concat!(
|
||||||
|
r#"{"jsonrpc":"2.0","result":["#,
|
||||||
|
r#"{"id":"0x01","payload":{"transaction":{"data":"0x","from":"0x0000000000000000000000000000000000000001","gas":"0x989680","gasPrice":"0x2710","nonce":null,"to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","value":"0x01"}}},"#,
|
||||||
|
r#"{"id":"0x02","payload":{"sign":{"address":"0x0000000000000000000000000000000000000001","hash":"0x0000000000000000000000000000000000000000000000000000000000000005"}}}"#,
|
||||||
|
r#"],"id":1}"#
|
||||||
|
);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
|
assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
|
||||||
@ -94,19 +100,19 @@ fn should_return_list_of_transactions_in_queue() {
|
|||||||
fn should_reject_transaction_from_queue_without_dispatching() {
|
fn should_reject_transaction_from_queue_without_dispatching() {
|
||||||
// given
|
// given
|
||||||
let tester = signer_tester();
|
let tester = signer_tester();
|
||||||
tester.queue.add_request(TransactionRequest {
|
tester.queue.add_request(ConfirmationPayload::Transaction(FilledTransactionRequest {
|
||||||
from: Address::from(1),
|
from: Address::from(1),
|
||||||
to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
|
to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
|
||||||
gas_price: Some(U256::from(10_000)),
|
gas_price: U256::from(10_000),
|
||||||
gas: Some(U256::from(10_000_000)),
|
gas: U256::from(10_000_000),
|
||||||
value: Some(U256::from(1)),
|
value: U256::from(1),
|
||||||
data: None,
|
data: vec![],
|
||||||
nonce: None,
|
nonce: None,
|
||||||
});
|
}));
|
||||||
assert_eq!(tester.queue.requests().len(), 1);
|
assert_eq!(tester.queue.requests().len(), 1);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let request = r#"{"jsonrpc":"2.0","method":"personal_rejectTransaction","params":["0x01"],"id":1}"#;
|
let request = r#"{"jsonrpc":"2.0","method":"personal_rejectRequest","params":["0x01"],"id":1}"#;
|
||||||
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
|
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
|
||||||
|
|
||||||
// then
|
// then
|
||||||
@ -119,19 +125,35 @@ fn should_reject_transaction_from_queue_without_dispatching() {
|
|||||||
fn should_not_remove_transaction_if_password_is_invalid() {
|
fn should_not_remove_transaction_if_password_is_invalid() {
|
||||||
// given
|
// given
|
||||||
let tester = signer_tester();
|
let tester = signer_tester();
|
||||||
tester.queue.add_request(TransactionRequest {
|
tester.queue.add_request(ConfirmationPayload::Transaction(FilledTransactionRequest {
|
||||||
from: Address::from(1),
|
from: Address::from(1),
|
||||||
to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
|
to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
|
||||||
gas_price: Some(U256::from(10_000)),
|
gas_price: U256::from(10_000),
|
||||||
gas: Some(U256::from(10_000_000)),
|
gas: U256::from(10_000_000),
|
||||||
value: Some(U256::from(1)),
|
value: U256::from(1),
|
||||||
data: None,
|
data: vec![],
|
||||||
nonce: None,
|
nonce: None,
|
||||||
});
|
}));
|
||||||
assert_eq!(tester.queue.requests().len(), 1);
|
assert_eq!(tester.queue.requests().len(), 1);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let request = r#"{"jsonrpc":"2.0","method":"personal_confirmTransaction","params":["0x01",{},"xxx"],"id":1}"#;
|
let request = r#"{"jsonrpc":"2.0","method":"personal_confirmRequest","params":["0x01",{},"xxx"],"id":1}"#;
|
||||||
|
let response = r#"{"jsonrpc":"2.0","error":{"code":-32021,"message":"Account password is invalid or account does not exist.","data":"SStore(InvalidAccount)"},"id":1}"#;
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(tester.io.handle_request(&request), Some(response.to_owned()));
|
||||||
|
assert_eq!(tester.queue.requests().len(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_not_remove_sign_if_password_is_invalid() {
|
||||||
|
// given
|
||||||
|
let tester = signer_tester();
|
||||||
|
tester.queue.add_request(ConfirmationPayload::Sign(0.into(), 5.into()));
|
||||||
|
assert_eq!(tester.queue.requests().len(), 1);
|
||||||
|
|
||||||
|
// when
|
||||||
|
let request = r#"{"jsonrpc":"2.0","method":"personal_confirmRequest","params":["0x01",{},"xxx"],"id":1}"#;
|
||||||
let response = r#"{"jsonrpc":"2.0","error":{"code":-32021,"message":"Account password is invalid or account does not exist.","data":"SStore(InvalidAccount)"},"id":1}"#;
|
let response = r#"{"jsonrpc":"2.0","error":{"code":-32021,"message":"Account password is invalid or account does not exist.","data":"SStore(InvalidAccount)"},"id":1}"#;
|
||||||
|
|
||||||
// then
|
// then
|
||||||
@ -145,15 +167,15 @@ fn should_confirm_transaction_and_dispatch() {
|
|||||||
let tester = signer_tester();
|
let tester = signer_tester();
|
||||||
let address = tester.accounts.new_account("test").unwrap();
|
let address = tester.accounts.new_account("test").unwrap();
|
||||||
let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap();
|
let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap();
|
||||||
tester.queue.add_request(TransactionRequest {
|
tester.queue.add_request(ConfirmationPayload::Transaction(FilledTransactionRequest {
|
||||||
from: address,
|
from: address,
|
||||||
to: Some(recipient),
|
to: Some(recipient),
|
||||||
gas_price: Some(U256::from(10_000)),
|
gas_price: U256::from(10_000),
|
||||||
gas: Some(U256::from(10_000_000)),
|
gas: U256::from(10_000_000),
|
||||||
value: Some(U256::from(1)),
|
value: U256::from(1),
|
||||||
data: None,
|
data: vec![],
|
||||||
nonce: None,
|
nonce: None,
|
||||||
});
|
}));
|
||||||
|
|
||||||
let t = Transaction {
|
let t = Transaction {
|
||||||
nonce: U256::zero(),
|
nonce: U256::zero(),
|
||||||
@ -172,7 +194,7 @@ fn should_confirm_transaction_and_dispatch() {
|
|||||||
// when
|
// when
|
||||||
let request = r#"{
|
let request = r#"{
|
||||||
"jsonrpc":"2.0",
|
"jsonrpc":"2.0",
|
||||||
"method":"personal_confirmTransaction",
|
"method":"personal_confirmRequest",
|
||||||
"params":["0x01", {"gasPrice":"0x1000"}, "test"],
|
"params":["0x01", {"gasPrice":"0x1000"}, "test"],
|
||||||
"id":1
|
"id":1
|
||||||
}"#;
|
}"#;
|
||||||
|
@ -206,26 +206,31 @@ pub trait EthSigning: Sized + Send + Sync + 'static {
|
|||||||
/// Signs the data with given address signature.
|
/// Signs the data with given address signature.
|
||||||
fn sign(&self, _: Params) -> Result<Value, Error>;
|
fn sign(&self, _: Params) -> Result<Value, Error>;
|
||||||
|
|
||||||
|
/// Posts sign request asynchronously.
|
||||||
|
/// Will return a confirmation ID for later use with check_transaction.
|
||||||
|
fn post_sign(&self, _: Params) -> Result<Value, Error>;
|
||||||
|
|
||||||
/// Sends transaction; will block for 20s to try to return the
|
/// Sends transaction; will block for 20s to try to return the
|
||||||
/// transaction hash.
|
/// transaction hash.
|
||||||
/// If it cannot yet be signed, it will return a transaction ID for
|
/// If it cannot yet be signed, it will return a transaction ID for
|
||||||
/// later use with check_transaction.
|
/// later use with check_transaction.
|
||||||
fn send_transaction(&self, _: Params) -> Result<Value, Error>;
|
fn send_transaction(&self, _: Params) -> Result<Value, Error>;
|
||||||
|
|
||||||
/// Posts transaction asynchronously.
|
/// Posts transaction asynchronously.
|
||||||
/// Will return a transaction ID for later use with check_transaction.
|
/// Will return a transaction ID for later use with check_transaction.
|
||||||
fn post_transaction(&self, _: Params) -> Result<Value, Error>;
|
fn post_transaction(&self, _: Params) -> Result<Value, Error>;
|
||||||
|
|
||||||
/// Checks the progress of a previously posted transaction.
|
/// Checks the progress of a previously posted transaction.
|
||||||
/// Should be given a valid send_transaction ID.
|
/// Should be given a valid send_transaction ID.
|
||||||
/// Returns the transaction hash, the zero hash (not yet available),
|
/// Returns the transaction hash, the zero hash (not yet available),
|
||||||
/// or an error.
|
/// or an error.
|
||||||
fn check_transaction(&self, _: Params) -> Result<Value, Error>;
|
fn check_transaction(&self, _: Params) -> Result<Value, Error>;
|
||||||
|
|
||||||
/// Should be used to convert object to io delegate.
|
/// Should be used to convert object to io delegate.
|
||||||
fn to_delegate(self) -> IoDelegate<Self> {
|
fn to_delegate(self) -> IoDelegate<Self> {
|
||||||
let mut delegate = IoDelegate::new(Arc::new(self));
|
let mut delegate = IoDelegate::new(Arc::new(self));
|
||||||
delegate.add_method("eth_sign", EthSigning::sign);
|
delegate.add_method("eth_sign", EthSigning::sign);
|
||||||
|
delegate.add_method("eth_postSign", EthSigning::post_sign);
|
||||||
delegate.add_method("eth_sendTransaction", EthSigning::send_transaction);
|
delegate.add_method("eth_sendTransaction", EthSigning::send_transaction);
|
||||||
delegate.add_method("eth_postTransaction", EthSigning::post_transaction);
|
delegate.add_method("eth_postTransaction", EthSigning::post_transaction);
|
||||||
delegate.add_method("eth_checkTransaction", EthSigning::check_transaction);
|
delegate.add_method("eth_checkTransaction", EthSigning::check_transaction);
|
||||||
|
@ -61,24 +61,24 @@ pub trait Personal: Sized + Send + Sync + 'static {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Personal extension for transactions confirmations rpc interface.
|
/// Personal extension for confirmations rpc interface.
|
||||||
pub trait PersonalSigner: Sized + Send + Sync + 'static {
|
pub trait PersonalSigner: Sized + Send + Sync + 'static {
|
||||||
|
|
||||||
/// Returns a list of transactions to confirm.
|
/// Returns a list of items to confirm.
|
||||||
fn transactions_to_confirm(&self, _: Params) -> Result<Value, Error>;
|
fn requests_to_confirm(&self, _: Params) -> Result<Value, Error>;
|
||||||
|
|
||||||
/// Confirm and send a specific transaction.
|
/// Confirm specific request.
|
||||||
fn confirm_transaction(&self, _: Params) -> Result<Value, Error>;
|
fn confirm_request(&self, _: Params) -> Result<Value, Error>;
|
||||||
|
|
||||||
/// Reject the transaction request.
|
/// Reject the confirmation request.
|
||||||
fn reject_transaction(&self, _: Params) -> Result<Value, Error>;
|
fn reject_request(&self, _: Params) -> Result<Value, Error>;
|
||||||
|
|
||||||
/// Should be used to convert object to io delegate.
|
/// Should be used to convert object to io delegate.
|
||||||
fn to_delegate(self) -> IoDelegate<Self> {
|
fn to_delegate(self) -> IoDelegate<Self> {
|
||||||
let mut delegate = IoDelegate::new(Arc::new(self));
|
let mut delegate = IoDelegate::new(Arc::new(self));
|
||||||
delegate.add_method("personal_transactionsToConfirm", PersonalSigner::transactions_to_confirm);
|
delegate.add_method("personal_requestsToConfirm", PersonalSigner::requests_to_confirm);
|
||||||
delegate.add_method("personal_confirmTransaction", PersonalSigner::confirm_transaction);
|
delegate.add_method("personal_confirmRequest", PersonalSigner::confirm_request);
|
||||||
delegate.add_method("personal_rejectTransaction", PersonalSigner::reject_transaction);
|
delegate.add_method("personal_rejectRequest", PersonalSigner::reject_request);
|
||||||
delegate
|
delegate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
150
rpc/src/v1/types/confirmations.rs
Normal file
150
rpc/src/v1/types/confirmations.rs
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Types used in Confirmations queue (Trusted Signer)
|
||||||
|
|
||||||
|
use v1::types::{U256, TransactionRequest, H160, H256};
|
||||||
|
use v1::helpers;
|
||||||
|
|
||||||
|
|
||||||
|
/// Confirmation waiting in a queue
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize)]
|
||||||
|
pub struct ConfirmationRequest {
|
||||||
|
/// Id of this confirmation
|
||||||
|
pub id: U256,
|
||||||
|
/// Payload
|
||||||
|
pub payload: ConfirmationPayload,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<helpers::ConfirmationRequest> for ConfirmationRequest {
|
||||||
|
fn from(c: helpers::ConfirmationRequest) -> Self {
|
||||||
|
ConfirmationRequest {
|
||||||
|
id: c.id.into(),
|
||||||
|
payload: c.payload.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sign request
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize)]
|
||||||
|
pub struct SignRequest {
|
||||||
|
/// Address
|
||||||
|
pub address: H160,
|
||||||
|
/// Hash to sign
|
||||||
|
pub hash: H256,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Confirmation payload, i.e. the thing to be confirmed
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize)]
|
||||||
|
pub enum ConfirmationPayload {
|
||||||
|
/// Transaction
|
||||||
|
#[serde(rename="transaction")]
|
||||||
|
Transaction(TransactionRequest),
|
||||||
|
/// Signature
|
||||||
|
#[serde(rename="sign")]
|
||||||
|
Sign(SignRequest),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<helpers::ConfirmationPayload> for ConfirmationPayload {
|
||||||
|
fn from(c: helpers::ConfirmationPayload) -> Self {
|
||||||
|
match c {
|
||||||
|
helpers::ConfirmationPayload::Transaction(t) => ConfirmationPayload::Transaction(t.into()),
|
||||||
|
helpers::ConfirmationPayload::Sign(address, hash) => ConfirmationPayload::Sign(SignRequest {
|
||||||
|
address: address.into(),
|
||||||
|
hash: hash.into(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Possible modifications to the confirmed transaction sent by `Trusted Signer`
|
||||||
|
#[derive(Debug, PartialEq, Deserialize)]
|
||||||
|
pub struct TransactionModification {
|
||||||
|
/// Modified gas price
|
||||||
|
#[serde(rename="gasPrice")]
|
||||||
|
pub gas_price: Option<U256>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::str::FromStr;
|
||||||
|
use serde_json;
|
||||||
|
use v1::types::U256;
|
||||||
|
use v1::helpers;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_serialize_sign_confirmation() {
|
||||||
|
// given
|
||||||
|
let request = helpers::ConfirmationRequest {
|
||||||
|
id: 15.into(),
|
||||||
|
payload: helpers::ConfirmationPayload::Sign(1.into(), 5.into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
// when
|
||||||
|
let res = serde_json::to_string(&ConfirmationRequest::from(request));
|
||||||
|
let expected = r#"{"id":"0x0f","payload":{"sign":{"address":"0x0000000000000000000000000000000000000001","hash":"0x0000000000000000000000000000000000000000000000000000000000000005"}}}"#;
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(res.unwrap(), expected.to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_serialize_transaction_confirmation() {
|
||||||
|
// given
|
||||||
|
let request = helpers::ConfirmationRequest {
|
||||||
|
id: 15.into(),
|
||||||
|
payload: helpers::ConfirmationPayload::Transaction(helpers::FilledTransactionRequest {
|
||||||
|
from: 0.into(),
|
||||||
|
to: None,
|
||||||
|
gas: 15_000.into(),
|
||||||
|
gas_price: 10_000.into(),
|
||||||
|
value: 100_000.into(),
|
||||||
|
data: vec![1, 2, 3],
|
||||||
|
nonce: Some(1.into()),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
// when
|
||||||
|
let res = serde_json::to_string(&ConfirmationRequest::from(request));
|
||||||
|
let expected = r#"{"id":"0x0f","payload":{"transaction":{"from":"0x0000000000000000000000000000000000000000","to":null,"gasPrice":"0x2710","gas":"0x3a98","value":"0x0186a0","data":"0x010203","nonce":"0x01"}}}"#;
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(res.unwrap(), expected.to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_deserialize_modification() {
|
||||||
|
// given
|
||||||
|
let s1 = r#"{
|
||||||
|
"gasPrice":"0x0ba43b7400"
|
||||||
|
}"#;
|
||||||
|
let s2 = r#"{}"#;
|
||||||
|
|
||||||
|
// when
|
||||||
|
let res1: TransactionModification = serde_json::from_str(s1).unwrap();
|
||||||
|
let res2: TransactionModification = serde_json::from_str(s2).unwrap();
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(res1, TransactionModification {
|
||||||
|
gas_price: Some(U256::from_str("0ba43b7400").unwrap()),
|
||||||
|
});
|
||||||
|
assert_eq!(res2, TransactionModification {
|
||||||
|
gas_price: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -17,6 +17,8 @@
|
|||||||
mod bytes;
|
mod bytes;
|
||||||
mod block;
|
mod block;
|
||||||
mod block_number;
|
mod block_number;
|
||||||
|
mod call_request;
|
||||||
|
mod confirmations;
|
||||||
mod filter;
|
mod filter;
|
||||||
mod hash;
|
mod hash;
|
||||||
mod index;
|
mod index;
|
||||||
@ -24,7 +26,6 @@ mod log;
|
|||||||
mod sync;
|
mod sync;
|
||||||
mod transaction;
|
mod transaction;
|
||||||
mod transaction_request;
|
mod transaction_request;
|
||||||
mod call_request;
|
|
||||||
mod receipt;
|
mod receipt;
|
||||||
mod trace;
|
mod trace;
|
||||||
mod trace_filter;
|
mod trace_filter;
|
||||||
@ -33,14 +34,15 @@ mod uint;
|
|||||||
pub use self::bytes::Bytes;
|
pub use self::bytes::Bytes;
|
||||||
pub use self::block::{Block, BlockTransactions};
|
pub use self::block::{Block, BlockTransactions};
|
||||||
pub use self::block_number::BlockNumber;
|
pub use self::block_number::BlockNumber;
|
||||||
|
pub use self::call_request::CallRequest;
|
||||||
|
pub use self::confirmations::{ConfirmationPayload, ConfirmationRequest, TransactionModification};
|
||||||
pub use self::filter::Filter;
|
pub use self::filter::Filter;
|
||||||
pub use self::hash::{H64, H160, H256, H520, H2048};
|
pub use self::hash::{H64, H160, H256, H520, H2048};
|
||||||
pub use self::index::Index;
|
pub use self::index::Index;
|
||||||
pub use self::log::Log;
|
pub use self::log::Log;
|
||||||
pub use self::sync::{SyncStatus, SyncInfo};
|
pub use self::sync::{SyncStatus, SyncInfo};
|
||||||
pub use self::transaction::Transaction;
|
pub use self::transaction::Transaction;
|
||||||
pub use self::transaction_request::{TransactionRequest, TransactionConfirmation, TransactionModification};
|
pub use self::transaction_request::TransactionRequest;
|
||||||
pub use self::call_request::CallRequest;
|
|
||||||
pub use self::receipt::Receipt;
|
pub use self::receipt::Receipt;
|
||||||
pub use self::trace::{LocalizedTrace, TraceResults};
|
pub use self::trace::{LocalizedTrace, TraceResults};
|
||||||
pub use self::trace_filter::TraceFilter;
|
pub use self::trace_filter::TraceFilter;
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
//! `TransactionRequest` type
|
//! `TransactionRequest` type
|
||||||
|
|
||||||
use v1::types::{Bytes, H160, U256};
|
use v1::types::{Bytes, H160, U256};
|
||||||
use v1::helpers::{TransactionRequest as Request, TransactionConfirmation as Confirmation};
|
use v1::helpers;
|
||||||
|
|
||||||
/// Transaction request coming from RPC
|
/// Transaction request coming from RPC
|
||||||
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
||||||
@ -39,8 +39,8 @@ pub struct TransactionRequest {
|
|||||||
pub nonce: Option<U256>,
|
pub nonce: Option<U256>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Request> for TransactionRequest {
|
impl From<helpers::TransactionRequest> for TransactionRequest {
|
||||||
fn from(r: Request) -> Self {
|
fn from(r: helpers::TransactionRequest) -> Self {
|
||||||
TransactionRequest {
|
TransactionRequest {
|
||||||
from: r.from.into(),
|
from: r.from.into(),
|
||||||
to: r.to.map(Into::into),
|
to: r.to.map(Into::into),
|
||||||
@ -53,9 +53,23 @@ impl From<Request> for TransactionRequest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<Request> for TransactionRequest {
|
impl From<helpers::FilledTransactionRequest> for TransactionRequest {
|
||||||
fn into(self) -> Request {
|
fn from(r: helpers::FilledTransactionRequest) -> Self {
|
||||||
Request {
|
TransactionRequest {
|
||||||
|
from: r.from.into(),
|
||||||
|
to: r.to.map(Into::into),
|
||||||
|
gas_price: Some(r.gas_price.into()),
|
||||||
|
gas: Some(r.gas.into()),
|
||||||
|
value: Some(r.value.into()),
|
||||||
|
data: Some(r.data.into()),
|
||||||
|
nonce: r.nonce.map(Into::into),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<helpers::TransactionRequest> for TransactionRequest {
|
||||||
|
fn into(self) -> helpers::TransactionRequest {
|
||||||
|
helpers::TransactionRequest {
|
||||||
from: self.from.into(),
|
from: self.from.into(),
|
||||||
to: self.to.map(Into::into),
|
to: self.to.map(Into::into),
|
||||||
gas_price: self.gas_price.map(Into::into),
|
gas_price: self.gas_price.map(Into::into),
|
||||||
@ -67,32 +81,6 @@ impl Into<Request> for TransactionRequest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Transaction confirmation waiting in a queue
|
|
||||||
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash, Serialize)]
|
|
||||||
pub struct TransactionConfirmation {
|
|
||||||
/// Id of this confirmation
|
|
||||||
pub id: U256,
|
|
||||||
/// TransactionRequest
|
|
||||||
pub transaction: TransactionRequest,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Confirmation> for TransactionConfirmation {
|
|
||||||
fn from(c: Confirmation) -> Self {
|
|
||||||
TransactionConfirmation {
|
|
||||||
id: c.id.into(),
|
|
||||||
transaction: c.transaction.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Possible modifications to the confirmed transaction sent by `SignerUI`
|
|
||||||
#[derive(Debug, PartialEq, Deserialize)]
|
|
||||||
pub struct TransactionModification {
|
|
||||||
/// Modified gas price
|
|
||||||
#[serde(rename="gasPrice")]
|
|
||||||
pub gas_price: Option<U256>,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
@ -188,7 +176,6 @@ mod tests {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn transaction_request_deserialize_error() {
|
fn transaction_request_deserialize_error() {
|
||||||
let s = r#"{
|
let s = r#"{
|
||||||
@ -203,26 +190,5 @@ mod tests {
|
|||||||
|
|
||||||
assert!(deserialized.is_err(), "Should be error because to is empty");
|
assert!(deserialized.is_err(), "Should be error because to is empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_deserialize_modification() {
|
|
||||||
// given
|
|
||||||
let s1 = r#"{
|
|
||||||
"gasPrice":"0x0ba43b7400"
|
|
||||||
}"#;
|
|
||||||
let s2 = r#"{}"#;
|
|
||||||
|
|
||||||
// when
|
|
||||||
let res1: TransactionModification = serde_json::from_str(s1).unwrap();
|
|
||||||
let res2: TransactionModification = serde_json::from_str(s2).unwrap();
|
|
||||||
|
|
||||||
// then
|
|
||||||
assert_eq!(res1, TransactionModification {
|
|
||||||
gas_price: Some(U256::from_str("0ba43b7400").unwrap()),
|
|
||||||
});
|
|
||||||
assert_eq!(res2, TransactionModification {
|
|
||||||
gas_price: None,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user