diff --git a/src/error.rs b/src/error.rs index 3e7d3eb6d..01618c66c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -28,7 +28,7 @@ pub enum ExecutionError { InvalidNonce { expected: U256, is: U256 }, /// Returned when cost of transaction (value + gas_price * gas) exceeds /// current sender balance. - NotEnoughCash { required: U256, is: U256 }, + NotEnoughCash { required: U512, is: U512 }, /// Returned when internal evm error occurs. Internal } diff --git a/src/executive.rs b/src/executive.rs index 0fdb46335..344b57e27 100644 --- a/src/executive.rs +++ b/src/executive.rs @@ -120,17 +120,17 @@ impl<'a> Executive<'a> { // TODO: we might need bigints here, or at least check overflows. let balance = self.state.balance(&sender); - let gas_cost = t.gas * t.gas_price; - let total_cost = t.value + gas_cost; + let gas_cost = U512::from(t.gas) * U512::from(t.gas_price); + let total_cost = U512::from(t.value) + gas_cost; // avoid unaffordable transactions - if balance < total_cost { - return Err(From::from(ExecutionError::NotEnoughCash { required: total_cost, is: balance })); + if U512::from(balance) < total_cost { + return Err(From::from(ExecutionError::NotEnoughCash { required: total_cost, is: U512::from(balance) })); } // NOTE: there can be no invalid transactions from this point. self.state.inc_nonce(&sender); - self.state.sub_balance(&sender, &gas_cost); + self.state.sub_balance(&sender, &U256::from(gas_cost)); let mut substate = Substate::new(); let backup = self.state.clone(); @@ -743,10 +743,9 @@ mod tests { #[test] fn test_transact_simple() { - let mut t = Transaction::new(U256::zero(), U256::zero(), U256::from(100_000), Action::Create, U256::from(17), "3331600055".from_hex().unwrap()); - t.r = U256::from_str("98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a").unwrap(); - t.s = U256::from_str("8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3").unwrap(); - t.v = 0x1c; + 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 sender = t.sender().unwrap(); let mut state = State::new_temp(); @@ -771,10 +770,7 @@ mod tests { #[test] fn test_transact_invalid_sender() { - let mut t = Transaction::new(U256::zero(), U256::zero(), U256::from(100_000), Action::Create, U256::from(17), "3331600055".from_hex().unwrap()); - t.r = U256::from_str("98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a").unwrap(); - t.s = U256::from_str("8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3").unwrap(); - t.v = 0x1d; + let mut t = Transaction::new_create(U256::from(17), "3331600055".from_hex().unwrap(), U256::from(100_000), U256::zero(), U256::zero()); let mut state = State::new_temp(); let info = EnvInfo::new(); @@ -793,11 +789,10 @@ mod tests { #[test] fn test_transact_invalid_nonce() { - let mut t = Transaction::new(U256::one(), U256::zero(), U256::from(100_000), Action::Create, U256::from(17), "3331600055".from_hex().unwrap()); - t.r = U256::from_str("98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a").unwrap(); - t.s = U256::from_str("8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3").unwrap(); - t.v = 0x1c; - + 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 mut state = State::new_temp(); let info = EnvInfo::new(); let engine = TestEngine::new(0); diff --git a/src/transaction.rs b/src/transaction.rs index df7da7da2..35bb0eed2 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -23,25 +23,58 @@ pub struct Transaction { pub r: U256, pub s: U256, - hash: RefCell>, //TODO: make this private + hash: RefCell>, + sender: RefCell>, } impl Transaction { - pub fn new(nonce: U256, gas_price: U256, gas: U256, action: Action, value: U256, data: Bytes) -> Transaction { + /// Create a new message-call transaction. + 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, + action: Action::Call(to), value: value, data: data, v: 0, r: U256::zero(), s: U256::zero(), - hash: RefCell::new(None) + hash: RefCell::new(None), + sender: RefCell::new(None), } } + /// Create a new contract-creation transaction. + 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: U256::zero(), + s: U256::zero(), + hash: RefCell::new(None), + sender: RefCell::new(None), + } + } + + /// Get the nonce of the transaction. + pub fn nonce(&self) -> &U256 { &self.nonce } + /// Get the gas price of the transaction. + pub fn gas_price(&self) -> &U256 { &self.gas_price } + /// Get the gas of the transaction. + pub fn gas(&self) -> &U256 { &self.gas } + /// Get the action of the transaction (Create or Call). + pub fn action(&self) -> &Action { &self.action } + /// Get the value of the transaction. + pub fn value(&self) -> &U256 { &self.value } + /// Get the data of the transaction. + pub fn data(&self) -> &Bytes { &self.data } + /// Append object into RLP stream, optionally with or without the signature. pub fn rlp_append_opt(&self, s: &mut RlpStream, with_seal: Seal) { s.append_list(6 + match with_seal { Seal::With => 3, _ => 0 }); @@ -90,9 +123,6 @@ impl Transaction { *self.hash.borrow_mut() = None; } - /// Returns transaction type. - pub fn action(&self) -> &Action { &self.action } - /// 0 is `v` is 27, 1 if 28, and 4 otherwise. pub fn standard_v(&self) -> u8 { match self.v { 27 => 0, 28 => 1, _ => 4 } } @@ -103,27 +133,47 @@ impl Transaction { pub fn message_hash(&self) -> H256 { self.rlp_bytes_opt(Seal::Without).sha3() } /// Returns transaction sender. - pub fn sender(&self) -> Result { Ok(From::from(try!(ec::recover(&self.signature(), &self.message_hash())).sha3())) } + pub fn sender(&self) -> Result { + 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) { + 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`. + 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) -> U256 { - // CRITICAL TODO XXX FIX NEED BIGINT!!!!! + pub fn gas_required_for(is_create: bool, data: &[u8], schedule: &Schedule) -> u64 { data.iter().fold( - U256::from(if is_create {schedule.tx_create_gas} else {schedule.tx_gas}), - |g, b| g + U256::from(match *b { 0 => schedule.tx_data_zero_gas, _ => schedule.tx_data_non_zero_gas}) + (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) -> U256 { + pub fn gas_required(&self, schedule: &Schedule) -> u64 { Self::gas_required_for(match self.action{Action::Create=>true, Action::Call(_)=>false}, &self.data, schedule) } /// Do basic validation, checking for valid signature and minimum gas, pub fn validate(self, schedule: &Schedule) -> Result { try!(self.sender()); - if self.gas < self.gas_required(&schedule) { - Err(From::from(TransactionError::InvalidGasLimit(OutOfBounds{min: Some(self.gas_required(&schedule)), max: None, found: self.gas}))) + if self.gas < U256::from(self.gas_required(&schedule)) { + Err(From::from(TransactionError::InvalidGasLimit(OutOfBounds{min: Some(U256::from(self.gas_required(&schedule))), max: None, found: self.gas}))) } else { Ok(self) } @@ -157,7 +207,8 @@ impl Decodable for Transaction { v: try!(u16::decode(&d[6])) as u8, r: try!(Decodable::decode(&d[7])), s: try!(Decodable::decode(&d[8])), - hash: RefCell::new(None) + hash: RefCell::new(None), + sender: RefCell::new(None), }) } } @@ -175,3 +226,10 @@ fn sender_test() { assert_eq!(t.value, U256::from(0x0au64)); assert_eq!(t.sender().unwrap(), address_from_hex("0f65fe9276bc9a24ae7083ae28e2660ef72df99e")); } + +#[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()); + assert_eq!(Address::from(key.public().sha3()), t.sender().unwrap()); +} \ No newline at end of file