Merge pull request #350 from ethcore/signed_transaction
SignedTransaction structure
This commit is contained in:
		
						commit
						720c280fde
					
				@ -30,7 +30,7 @@ pub struct Block {
 | 
			
		||||
	/// The header of this block.
 | 
			
		||||
	pub header: Header,
 | 
			
		||||
	/// The transactions in this block.
 | 
			
		||||
	pub transactions: Vec<Transaction>,
 | 
			
		||||
	pub transactions: Vec<SignedTransaction>,
 | 
			
		||||
	/// The uncles of this block.
 | 
			
		||||
	pub uncles: Vec<Header>,
 | 
			
		||||
}
 | 
			
		||||
@ -92,7 +92,7 @@ pub struct BlockRefMut<'a> {
 | 
			
		||||
	/// Block header.
 | 
			
		||||
	pub header: &'a Header,
 | 
			
		||||
	/// Block transactions.
 | 
			
		||||
	pub transactions: &'a Vec<Transaction>,
 | 
			
		||||
	pub transactions: &'a Vec<SignedTransaction>,
 | 
			
		||||
	/// Block uncles.
 | 
			
		||||
	pub uncles: &'a Vec<Header>,
 | 
			
		||||
	/// Transaction receipts.
 | 
			
		||||
@ -129,7 +129,7 @@ pub trait IsBlock {
 | 
			
		||||
	fn state(&self) -> &State { &self.block().state }
 | 
			
		||||
 | 
			
		||||
	/// Get all information on transactions in this block.
 | 
			
		||||
	fn transactions(&self) -> &Vec<Transaction> { &self.block().base.transactions }
 | 
			
		||||
	fn transactions(&self) -> &Vec<SignedTransaction> { &self.block().base.transactions }
 | 
			
		||||
 | 
			
		||||
	/// Get all information on receipts in this block.
 | 
			
		||||
	fn receipts(&self) -> &Vec<Receipt> { &self.block().receipts }
 | 
			
		||||
@ -244,7 +244,7 @@ impl<'x, 'y> OpenBlock<'x, 'y> {
 | 
			
		||||
	/// Push a transaction into the block.
 | 
			
		||||
	///
 | 
			
		||||
	/// If valid, it will be executed, and archived together with the receipt.
 | 
			
		||||
	pub fn push_transaction(&mut self, t: Transaction, h: Option<H256>) -> Result<&Receipt, Error> {
 | 
			
		||||
	pub fn push_transaction(&mut self, t: SignedTransaction, h: Option<H256>) -> Result<&Receipt, Error> {
 | 
			
		||||
		let env_info = self.env_info();
 | 
			
		||||
//		info!("env_info says gas_used={}", env_info.gas_used);
 | 
			
		||||
		match self.block.state.apply(&env_info, self.engine, &t) {
 | 
			
		||||
@ -332,7 +332,7 @@ impl IsBlock for SealedBlock {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Enact the block given by block header, transactions and uncles
 | 
			
		||||
pub fn enact<'x, 'y>(header: &Header, transactions: &[Transaction], uncles: &[Header], engine: &'x Engine, db: JournalDB, parent: &Header, last_hashes: &'y LastHashes) -> Result<ClosedBlock<'x, 'y>, Error> {
 | 
			
		||||
pub fn enact<'x, 'y>(header: &Header, transactions: &[SignedTransaction], uncles: &[Header], engine: &'x Engine, db: JournalDB, parent: &Header, last_hashes: &'y LastHashes) -> Result<ClosedBlock<'x, 'y>, Error> {
 | 
			
		||||
	{
 | 
			
		||||
		if ::log::max_log_level() >= ::log::LogLevel::Trace {
 | 
			
		||||
			let s = State::from_existing(db.clone(), parent.state_root().clone(), engine.account_start_nonce());
 | 
			
		||||
 | 
			
		||||
@ -109,7 +109,7 @@ pub trait BlockProvider {
 | 
			
		||||
 | 
			
		||||
	/// Get a list of transactions for a given block.
 | 
			
		||||
	/// Returns None if block deos not exist.
 | 
			
		||||
	fn transactions(&self, hash: &H256) -> Option<Vec<Transaction>> {
 | 
			
		||||
	fn transactions(&self, hash: &H256) -> Option<Vec<SignedTransaction>> {
 | 
			
		||||
		self.block(hash).map(|bytes| BlockView::new(&bytes).transactions())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -72,9 +72,9 @@ pub trait Engine : Sync + Send {
 | 
			
		||||
	/// Additional verification for transactions in blocks.
 | 
			
		||||
	// TODO: Add flags for which bits of the transaction to check.
 | 
			
		||||
	// TODO: consider including State in the params.
 | 
			
		||||
	fn verify_transaction_basic(&self, _t: &Transaction, _header: &Header) -> Result<(), Error> { Ok(()) }
 | 
			
		||||
	fn verify_transaction_basic(&self, _t: &SignedTransaction, _header: &Header) -> Result<(), Error> { Ok(()) }
 | 
			
		||||
	/// Verify a particular transaction is valid.
 | 
			
		||||
	fn verify_transaction(&self, _t: &Transaction, _header: &Header) -> Result<(), Error> { Ok(()) }
 | 
			
		||||
	fn verify_transaction(&self, _t: &SignedTransaction, _header: &Header) -> Result<(), Error> { Ok(()) }
 | 
			
		||||
 | 
			
		||||
	/// Don't forget to call Super::populateFromParent when subclassing & overriding.
 | 
			
		||||
	// TODO: consider including State in the params.
 | 
			
		||||
 | 
			
		||||
@ -170,14 +170,14 @@ impl Engine for Ethash {
 | 
			
		||||
		Ok(())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fn verify_transaction_basic(&self, t: &Transaction, header: &Header) -> result::Result<(), Error> {
 | 
			
		||||
	fn verify_transaction_basic(&self, t: &SignedTransaction, header: &Header) -> result::Result<(), Error> {
 | 
			
		||||
		if header.number() >= self.u64_param("frontierCompatibilityModeLimit") {
 | 
			
		||||
			try!(t.check_low_s());
 | 
			
		||||
		}
 | 
			
		||||
		Ok(())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fn verify_transaction(&self, t: &Transaction, _header: &Header) -> Result<(), Error> {
 | 
			
		||||
	fn verify_transaction(&self, t: &SignedTransaction, _header: &Header) -> Result<(), Error> {
 | 
			
		||||
		t.sender().map(|_|()) // Perform EC recovery and cache sender
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -102,7 +102,7 @@ impl<'a> Executive<'a> {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// This funtion should be used to execute transaction.
 | 
			
		||||
	pub fn transact(&'a mut self, t: &Transaction) -> Result<Executed, Error> {
 | 
			
		||||
	pub fn transact(&'a mut self, t: &SignedTransaction) -> Result<Executed, Error> {
 | 
			
		||||
		let sender = try!(t.sender());
 | 
			
		||||
		let nonce = self.state.nonce(&sender);
 | 
			
		||||
 | 
			
		||||
@ -286,7 +286,7 @@ impl<'a> Executive<'a> {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Finalizes the transaction (does refunds and suicides).
 | 
			
		||||
	fn finalize(&mut self, t: &Transaction, substate: Substate, result: evm::Result) -> ExecutionResult {
 | 
			
		||||
	fn finalize(&mut self, t: &SignedTransaction, substate: Substate, result: evm::Result) -> ExecutionResult {
 | 
			
		||||
		let schedule = self.engine.schedule(self.info);
 | 
			
		||||
 | 
			
		||||
		// refunds from SSTORE nonzero -> zero
 | 
			
		||||
@ -706,9 +706,15 @@ mod tests {
 | 
			
		||||
	// test is incorrect, mk
 | 
			
		||||
	evm_test_ignore!{test_transact_simple: test_transact_simple_jit, test_transact_simple_int}
 | 
			
		||||
	fn test_transact_simple(factory: Factory) {
 | 
			
		||||
		let mut t = Transaction::new_create(U256::from(17), "3331600055".from_hex().unwrap(), U256::from(100_000), U256::zero(), U256::zero());
 | 
			
		||||
		let keypair = KeyPair::create().unwrap();
 | 
			
		||||
		t.sign(&keypair.secret());
 | 
			
		||||
		let t = Transaction {
 | 
			
		||||
			action: Action::Create,
 | 
			
		||||
			value: U256::from(17),
 | 
			
		||||
			data: "3331600055".from_hex().unwrap(),
 | 
			
		||||
			gas: U256::from(100_000),
 | 
			
		||||
			gas_price: U256::zero(),
 | 
			
		||||
			nonce: U256::zero()
 | 
			
		||||
		}.sign(&keypair.secret());
 | 
			
		||||
		let sender = t.sender().unwrap();
 | 
			
		||||
		let contract = contract_address(&sender, &U256::zero());
 | 
			
		||||
 | 
			
		||||
@ -738,8 +744,14 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
	evm_test!{test_transact_invalid_sender: test_transact_invalid_sender_jit, test_transact_invalid_sender_int}
 | 
			
		||||
	fn test_transact_invalid_sender(factory: Factory) {
 | 
			
		||||
		let t = Transaction::new_create(U256::from(17), "3331600055".from_hex().unwrap(), U256::from(100_000), U256::zero(), U256::zero());
 | 
			
		||||
 | 
			
		||||
		let t = Transaction {
 | 
			
		||||
			action: Action::Create,
 | 
			
		||||
			value: U256::from(17),
 | 
			
		||||
			data: "3331600055".from_hex().unwrap(),
 | 
			
		||||
			gas: U256::from(100_000),
 | 
			
		||||
			gas_price: U256::zero(),
 | 
			
		||||
			nonce: U256::zero()
 | 
			
		||||
		}.fake_sign();
 | 
			
		||||
		let mut state_result = get_temp_state();
 | 
			
		||||
		let mut state = state_result.reference_mut();
 | 
			
		||||
		let mut info = EnvInfo::default();
 | 
			
		||||
@ -759,11 +771,17 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
	evm_test!{test_transact_invalid_nonce: test_transact_invalid_nonce_jit, test_transact_invalid_nonce_int}
 | 
			
		||||
	fn test_transact_invalid_nonce(factory: Factory) {
 | 
			
		||||
		let mut t = Transaction::new_create(U256::from(17), "3331600055".from_hex().unwrap(), U256::from(100_000), U256::zero(), U256::one());
 | 
			
		||||
		let keypair = KeyPair::create().unwrap();
 | 
			
		||||
		t.sign(&keypair.secret());
 | 
			
		||||
		let t = Transaction {
 | 
			
		||||
			action: Action::Create,
 | 
			
		||||
			value: U256::from(17),
 | 
			
		||||
			data: "3331600055".from_hex().unwrap(),
 | 
			
		||||
			gas: U256::from(100_000),
 | 
			
		||||
			gas_price: U256::zero(),
 | 
			
		||||
			nonce: U256::one()
 | 
			
		||||
		}.sign(&keypair.secret());
 | 
			
		||||
		let sender = t.sender().unwrap();
 | 
			
		||||
		
 | 
			
		||||
 | 
			
		||||
		let mut state_result = get_temp_state();
 | 
			
		||||
		let mut state = state_result.reference_mut();
 | 
			
		||||
		state.add_balance(&sender, &U256::from(17));
 | 
			
		||||
@ -785,9 +803,15 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
	evm_test!{test_transact_gas_limit_reached: test_transact_gas_limit_reached_jit, test_transact_gas_limit_reached_int}
 | 
			
		||||
	fn test_transact_gas_limit_reached(factory: Factory) {
 | 
			
		||||
		let mut t = Transaction::new_create(U256::from(17), "3331600055".from_hex().unwrap(), U256::from(80_001), U256::zero(), U256::zero());
 | 
			
		||||
		let keypair = KeyPair::create().unwrap();
 | 
			
		||||
		t.sign(&keypair.secret());
 | 
			
		||||
		let t = Transaction {
 | 
			
		||||
			action: Action::Create,
 | 
			
		||||
			value: U256::from(17),
 | 
			
		||||
			data: "3331600055".from_hex().unwrap(),
 | 
			
		||||
			gas: U256::from(80_001),
 | 
			
		||||
			gas_price: U256::zero(),
 | 
			
		||||
			nonce: U256::zero()
 | 
			
		||||
		}.sign(&keypair.secret());
 | 
			
		||||
		let sender = t.sender().unwrap();
 | 
			
		||||
 | 
			
		||||
		let mut state_result = get_temp_state();
 | 
			
		||||
@ -812,9 +836,16 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
	evm_test!{test_not_enough_cash: test_not_enough_cash_jit, test_not_enough_cash_int}
 | 
			
		||||
	fn test_not_enough_cash(factory: Factory) {
 | 
			
		||||
		let mut t = Transaction::new_create(U256::from(18), "3331600055".from_hex().unwrap(), U256::from(100_000), U256::one(), U256::zero());
 | 
			
		||||
 | 
			
		||||
		let keypair = KeyPair::create().unwrap();
 | 
			
		||||
		t.sign(&keypair.secret());
 | 
			
		||||
		let t = Transaction {
 | 
			
		||||
			action: Action::Create,
 | 
			
		||||
			value: U256::from(18),
 | 
			
		||||
			data: "3331600055".from_hex().unwrap(),
 | 
			
		||||
			gas: U256::from(100_000),
 | 
			
		||||
			gas_price: U256::one(),
 | 
			
		||||
			nonce: U256::zero()
 | 
			
		||||
		}.sign(&keypair.secret());
 | 
			
		||||
		let sender = t.sender().unwrap();
 | 
			
		||||
 | 
			
		||||
		let mut state_result = get_temp_state();
 | 
			
		||||
 | 
			
		||||
@ -49,7 +49,7 @@ pub fn json_chain_test(json_data: &[u8], era: ChainEra) -> Vec<String> {
 | 
			
		||||
 | 
			
		||||
			flush!("   - {}...", name);
 | 
			
		||||
 | 
			
		||||
			let t = Transaction::from_json(&test["transaction"]);
 | 
			
		||||
			let t = SignedTransaction::from_json(&test["transaction"]);
 | 
			
		||||
			let env = EnvInfo::from_json(&test["env"]);
 | 
			
		||||
			let _out = Bytes::from_json(&test["out"]);
 | 
			
		||||
			let post_state_root = xjson!(&test["postStateRoot"]);
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,7 @@ fn do_json_test(json_data: &[u8]) -> Vec<String> {
 | 
			
		||||
	let mut failed = Vec::new();
 | 
			
		||||
	let old_schedule = evm::Schedule::new_frontier();
 | 
			
		||||
	let new_schedule = evm::Schedule::new_homestead();
 | 
			
		||||
	let ot = RefCell::new(Transaction::new());
 | 
			
		||||
	let ot = RefCell::new(None);
 | 
			
		||||
	for (name, test) in json.as_object().unwrap() {
 | 
			
		||||
		let mut fail = false;
 | 
			
		||||
		let mut fail_unless = |cond: bool| if !cond && !fail { failed.push(name.clone()); println!("Transaction: {:?}", ot.borrow()); fail = true };
 | 
			
		||||
@ -31,7 +31,7 @@ fn do_json_test(json_data: &[u8]) -> Vec<String> {
 | 
			
		||||
			.and_then(|s| BlockNumber::from_str(s).ok())
 | 
			
		||||
			.unwrap_or(0) { x if x < 1_000_000 => &old_schedule, _ => &new_schedule };
 | 
			
		||||
		let rlp = Bytes::from_json(&test["rlp"]);
 | 
			
		||||
		let res = UntrustedRlp::new(&rlp).as_val().map_err(From::from).and_then(|t: Transaction| t.validate(schedule, schedule.have_delegate_call));
 | 
			
		||||
		let res = UntrustedRlp::new(&rlp).as_val().map_err(From::from).and_then(|t: SignedTransaction| t.validate(schedule, schedule.have_delegate_call));
 | 
			
		||||
		fail_unless(test.find("transaction").is_none() == res.is_err());
 | 
			
		||||
		if let (Some(&Json::Object(ref tx)), Some(&Json::String(ref expect_sender))) = (test.find("transaction"), test.find("sender")) {
 | 
			
		||||
			let t = res.unwrap();
 | 
			
		||||
@ -42,10 +42,10 @@ fn do_json_test(json_data: &[u8]) -> Vec<String> {
 | 
			
		||||
			fail_unless(t.nonce == xjson!(&tx["nonce"]));
 | 
			
		||||
			fail_unless(t.value == xjson!(&tx["value"]));
 | 
			
		||||
			if let Action::Call(ref to) = t.action {
 | 
			
		||||
				*ot.borrow_mut() = t.clone();
 | 
			
		||||
				*ot.borrow_mut() = Some(t.clone());
 | 
			
		||||
				fail_unless(to == &xjson!(&tx["to"]));
 | 
			
		||||
			} else {
 | 
			
		||||
				*ot.borrow_mut() = t.clone();
 | 
			
		||||
				*ot.borrow_mut() = Some(t.clone());
 | 
			
		||||
				fail_unless(Bytes::from_json(&tx["to"]).is_empty());
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
@ -209,7 +209,7 @@ impl State {
 | 
			
		||||
 | 
			
		||||
	/// Execute a given transaction.
 | 
			
		||||
	/// This will change the state accordingly.
 | 
			
		||||
	pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, t: &Transaction) -> ApplyResult {
 | 
			
		||||
	pub fn apply(&mut self, env_info: &EnvInfo, engine: &Engine, t: &SignedTransaction) -> ApplyResult {
 | 
			
		||||
//		let old = self.to_pod();
 | 
			
		||||
 | 
			
		||||
		let e = try!(Executive::new(self, env_info, engine).transact(t));
 | 
			
		||||
 | 
			
		||||
@ -123,12 +123,12 @@ fn create_unverifiable_block(order: u32, parent_hash: H256) -> Bytes {
 | 
			
		||||
	create_test_block(&create_unverifiable_block_header(order, parent_hash))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn create_test_block_with_data(header: &Header, transactions: &[&Transaction], uncles: &[Header]) -> Bytes {
 | 
			
		||||
pub fn create_test_block_with_data(header: &Header, transactions: &[&SignedTransaction], uncles: &[Header]) -> Bytes {
 | 
			
		||||
	let mut rlp = RlpStream::new_list(3);
 | 
			
		||||
	rlp.append(header);
 | 
			
		||||
	rlp.begin_list(transactions.len());
 | 
			
		||||
	for t in transactions {
 | 
			
		||||
		rlp.append_raw(&t.rlp_bytes_opt(Seal::With), 1);
 | 
			
		||||
		rlp.append_raw(&encode::<SignedTransaction>(t).to_vec(), 1);
 | 
			
		||||
	}
 | 
			
		||||
	rlp.append(&uncles);
 | 
			
		||||
	rlp.out()
 | 
			
		||||
 | 
			
		||||
@ -17,7 +17,6 @@
 | 
			
		||||
//! Transaction data structure.
 | 
			
		||||
 | 
			
		||||
use util::*;
 | 
			
		||||
use basic_types::*;
 | 
			
		||||
use error::*;
 | 
			
		||||
use evm::Schedule;
 | 
			
		||||
 | 
			
		||||
@ -34,6 +33,16 @@ impl Default for Action {
 | 
			
		||||
	fn default() -> Action { Action::Create }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Decodable for Action {
 | 
			
		||||
	fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
 | 
			
		||||
		let rlp = decoder.as_rlp();
 | 
			
		||||
		match rlp.is_empty() {
 | 
			
		||||
			true => Ok(Action::Create),
 | 
			
		||||
			false => Ok(Action::Call(try!(rlp.as_val())))
 | 
			
		||||
		} 
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A set of information describing an externally-originating message call
 | 
			
		||||
/// or contract creation operation.
 | 
			
		||||
#[derive(Default, Debug, Clone)]
 | 
			
		||||
@ -50,103 +59,27 @@ pub struct Transaction {
 | 
			
		||||
	pub value: U256,
 | 
			
		||||
	/// Transaction data.
 | 
			
		||||
	pub data: Bytes,
 | 
			
		||||
 | 
			
		||||
	// signature
 | 
			
		||||
	/// The V field of the signature, either 27 or 28; helps describe the point on the curve.
 | 
			
		||||
	pub v: u8,
 | 
			
		||||
	/// The R field of the signature; helps describe the point on the curve.
 | 
			
		||||
	pub r: U256,
 | 
			
		||||
	/// The S field of the signature; helps describe the point on the curve.
 | 
			
		||||
	pub s: U256,
 | 
			
		||||
 | 
			
		||||
	hash: RefCell<Option<H256>>,
 | 
			
		||||
	sender: RefCell<Option<Address>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Transaction {
 | 
			
		||||
	/// Create a new transaction.
 | 
			
		||||
	#[cfg(test)]
 | 
			
		||||
	#[cfg(feature = "json-tests")]
 | 
			
		||||
	pub fn new() -> Self {
 | 
			
		||||
		Transaction {
 | 
			
		||||
			nonce: x!(0),
 | 
			
		||||
			gas_price: x!(0),
 | 
			
		||||
			gas: x!(0),
 | 
			
		||||
			action: Action::Create,
 | 
			
		||||
			value: x!(0),
 | 
			
		||||
			data: vec![],
 | 
			
		||||
			v: 0,
 | 
			
		||||
			r: x!(0),
 | 
			
		||||
			s: x!(0),
 | 
			
		||||
			hash: RefCell::new(None),
 | 
			
		||||
			sender: RefCell::new(None),
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Create a new message-call transaction.
 | 
			
		||||
	#[allow(dead_code)]
 | 
			
		||||
	pub fn new_call(to: Address, value: U256, data: Bytes, gas: U256, gas_price: U256, nonce: U256) -> Transaction {
 | 
			
		||||
		Transaction {
 | 
			
		||||
			nonce: nonce,
 | 
			
		||||
			gas_price: gas_price,
 | 
			
		||||
			gas: gas,
 | 
			
		||||
			action: Action::Call(to),
 | 
			
		||||
			value: value,
 | 
			
		||||
			data: data,
 | 
			
		||||
			v: 0,
 | 
			
		||||
			r: x!(0),
 | 
			
		||||
			s: x!(0),
 | 
			
		||||
			hash: RefCell::new(None),
 | 
			
		||||
			sender: RefCell::new(None),
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Create a new contract-creation transaction.
 | 
			
		||||
	#[cfg(test)]
 | 
			
		||||
	pub fn new_create(value: U256, data: Bytes, gas: U256, gas_price: U256, nonce: U256) -> Transaction {
 | 
			
		||||
		Transaction {
 | 
			
		||||
			nonce: nonce,
 | 
			
		||||
			gas_price: gas_price,
 | 
			
		||||
			gas: gas,
 | 
			
		||||
			action: Action::Create,
 | 
			
		||||
			value: value,
 | 
			
		||||
			data: data,
 | 
			
		||||
			v: 0,
 | 
			
		||||
			r: x!(0),
 | 
			
		||||
			s: x!(0),
 | 
			
		||||
			hash: RefCell::new(None),
 | 
			
		||||
			sender: RefCell::new(None),
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Append object into RLP stream, optionally with or without the signature.
 | 
			
		||||
	pub fn rlp_append_opt(&self, s: &mut RlpStream, with_seal: Seal) {
 | 
			
		||||
		s.begin_list(6 + match with_seal { Seal::With => 3, _ => 0 });
 | 
			
		||||
	/// Append object with a without signature into RLP stream
 | 
			
		||||
	pub fn rlp_append_unsigned_transaction(&self, s: &mut RlpStream) {
 | 
			
		||||
		s.begin_list(6);
 | 
			
		||||
		s.append(&self.nonce);
 | 
			
		||||
		s.append(&self.gas_price);
 | 
			
		||||
		s.append(&self.gas);
 | 
			
		||||
		match self.action {
 | 
			
		||||
			Action::Create => s.append_empty_data(),
 | 
			
		||||
			Action::Call(ref to) => s.append(to),
 | 
			
		||||
			Action::Call(ref to) => s.append(to)
 | 
			
		||||
		};
 | 
			
		||||
		s.append(&self.value);
 | 
			
		||||
		s.append(&self.data);
 | 
			
		||||
		if let Seal::With = with_seal {
 | 
			
		||||
			s.append(&(self.v as u16)).append(&self.r).append(&self.s);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Get the RLP serialisation of the object, optionally with or without the signature.
 | 
			
		||||
	pub fn rlp_bytes_opt(&self, with_seal: Seal) -> Bytes {
 | 
			
		||||
		let mut s = RlpStream::new();
 | 
			
		||||
		self.rlp_append_opt(&mut s, with_seal);
 | 
			
		||||
		s.out()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl FromJson for Transaction {
 | 
			
		||||
	fn from_json(json: &Json) -> Transaction {
 | 
			
		||||
		let mut r = Transaction {
 | 
			
		||||
impl FromJson for SignedTransaction {
 | 
			
		||||
	fn from_json(json: &Json) -> SignedTransaction {
 | 
			
		||||
		let t = Transaction {
 | 
			
		||||
			nonce: xjson!(&json["nonce"]),
 | 
			
		||||
			gas_price: xjson!(&json["gasPrice"]),
 | 
			
		||||
			gas: xjson!(&json["gasLimit"]),
 | 
			
		||||
@ -156,27 +89,145 @@ impl FromJson for Transaction {
 | 
			
		||||
			},
 | 
			
		||||
			value: xjson!(&json["value"]),
 | 
			
		||||
			data: xjson!(&json["data"]),
 | 
			
		||||
			v: match json.find("v") { Some(ref j) => u16::from_json(j) as u8, None => 0 },
 | 
			
		||||
			r: match json.find("r") { Some(j) => xjson!(j), None => x!(0) },
 | 
			
		||||
			s: match json.find("s") { Some(j) => xjson!(j), None => x!(0) },
 | 
			
		||||
			hash: RefCell::new(None),
 | 
			
		||||
			sender: match json.find("sender") {
 | 
			
		||||
				Some(&Json::String(ref sender)) => RefCell::new(Some(address_from_hex(clean(sender)))),
 | 
			
		||||
				_ => RefCell::new(None),
 | 
			
		||||
			},
 | 
			
		||||
		};
 | 
			
		||||
		if let Some(&Json::String(ref secret_key)) = json.find("secretKey") {
 | 
			
		||||
			r.sign(&h256_from_hex(clean(secret_key)));
 | 
			
		||||
		match json.find("secretKey") {
 | 
			
		||||
			Some(&Json::String(ref secret_key)) => t.sign(&h256_from_hex(clean(secret_key))),
 | 
			
		||||
			_ => SignedTransaction {
 | 
			
		||||
				unsigned: t,
 | 
			
		||||
				v: match json.find("v") { Some(ref j) => u16::from_json(j) as u8, None => 0 },
 | 
			
		||||
				r: match json.find("r") { Some(j) => xjson!(j), None => x!(0) },
 | 
			
		||||
				s: match json.find("s") { Some(j) => xjson!(j), None => x!(0) },
 | 
			
		||||
				hash: RefCell::new(None),
 | 
			
		||||
				sender: match json.find("sender") {
 | 
			
		||||
					Some(&Json::String(ref sender)) => RefCell::new(Some(address_from_hex(clean(sender)))),
 | 
			
		||||
					_ => RefCell::new(None),
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		r
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Encodable for Transaction {
 | 
			
		||||
	fn rlp_append(&self, s: &mut RlpStream) { self.rlp_append_opt(s, Seal::With) }
 | 
			
		||||
impl Transaction {
 | 
			
		||||
	/// The message hash of the transaction.
 | 
			
		||||
	pub fn hash(&self) -> H256 { 
 | 
			
		||||
		let mut stream = RlpStream::new();
 | 
			
		||||
		self.rlp_append_unsigned_transaction(&mut stream);
 | 
			
		||||
		stream.out().sha3()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Signs the transaction as coming from `sender`.
 | 
			
		||||
	pub fn sign(self, secret: &Secret) -> SignedTransaction {
 | 
			
		||||
		let sig = ec::sign(secret, &self.hash());
 | 
			
		||||
		let (r, s, v) = sig.unwrap().to_rsv();
 | 
			
		||||
		SignedTransaction {
 | 
			
		||||
			unsigned: self,
 | 
			
		||||
			r: r,
 | 
			
		||||
			s: s,
 | 
			
		||||
			v: v + 27,
 | 
			
		||||
			hash: RefCell::new(None),
 | 
			
		||||
			sender: RefCell::new(None)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Useful for test incorrectly signed transactions.
 | 
			
		||||
	#[cfg(test)]
 | 
			
		||||
	pub fn fake_sign(self) -> SignedTransaction {
 | 
			
		||||
		SignedTransaction {
 | 
			
		||||
			unsigned: self,
 | 
			
		||||
			r: U256::zero(),
 | 
			
		||||
			s: U256::zero(),
 | 
			
		||||
			v: 0,
 | 
			
		||||
			hash: RefCell::new(None),
 | 
			
		||||
			sender: RefCell::new(None)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Get the transaction cost in gas for the given params.
 | 
			
		||||
	pub fn gas_required_for(is_create: bool, data: &[u8], schedule: &Schedule) -> u64 {
 | 
			
		||||
		data.iter().fold(
 | 
			
		||||
			(if is_create {schedule.tx_create_gas} else {schedule.tx_gas}) as u64,
 | 
			
		||||
			|g, b| g + (match *b { 0 => schedule.tx_data_zero_gas, _ => schedule.tx_data_non_zero_gas }) as u64
 | 
			
		||||
		)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Get the transaction cost in gas for this transaction.
 | 
			
		||||
	pub fn gas_required(&self, schedule: &Schedule) -> u64 {
 | 
			
		||||
		Self::gas_required_for(match self.action{Action::Create=>true, Action::Call(_)=>false}, &self.data, schedule)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Transaction {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub struct SignedTransaction {
 | 
			
		||||
	/// Plain Transaction.
 | 
			
		||||
	unsigned: Transaction,
 | 
			
		||||
	/// The V field of the signature, either 27 or 28; helps describe the point on the curve.
 | 
			
		||||
	v: u8,
 | 
			
		||||
	/// The R field of the signature; helps describe the point on the curve.
 | 
			
		||||
	r: U256,
 | 
			
		||||
	/// The S field of the signature; helps describe the point on the curve.
 | 
			
		||||
	s: U256,
 | 
			
		||||
	/// Cached hash.
 | 
			
		||||
	hash: RefCell<Option<H256>>,
 | 
			
		||||
	/// Cached sender.
 | 
			
		||||
	sender: RefCell<Option<Address>>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Deref for SignedTransaction {
 | 
			
		||||
	type Target = Transaction;
 | 
			
		||||
 | 
			
		||||
	fn deref(&self) -> &Self::Target {
 | 
			
		||||
		&self.unsigned
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Decodable for SignedTransaction {
 | 
			
		||||
	fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
 | 
			
		||||
		let d = decoder.as_rlp();
 | 
			
		||||
		if d.item_count() != 9 {
 | 
			
		||||
			return Err(DecoderError::RlpIncorrectListLen);
 | 
			
		||||
		}
 | 
			
		||||
		Ok(SignedTransaction {
 | 
			
		||||
			unsigned: Transaction {
 | 
			
		||||
				nonce: try!(d.val_at(0)),
 | 
			
		||||
				gas_price: try!(d.val_at(1)),
 | 
			
		||||
				gas: try!(d.val_at(2)),
 | 
			
		||||
				action: try!(d.val_at(3)),
 | 
			
		||||
				value: try!(d.val_at(4)),
 | 
			
		||||
				data: try!(d.val_at(5)),
 | 
			
		||||
			},
 | 
			
		||||
			v: try!(d.val_at(6)),
 | 
			
		||||
			r: try!(d.val_at(7)),
 | 
			
		||||
			s: try!(d.val_at(8)),
 | 
			
		||||
			hash: RefCell::new(None),
 | 
			
		||||
			sender: RefCell::new(None),
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Encodable for SignedTransaction {
 | 
			
		||||
	fn rlp_append(&self, s: &mut RlpStream) { self.rlp_append_sealed_transaction(s) }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl SignedTransaction {
 | 
			
		||||
	/// Append object with a signature into RLP stream
 | 
			
		||||
	pub fn rlp_append_sealed_transaction(&self, s: &mut RlpStream) {
 | 
			
		||||
		s.begin_list(9);
 | 
			
		||||
		s.append(&self.nonce);
 | 
			
		||||
		s.append(&self.gas_price);
 | 
			
		||||
		s.append(&self.gas);
 | 
			
		||||
		match self.action {
 | 
			
		||||
			Action::Create => s.append_empty_data(),
 | 
			
		||||
			Action::Call(ref to) => s.append(to)
 | 
			
		||||
		};
 | 
			
		||||
		s.append(&self.value);
 | 
			
		||||
		s.append(&self.data);
 | 
			
		||||
		s.append(&self.v);
 | 
			
		||||
		s.append(&self.r);
 | 
			
		||||
		s.append(&self.s);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Get the hash of this header (sha3 of the RLP).
 | 
			
		||||
	pub fn hash(&self) -> H256 {
 | 
			
		||||
 		let mut hash = self.hash.borrow_mut();
 | 
			
		||||
@ -195,48 +246,6 @@ impl Transaction {
 | 
			
		||||
	/// Construct a signature object from the sig.
 | 
			
		||||
	pub fn signature(&self) -> Signature { Signature::from_rsv(&From::from(&self.r), &From::from(&self.s), self.standard_v()) }
 | 
			
		||||
 | 
			
		||||
	/// The message hash of the transaction.
 | 
			
		||||
	pub fn message_hash(&self) -> H256 { self.rlp_bytes_opt(Seal::Without).sha3() }
 | 
			
		||||
 | 
			
		||||
	/// Returns transaction sender.
 | 
			
		||||
	pub fn sender(&self) -> Result<Address, Error> {
 | 
			
		||||
 		let mut sender = self.sender.borrow_mut();
 | 
			
		||||
 		match &mut *sender {
 | 
			
		||||
 			&mut Some(ref h) => Ok(h.clone()),
 | 
			
		||||
 			sender @ &mut None => {
 | 
			
		||||
 				*sender = Some(From::from(try!(ec::recover(&self.signature(), &self.message_hash())).sha3()));
 | 
			
		||||
 				Ok(sender.as_ref().unwrap().clone())
 | 
			
		||||
 			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Signs the transaction as coming from `sender`.
 | 
			
		||||
	pub fn sign(&mut self, secret: &Secret) {
 | 
			
		||||
		// TODO: make always low.
 | 
			
		||||
		let sig = ec::sign(secret, &self.message_hash());
 | 
			
		||||
		let (r, s, v) = sig.unwrap().to_rsv();
 | 
			
		||||
		self.r = r;
 | 
			
		||||
		self.s = s;
 | 
			
		||||
		self.v = v + 27;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Signs the transaction as coming from `sender`.
 | 
			
		||||
	#[cfg(test)]
 | 
			
		||||
	pub fn signed(self, secret: &Secret) -> Transaction { let mut r = self; r.sign(secret); r }
 | 
			
		||||
 | 
			
		||||
	/// Get the transaction cost in gas for the given params.
 | 
			
		||||
	pub fn gas_required_for(is_create: bool, data: &[u8], schedule: &Schedule) -> u64 {
 | 
			
		||||
		data.iter().fold(
 | 
			
		||||
			(if is_create {schedule.tx_create_gas} else {schedule.tx_gas}) as u64,
 | 
			
		||||
			|g, b| g + (match *b { 0 => schedule.tx_data_zero_gas, _ => schedule.tx_data_non_zero_gas }) as u64
 | 
			
		||||
		)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Get the transaction cost in gas for this transaction.
 | 
			
		||||
	pub fn gas_required(&self, schedule: &Schedule) -> u64 {
 | 
			
		||||
		Self::gas_required_for(match self.action{Action::Create=>true, Action::Call(_)=>false}, &self.data, schedule)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Checks whether the signature has a low 's' value.
 | 
			
		||||
	pub fn check_low_s(&self) -> Result<(), Error> {
 | 
			
		||||
		if !ec::is_low_s(&self.s) {
 | 
			
		||||
@ -246,11 +255,23 @@ impl Transaction {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Returns transaction sender.
 | 
			
		||||
	pub fn sender(&self) -> Result<Address, Error> {
 | 
			
		||||
 		let mut sender = self.sender.borrow_mut();
 | 
			
		||||
 		match &mut *sender {
 | 
			
		||||
 			&mut Some(ref h) => Ok(h.clone()),
 | 
			
		||||
 			sender @ &mut None => {
 | 
			
		||||
 				*sender = Some(From::from(try!(ec::recover(&self.signature(), &self.unsigned.hash())).sha3()));
 | 
			
		||||
 				Ok(sender.as_ref().unwrap().clone())
 | 
			
		||||
 			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Do basic validation, checking for valid signature and minimum gas,
 | 
			
		||||
	// TODO: consider use in block validation.
 | 
			
		||||
	#[cfg(test)]
 | 
			
		||||
	#[cfg(feature = "json-tests")]
 | 
			
		||||
	pub fn validate(self, schedule: &Schedule, require_low: bool) -> Result<Transaction, Error> {
 | 
			
		||||
	pub fn validate(self, schedule: &Schedule, require_low: bool) -> Result<SignedTransaction, Error> {
 | 
			
		||||
		if require_low && !ec::is_low_s(&self.s) {
 | 
			
		||||
			return Err(Error::Util(UtilError::Crypto(CryptoError::InvalidSignature)));
 | 
			
		||||
		}
 | 
			
		||||
@ -263,42 +284,9 @@ impl Transaction {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Decodable for Action {
 | 
			
		||||
	fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
 | 
			
		||||
		let rlp = decoder.as_rlp();
 | 
			
		||||
		if rlp.is_empty() {
 | 
			
		||||
			Ok(Action::Create)
 | 
			
		||||
		} else {
 | 
			
		||||
			Ok(Action::Call(try!(rlp.as_val())))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Decodable for Transaction {
 | 
			
		||||
	fn decode<D>(decoder: &D) -> Result<Self, DecoderError> where D: Decoder {
 | 
			
		||||
		let d = decoder.as_rlp();
 | 
			
		||||
		if d.item_count() != 9 {
 | 
			
		||||
			return Err(DecoderError::RlpIncorrectListLen);
 | 
			
		||||
		}
 | 
			
		||||
		Ok(Transaction {
 | 
			
		||||
			nonce: try!(d.val_at(0)),
 | 
			
		||||
			gas_price: try!(d.val_at(1)),
 | 
			
		||||
			gas: try!(d.val_at(2)),
 | 
			
		||||
			action: try!(d.val_at(3)),
 | 
			
		||||
			value: try!(d.val_at(4)),
 | 
			
		||||
			data: try!(d.val_at(5)),
 | 
			
		||||
			v: try!(d.val_at(6)),
 | 
			
		||||
			r: try!(d.val_at(7)),
 | 
			
		||||
			s: try!(d.val_at(8)),
 | 
			
		||||
			hash: RefCell::new(None),
 | 
			
		||||
			sender: RefCell::new(None),
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn sender_test() {
 | 
			
		||||
	let t: Transaction = decode(&FromHex::from_hex("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap());
 | 
			
		||||
	let t: SignedTransaction = decode(&FromHex::from_hex("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap());
 | 
			
		||||
	assert_eq!(t.data, b"");
 | 
			
		||||
	assert_eq!(t.gas, U256::from(0x5208u64));
 | 
			
		||||
	assert_eq!(t.gas_price, U256::from(0x01u64));
 | 
			
		||||
@ -313,6 +301,13 @@ fn sender_test() {
 | 
			
		||||
#[test]
 | 
			
		||||
fn signing() {
 | 
			
		||||
	let key = KeyPair::create().unwrap();
 | 
			
		||||
	let t = Transaction::new_create(U256::from(42u64), b"Hello!".to_vec(), U256::from(3000u64), U256::from(50_000u64), U256::from(1u64)).signed(&key.secret());
 | 
			
		||||
	let t = Transaction {
 | 
			
		||||
		action: Action::Create,
 | 
			
		||||
		nonce: U256::from(42),
 | 
			
		||||
		gas_price: U256::from(3000),
 | 
			
		||||
		gas: U256::from(50_000),
 | 
			
		||||
		value: U256::from(1),
 | 
			
		||||
		data: b"Hello!".to_vec()
 | 
			
		||||
	}.sign(&key.secret());
 | 
			
		||||
	assert_eq!(Address::from(key.public().sha3()), t.sender().unwrap());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -30,7 +30,7 @@ pub struct PreVerifiedBlock {
 | 
			
		||||
	/// Populated block header
 | 
			
		||||
	pub header: Header,
 | 
			
		||||
	/// Populated block transactions
 | 
			
		||||
	pub transactions: Vec<Transaction>,
 | 
			
		||||
	pub transactions: Vec<SignedTransaction>,
 | 
			
		||||
	/// Block bytes
 | 
			
		||||
	pub bytes: Bytes,
 | 
			
		||||
}
 | 
			
		||||
@ -236,7 +236,6 @@ mod tests {
 | 
			
		||||
	use engine::*;
 | 
			
		||||
	use spec::*;
 | 
			
		||||
	use transaction::*;
 | 
			
		||||
	use basic_types::*;
 | 
			
		||||
	use tests::helpers::*;
 | 
			
		||||
 | 
			
		||||
	fn check_ok(result: Result<(), Error>) {
 | 
			
		||||
@ -325,8 +324,26 @@ mod tests {
 | 
			
		||||
		good.timestamp = 40;
 | 
			
		||||
		good.number = 10;
 | 
			
		||||
 | 
			
		||||
		let tr1 = Transaction::new_create(x!(0), Bytes::new(), x!(30000), x!(40000), x!(1));
 | 
			
		||||
		let tr2 = Transaction::new_create(x!(0), Bytes::new(), x!(30000), x!(40000), x!(2));
 | 
			
		||||
		let keypair = KeyPair::create().unwrap();
 | 
			
		||||
 | 
			
		||||
		let tr1 = Transaction {
 | 
			
		||||
			action: Action::Create,
 | 
			
		||||
			value: U256::from(0),
 | 
			
		||||
			data: Bytes::new(),
 | 
			
		||||
			gas: U256::from(30_000),
 | 
			
		||||
			gas_price: U256::from(40_000),
 | 
			
		||||
			nonce: U256::one()
 | 
			
		||||
		}.sign(&keypair.secret());
 | 
			
		||||
 | 
			
		||||
		let tr2 = Transaction {
 | 
			
		||||
			action: Action::Create,
 | 
			
		||||
			value: U256::from(0),
 | 
			
		||||
			data: Bytes::new(),
 | 
			
		||||
			gas: U256::from(30_000),
 | 
			
		||||
			gas_price: U256::from(40_000),
 | 
			
		||||
			nonce: U256::from(2)
 | 
			
		||||
		}.sign(&keypair.secret());
 | 
			
		||||
 | 
			
		||||
		let good_transactions = [ &tr1, &tr2 ];
 | 
			
		||||
 | 
			
		||||
		let diff_inc = U256::from(0x40);
 | 
			
		||||
@ -362,7 +379,7 @@ mod tests {
 | 
			
		||||
		let mut uncles_rlp = RlpStream::new();
 | 
			
		||||
		uncles_rlp.append(&good_uncles);
 | 
			
		||||
		let good_uncles_hash = uncles_rlp.as_raw().sha3();
 | 
			
		||||
		let good_transactions_root = ordered_trie_root(good_transactions.iter().map(|t| t.rlp_bytes_opt(Seal::With)).collect());
 | 
			
		||||
		let good_transactions_root = ordered_trie_root(good_transactions.iter().map(|t| encode::<SignedTransaction>(t).to_vec()).collect());
 | 
			
		||||
 | 
			
		||||
		let mut parent = good.clone();
 | 
			
		||||
		parent.number = 9;
 | 
			
		||||
 | 
			
		||||
@ -151,7 +151,7 @@ impl<'a> BlockView<'a> {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/// Return List of transactions in given block.
 | 
			
		||||
	pub fn transactions(&self) -> Vec<Transaction> {
 | 
			
		||||
	pub fn transactions(&self) -> Vec<SignedTransaction> {
 | 
			
		||||
		self.rlp.val_at(1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user