2017-01-25 18:51:41 +01:00
|
|
|
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
2016-02-09 12:27:05 +01:00
|
|
|
// 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/>.
|
|
|
|
|
2016-11-16 17:54:54 +01:00
|
|
|
use serde::{Serialize, Serializer};
|
2017-02-13 16:38:47 +01:00
|
|
|
use serde::ser::SerializeStruct;
|
2016-11-16 17:54:54 +01:00
|
|
|
use ethcore::miner;
|
2017-04-19 14:30:00 +02:00
|
|
|
use ethcore::{contract_address, CreateContractAddress};
|
2016-12-15 18:19:19 +01:00
|
|
|
use ethcore::transaction::{LocalizedTransaction, Action, PendingTransaction, SignedTransaction};
|
2016-11-16 17:54:54 +01:00
|
|
|
use v1::helpers::errors;
|
2017-02-03 19:32:10 +01:00
|
|
|
use v1::types::{Bytes, H160, H256, U256, H512, TransactionCondition};
|
2016-02-08 11:58:47 +01:00
|
|
|
|
2016-05-28 19:30:31 +02:00
|
|
|
/// Transaction
|
2016-11-18 11:03:29 +01:00
|
|
|
#[derive(Debug, Default, Clone, PartialEq, Serialize)]
|
2016-02-08 11:58:47 +01:00
|
|
|
pub struct Transaction {
|
2016-05-28 19:30:31 +02:00
|
|
|
/// Hash
|
2016-02-09 13:17:44 +01:00
|
|
|
pub hash: H256,
|
2016-05-28 19:30:31 +02:00
|
|
|
/// Nonce
|
2016-02-09 13:17:44 +01:00
|
|
|
pub nonce: U256,
|
2016-05-28 19:30:31 +02:00
|
|
|
/// Block hash
|
2016-02-08 11:58:47 +01:00
|
|
|
#[serde(rename="blockHash")]
|
2016-07-06 11:23:29 +02:00
|
|
|
pub block_hash: Option<H256>,
|
2016-05-28 19:30:31 +02:00
|
|
|
/// Block number
|
2016-02-08 11:58:47 +01:00
|
|
|
#[serde(rename="blockNumber")]
|
2016-07-06 11:23:29 +02:00
|
|
|
pub block_number: Option<U256>,
|
2016-05-28 19:30:31 +02:00
|
|
|
/// Transaction Index
|
2016-02-08 11:58:47 +01:00
|
|
|
#[serde(rename="transactionIndex")]
|
2016-07-06 11:23:29 +02:00
|
|
|
pub transaction_index: Option<U256>,
|
2016-05-28 19:30:31 +02:00
|
|
|
/// Sender
|
2016-07-06 11:23:29 +02:00
|
|
|
pub from: H160,
|
2016-05-28 19:30:31 +02:00
|
|
|
/// Recipient
|
2016-07-06 11:23:29 +02:00
|
|
|
pub to: Option<H160>,
|
2016-05-28 19:30:31 +02:00
|
|
|
/// Transfered value
|
2016-02-09 13:17:44 +01:00
|
|
|
pub value: U256,
|
2016-05-28 19:30:31 +02:00
|
|
|
/// Gas Price
|
2016-02-08 11:58:47 +01:00
|
|
|
#[serde(rename="gasPrice")]
|
2016-02-09 13:17:44 +01:00
|
|
|
pub gas_price: U256,
|
2016-05-28 19:30:31 +02:00
|
|
|
/// Gas
|
2016-02-09 13:17:44 +01:00
|
|
|
pub gas: U256,
|
2016-05-28 19:30:31 +02:00
|
|
|
/// Data
|
2016-06-02 12:40:31 +02:00
|
|
|
pub input: Bytes,
|
|
|
|
/// Creates contract
|
2016-07-06 11:23:29 +02:00
|
|
|
pub creates: Option<H160>,
|
2016-07-26 16:48:50 +02:00
|
|
|
/// Raw transaction data
|
|
|
|
pub raw: Bytes,
|
2016-09-22 14:48:22 +02:00
|
|
|
/// Public key of the signer.
|
|
|
|
#[serde(rename="publicKey")]
|
|
|
|
pub public_key: Option<H512>,
|
2016-11-27 14:11:37 +01:00
|
|
|
/// The network id of the transaction, if any.
|
2017-08-21 13:46:58 +02:00
|
|
|
#[serde(rename="chainId")]
|
|
|
|
pub chain_id: Option<u64>,
|
2016-11-27 14:11:37 +01:00
|
|
|
/// The standardised V field of the signature (0 or 1).
|
|
|
|
#[serde(rename="standardV")]
|
2016-11-29 13:46:06 +01:00
|
|
|
pub standard_v: U256,
|
2016-11-27 14:11:37 +01:00
|
|
|
/// The standardised V field of the signature.
|
2016-11-29 13:46:06 +01:00
|
|
|
pub v: U256,
|
2016-11-04 12:05:10 +01:00
|
|
|
/// The R field of the signature.
|
2016-11-27 14:11:37 +01:00
|
|
|
pub r: U256,
|
2016-11-04 12:05:10 +01:00
|
|
|
/// The S field of the signature.
|
2016-11-27 14:11:37 +01:00
|
|
|
pub s: U256,
|
2016-12-15 18:19:19 +01:00
|
|
|
/// Transaction activates at specified block.
|
2017-02-03 19:32:10 +01:00
|
|
|
pub condition: Option<TransactionCondition>,
|
2016-02-08 11:58:47 +01:00
|
|
|
}
|
|
|
|
|
2016-11-16 17:54:54 +01:00
|
|
|
/// Local Transaction Status
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum LocalTransactionStatus {
|
|
|
|
/// Transaction is pending
|
|
|
|
Pending,
|
|
|
|
/// Transaction is in future part of the queue
|
|
|
|
Future,
|
|
|
|
/// Transaction is already mined.
|
|
|
|
Mined(Transaction),
|
|
|
|
/// Transaction was dropped because of limit.
|
|
|
|
Dropped(Transaction),
|
|
|
|
/// Transaction was replaced by transaction with higher gas price.
|
|
|
|
Replaced(Transaction, U256, H256),
|
|
|
|
/// Transaction never got into the queue.
|
|
|
|
Rejected(Transaction, String),
|
|
|
|
/// Transaction is invalid.
|
|
|
|
Invalid(Transaction),
|
2017-03-29 14:43:55 +02:00
|
|
|
/// Transaction was canceled.
|
|
|
|
Canceled(Transaction),
|
2016-11-16 17:54:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Serialize for LocalTransactionStatus {
|
2017-02-13 16:38:47 +01:00
|
|
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
2016-11-16 17:54:54 +01:00
|
|
|
where S: Serializer
|
|
|
|
{
|
|
|
|
use self::LocalTransactionStatus::*;
|
|
|
|
|
|
|
|
let elems = match *self {
|
|
|
|
Pending | Future => 1,
|
2017-03-29 14:43:55 +02:00
|
|
|
Mined(..) | Dropped(..) | Invalid(..) | Canceled(..) => 2,
|
2016-11-16 17:54:54 +01:00
|
|
|
Rejected(..) => 3,
|
|
|
|
Replaced(..) => 4,
|
|
|
|
};
|
|
|
|
|
|
|
|
let status = "status";
|
|
|
|
let transaction = "transaction";
|
|
|
|
|
2017-02-13 16:38:47 +01:00
|
|
|
let mut struc = serializer.serialize_struct("LocalTransactionStatus", elems)?;
|
2016-11-16 17:54:54 +01:00
|
|
|
match *self {
|
2017-02-13 16:38:47 +01:00
|
|
|
Pending => struc.serialize_field(status, "pending")?,
|
|
|
|
Future => struc.serialize_field(status, "future")?,
|
2016-11-16 17:54:54 +01:00
|
|
|
Mined(ref tx) => {
|
2017-02-13 16:38:47 +01:00
|
|
|
struc.serialize_field(status, "mined")?;
|
|
|
|
struc.serialize_field(transaction, tx)?;
|
2016-11-16 17:54:54 +01:00
|
|
|
},
|
|
|
|
Dropped(ref tx) => {
|
2017-02-13 16:38:47 +01:00
|
|
|
struc.serialize_field(status, "dropped")?;
|
|
|
|
struc.serialize_field(transaction, tx)?;
|
2016-11-16 17:54:54 +01:00
|
|
|
},
|
2017-03-29 14:43:55 +02:00
|
|
|
Canceled(ref tx) => {
|
|
|
|
struc.serialize_field(status, "canceled")?;
|
|
|
|
struc.serialize_field(transaction, tx)?;
|
|
|
|
},
|
2016-11-16 17:54:54 +01:00
|
|
|
Invalid(ref tx) => {
|
2017-02-13 16:38:47 +01:00
|
|
|
struc.serialize_field(status, "invalid")?;
|
|
|
|
struc.serialize_field(transaction, tx)?;
|
2016-11-16 17:54:54 +01:00
|
|
|
},
|
|
|
|
Rejected(ref tx, ref reason) => {
|
2017-02-13 16:38:47 +01:00
|
|
|
struc.serialize_field(status, "rejected")?;
|
|
|
|
struc.serialize_field(transaction, tx)?;
|
|
|
|
struc.serialize_field("error", reason)?;
|
2016-11-16 17:54:54 +01:00
|
|
|
},
|
|
|
|
Replaced(ref tx, ref gas_price, ref hash) => {
|
2017-02-13 16:38:47 +01:00
|
|
|
struc.serialize_field(status, "replaced")?;
|
|
|
|
struc.serialize_field(transaction, tx)?;
|
|
|
|
struc.serialize_field("hash", hash)?;
|
|
|
|
struc.serialize_field("gasPrice", gas_price)?;
|
2016-11-16 17:54:54 +01:00
|
|
|
},
|
|
|
|
}
|
2017-02-13 16:38:47 +01:00
|
|
|
|
|
|
|
struc.end()
|
2016-11-16 17:54:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-18 11:03:29 +01:00
|
|
|
/// Geth-compatible output for eth_signTransaction method
|
|
|
|
#[derive(Debug, Default, Clone, PartialEq, Serialize)]
|
|
|
|
pub struct RichRawTransaction {
|
|
|
|
/// Raw transaction RLP
|
|
|
|
pub raw: Bytes,
|
|
|
|
/// Transaction details
|
|
|
|
#[serde(rename="tx")]
|
|
|
|
pub transaction: Transaction
|
|
|
|
}
|
|
|
|
|
2017-04-19 14:30:00 +02:00
|
|
|
|
2016-11-18 11:03:29 +01:00
|
|
|
impl From<SignedTransaction> for RichRawTransaction {
|
|
|
|
fn from(t: SignedTransaction) -> Self {
|
2017-04-19 14:30:00 +02:00
|
|
|
// TODO: change transition to 0 when EIP-86 is commonly used.
|
|
|
|
let tx: Transaction = Transaction::from_signed(t, 0, u64::max_value());
|
2016-11-18 11:03:29 +01:00
|
|
|
RichRawTransaction {
|
|
|
|
raw: tx.raw.clone(),
|
|
|
|
transaction: tx,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-19 14:30:00 +02:00
|
|
|
impl Transaction {
|
|
|
|
/// Convert `LocalizedTransaction` into RPC Transaction.
|
|
|
|
pub fn from_localized(mut t: LocalizedTransaction, eip86_transition: u64) -> Transaction {
|
2016-11-04 12:05:10 +01:00
|
|
|
let signature = t.signature();
|
2017-04-19 14:30:00 +02:00
|
|
|
let scheme = if t.block_number >= eip86_transition { CreateContractAddress::FromCodeHash } else { CreateContractAddress::FromSenderAndNonce };
|
2016-02-09 16:38:21 +01:00
|
|
|
Transaction {
|
2016-07-06 11:23:29 +02:00
|
|
|
hash: t.hash().into(),
|
|
|
|
nonce: t.nonce.into(),
|
|
|
|
block_hash: Some(t.block_hash.clone().into()),
|
|
|
|
block_number: Some(t.block_number.into()),
|
|
|
|
transaction_index: Some(t.transaction_index.into()),
|
2017-01-13 09:51:36 +01:00
|
|
|
from: t.sender().into(),
|
2016-02-09 16:38:21 +01:00
|
|
|
to: match t.action {
|
2016-07-06 11:23:29 +02:00
|
|
|
Action::Create => None,
|
|
|
|
Action::Call(ref address) => Some(address.clone().into())
|
2016-02-09 16:38:21 +01:00
|
|
|
},
|
2016-07-06 11:23:29 +02:00
|
|
|
value: t.value.into(),
|
|
|
|
gas_price: t.gas_price.into(),
|
|
|
|
gas: t.gas.into(),
|
2016-06-02 12:40:31 +02:00
|
|
|
input: Bytes::new(t.data.clone()),
|
|
|
|
creates: match t.action {
|
2017-06-30 11:30:32 +02:00
|
|
|
Action::Create => Some(contract_address(scheme, &t.sender(), &t.nonce, &t.data).0.into()),
|
2016-07-06 11:23:29 +02:00
|
|
|
Action::Call(_) => None,
|
2016-06-02 12:40:31 +02:00
|
|
|
},
|
2017-06-28 14:16:53 +02:00
|
|
|
raw: ::rlp::encode(&t.signed).into_vec().into(),
|
2017-01-13 09:51:36 +01:00
|
|
|
public_key: t.recover_public().ok().map(Into::into),
|
2017-08-21 13:46:58 +02:00
|
|
|
chain_id: t.chain_id(),
|
2016-11-29 13:46:06 +01:00
|
|
|
standard_v: t.standard_v().into(),
|
|
|
|
v: t.original_v().into(),
|
2016-11-04 12:05:10 +01:00
|
|
|
r: signature.r().into(),
|
|
|
|
s: signature.s().into(),
|
2017-02-03 19:32:10 +01:00
|
|
|
condition: None,
|
2016-02-09 16:38:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-19 14:30:00 +02:00
|
|
|
/// Convert `SignedTransaction` into RPC Transaction.
|
|
|
|
pub fn from_signed(t: SignedTransaction, block_number: u64, eip86_transition: u64) -> Transaction {
|
2016-11-04 12:05:10 +01:00
|
|
|
let signature = t.signature();
|
2017-04-19 14:30:00 +02:00
|
|
|
let scheme = if block_number >= eip86_transition { CreateContractAddress::FromCodeHash } else { CreateContractAddress::FromSenderAndNonce };
|
2016-03-27 15:12:21 +02:00
|
|
|
Transaction {
|
2016-07-06 11:23:29 +02:00
|
|
|
hash: t.hash().into(),
|
|
|
|
nonce: t.nonce.into(),
|
|
|
|
block_hash: None,
|
|
|
|
block_number: None,
|
|
|
|
transaction_index: None,
|
2017-01-13 09:51:36 +01:00
|
|
|
from: t.sender().into(),
|
2016-03-27 15:12:21 +02:00
|
|
|
to: match t.action {
|
2016-07-06 11:23:29 +02:00
|
|
|
Action::Create => None,
|
|
|
|
Action::Call(ref address) => Some(address.clone().into())
|
2016-03-27 15:12:21 +02:00
|
|
|
},
|
2016-07-06 11:23:29 +02:00
|
|
|
value: t.value.into(),
|
|
|
|
gas_price: t.gas_price.into(),
|
|
|
|
gas: t.gas.into(),
|
2016-06-02 12:40:31 +02:00
|
|
|
input: Bytes::new(t.data.clone()),
|
|
|
|
creates: match t.action {
|
2017-06-30 11:30:32 +02:00
|
|
|
Action::Create => Some(contract_address(scheme, &t.sender(), &t.nonce, &t.data).0.into()),
|
2016-07-06 11:23:29 +02:00
|
|
|
Action::Call(_) => None,
|
2016-06-02 12:40:31 +02:00
|
|
|
},
|
2017-06-28 14:16:53 +02:00
|
|
|
raw: ::rlp::encode(&t).into_vec().into(),
|
2017-04-19 14:30:00 +02:00
|
|
|
public_key: t.public_key().map(Into::into),
|
2017-08-21 13:46:58 +02:00
|
|
|
chain_id: t.chain_id(),
|
2016-11-29 13:46:06 +01:00
|
|
|
standard_v: t.standard_v().into(),
|
|
|
|
v: t.original_v().into(),
|
2016-11-04 12:05:10 +01:00
|
|
|
r: signature.r().into(),
|
|
|
|
s: signature.s().into(),
|
2017-02-03 19:32:10 +01:00
|
|
|
condition: None,
|
2016-03-27 15:12:21 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-19 14:30:00 +02:00
|
|
|
/// Convert `PendingTransaction` into RPC Transaction.
|
|
|
|
pub fn from_pending(t: PendingTransaction, block_number: u64, eip86_transition: u64) -> Transaction {
|
|
|
|
let mut r = Transaction::from_signed(t.transaction, block_number, eip86_transition);
|
2017-02-03 19:32:10 +01:00
|
|
|
r.condition = t.condition.map(|b| b.into());
|
2016-12-15 18:19:19 +01:00
|
|
|
r
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-19 14:30:00 +02:00
|
|
|
impl LocalTransactionStatus {
|
|
|
|
/// Convert `LocalTransactionStatus` into RPC `LocalTransactionStatus`.
|
|
|
|
pub fn from(s: miner::LocalTransactionStatus, block_number: u64, eip86_transition: u64) -> Self {
|
2016-11-16 17:54:54 +01:00
|
|
|
use ethcore::miner::LocalTransactionStatus::*;
|
|
|
|
match s {
|
|
|
|
Pending => LocalTransactionStatus::Pending,
|
|
|
|
Future => LocalTransactionStatus::Future,
|
2017-04-19 14:30:00 +02:00
|
|
|
Mined(tx) => LocalTransactionStatus::Mined(Transaction::from_signed(tx, block_number, eip86_transition)),
|
|
|
|
Dropped(tx) => LocalTransactionStatus::Dropped(Transaction::from_signed(tx, block_number, eip86_transition)),
|
|
|
|
Rejected(tx, err) => LocalTransactionStatus::Rejected(Transaction::from_signed(tx, block_number, eip86_transition), errors::transaction_message(err)),
|
|
|
|
Replaced(tx, gas_price, hash) => LocalTransactionStatus::Replaced(Transaction::from_signed(tx, block_number, eip86_transition), gas_price.into(), hash.into()),
|
|
|
|
Invalid(tx) => LocalTransactionStatus::Invalid(Transaction::from_signed(tx, block_number, eip86_transition)),
|
|
|
|
Canceled(tx) => LocalTransactionStatus::Canceled(Transaction::from_pending(tx, block_number, eip86_transition)),
|
2016-11-16 17:54:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-08 11:58:47 +01:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2016-11-16 17:54:54 +01:00
|
|
|
use super::{Transaction, LocalTransactionStatus};
|
2016-02-08 11:58:47 +01:00
|
|
|
use serde_json;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_transaction_serialize() {
|
|
|
|
let t = Transaction::default();
|
|
|
|
let serialized = serde_json::to_string(&t).unwrap();
|
2017-08-21 13:46:58 +02:00
|
|
|
assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"chainId":null,"standardV":"0x0","v":"0x0","r":"0x0","s":"0x0","condition":null}"#);
|
2016-02-08 11:58:47 +01:00
|
|
|
}
|
2016-11-16 17:54:54 +01:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_local_transaction_status_serialize() {
|
|
|
|
let tx_ser = serde_json::to_string(&Transaction::default()).unwrap();
|
|
|
|
let status1 = LocalTransactionStatus::Pending;
|
|
|
|
let status2 = LocalTransactionStatus::Future;
|
|
|
|
let status3 = LocalTransactionStatus::Mined(Transaction::default());
|
|
|
|
let status4 = LocalTransactionStatus::Dropped(Transaction::default());
|
|
|
|
let status5 = LocalTransactionStatus::Invalid(Transaction::default());
|
|
|
|
let status6 = LocalTransactionStatus::Rejected(Transaction::default(), "Just because".into());
|
|
|
|
let status7 = LocalTransactionStatus::Replaced(Transaction::default(), 5.into(), 10.into());
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
serde_json::to_string(&status1).unwrap(),
|
|
|
|
r#"{"status":"pending"}"#
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
serde_json::to_string(&status2).unwrap(),
|
|
|
|
r#"{"status":"future"}"#
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
serde_json::to_string(&status3).unwrap(),
|
|
|
|
r#"{"status":"mined","transaction":"#.to_owned() + &format!("{}", tx_ser) + r#"}"#
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
serde_json::to_string(&status4).unwrap(),
|
|
|
|
r#"{"status":"dropped","transaction":"#.to_owned() + &format!("{}", tx_ser) + r#"}"#
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
serde_json::to_string(&status5).unwrap(),
|
|
|
|
r#"{"status":"invalid","transaction":"#.to_owned() + &format!("{}", tx_ser) + r#"}"#
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
serde_json::to_string(&status6).unwrap(),
|
|
|
|
r#"{"status":"rejected","transaction":"#.to_owned() +
|
|
|
|
&format!("{}", tx_ser) +
|
|
|
|
r#","error":"Just because"}"#
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
serde_json::to_string(&status7).unwrap(),
|
|
|
|
r#"{"status":"replaced","transaction":"#.to_owned() +
|
|
|
|
&format!("{}", tx_ser) +
|
|
|
|
r#","hash":"0x000000000000000000000000000000000000000000000000000000000000000a","gasPrice":"0x5"}"#
|
|
|
|
);
|
|
|
|
}
|
2016-02-08 11:58:47 +01:00
|
|
|
}
|
|
|
|
|