Merge branch 'master' of https://github.com/gavofyork/ethcore into executive_tests

This commit is contained in:
debris 2016-01-12 19:11:05 +01:00
commit 7d4365c875
3 changed files with 88 additions and 35 deletions

View File

@ -28,7 +28,7 @@ pub enum ExecutionError {
InvalidNonce { expected: U256, is: U256 }, InvalidNonce { expected: U256, is: U256 },
/// Returned when cost of transaction (value + gas_price * gas) exceeds /// Returned when cost of transaction (value + gas_price * gas) exceeds
/// current sender balance. /// current sender balance.
NotEnoughCash { required: U256, is: U256 }, NotEnoughCash { required: U512, is: U512 },
/// Returned when internal evm error occurs. /// Returned when internal evm error occurs.
Internal Internal
} }

View File

@ -120,17 +120,17 @@ impl<'a> Executive<'a> {
// TODO: we might need bigints here, or at least check overflows. // TODO: we might need bigints here, or at least check overflows.
let balance = self.state.balance(&sender); let balance = self.state.balance(&sender);
let gas_cost = t.gas * t.gas_price; let gas_cost = U512::from(t.gas) * U512::from(t.gas_price);
let total_cost = t.value + gas_cost; let total_cost = U512::from(t.value) + gas_cost;
// avoid unaffordable transactions // avoid unaffordable transactions
if balance < total_cost { if U512::from(balance) < total_cost {
return Err(From::from(ExecutionError::NotEnoughCash { required: total_cost, is: balance })); return Err(From::from(ExecutionError::NotEnoughCash { required: total_cost, is: U512::from(balance) }));
} }
// NOTE: there can be no invalid transactions from this point. // NOTE: there can be no invalid transactions from this point.
self.state.inc_nonce(&sender); 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 mut substate = Substate::new();
let backup = self.state.clone(); let backup = self.state.clone();
@ -743,10 +743,9 @@ mod tests {
#[test] #[test]
fn test_transact_simple() { 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()); let mut t = Transaction::new_create(U256::from(17), "3331600055".from_hex().unwrap(), U256::from(100_000), U256::zero(), U256::zero());
t.r = U256::from_str("98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a").unwrap(); let keypair = KeyPair::create().unwrap();
t.s = U256::from_str("8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3").unwrap(); t.sign(&keypair.secret());
t.v = 0x1c;
let sender = t.sender().unwrap(); let sender = t.sender().unwrap();
let mut state = State::new_temp(); let mut state = State::new_temp();
@ -771,10 +770,7 @@ mod tests {
#[test] #[test]
fn test_transact_invalid_sender() { 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()); let mut t = Transaction::new_create(U256::from(17), "3331600055".from_hex().unwrap(), U256::from(100_000), U256::zero(), U256::zero());
t.r = U256::from_str("98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a").unwrap();
t.s = U256::from_str("8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3").unwrap();
t.v = 0x1d;
let mut state = State::new_temp(); let mut state = State::new_temp();
let info = EnvInfo::new(); let info = EnvInfo::new();
@ -793,10 +789,9 @@ mod tests {
#[test] #[test]
fn test_transact_invalid_nonce() { 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()); let mut t = Transaction::new_create(U256::from(17), "3331600055".from_hex().unwrap(), U256::from(100_000), U256::zero(), U256::one());
t.r = U256::from_str("98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a").unwrap(); let keypair = KeyPair::create().unwrap();
t.s = U256::from_str("8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3").unwrap(); t.sign(&keypair.secret());
t.v = 0x1c;
let mut state = State::new_temp(); let mut state = State::new_temp();
let info = EnvInfo::new(); let info = EnvInfo::new();

View File

@ -23,25 +23,58 @@ pub struct Transaction {
pub r: U256, pub r: U256,
pub s: U256, pub s: U256,
hash: RefCell<Option<H256>>, //TODO: make this private hash: RefCell<Option<H256>>,
sender: RefCell<Option<Address>>,
} }
impl Transaction { 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 { Transaction {
nonce: nonce, nonce: nonce,
gas_price: gas_price, gas_price: gas_price,
gas: gas, gas: gas,
action: action, action: Action::Call(to),
value: value, value: value,
data: data, data: data,
v: 0, v: 0,
r: U256::zero(), r: U256::zero(),
s: 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. /// Append object into RLP stream, optionally with or without the signature.
pub fn rlp_append_opt(&self, s: &mut RlpStream, with_seal: Seal) { pub fn rlp_append_opt(&self, s: &mut RlpStream, with_seal: Seal) {
s.append_list(6 + match with_seal { Seal::With => 3, _ => 0 }); s.append_list(6 + match with_seal { Seal::With => 3, _ => 0 });
@ -90,9 +123,6 @@ impl Transaction {
*self.hash.borrow_mut() = None; *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. /// 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 } } 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() } pub fn message_hash(&self) -> H256 { self.rlp_bytes_opt(Seal::Without).sha3() }
/// Returns transaction sender. /// Returns transaction sender.
pub fn sender(&self) -> Result<Address, Error> { Ok(From::from(try!(ec::recover(&self.signature(), &self.message_hash())).sha3())) } 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) {
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. /// Get the transaction cost in gas for the given params.
pub fn gas_required_for(is_create: bool, data: &[u8], schedule: &Schedule) -> U256 { pub fn gas_required_for(is_create: bool, data: &[u8], schedule: &Schedule) -> u64 {
// CRITICAL TODO XXX FIX NEED BIGINT!!!!!
data.iter().fold( data.iter().fold(
U256::from(if is_create {schedule.tx_create_gas} else {schedule.tx_gas}), (if is_create {schedule.tx_create_gas} else {schedule.tx_gas}) as u64,
|g, b| g + U256::from(match *b { 0 => schedule.tx_data_zero_gas, _ => schedule.tx_data_non_zero_gas}) |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. /// 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) 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, /// Do basic validation, checking for valid signature and minimum gas,
pub fn validate(self, schedule: &Schedule) -> Result<Transaction, Error> { pub fn validate(self, schedule: &Schedule) -> Result<Transaction, Error> {
try!(self.sender()); try!(self.sender());
if self.gas < self.gas_required(&schedule) { if self.gas < U256::from(self.gas_required(&schedule)) {
Err(From::from(TransactionError::InvalidGasLimit(OutOfBounds{min: Some(self.gas_required(&schedule)), max: None, found: self.gas}))) Err(From::from(TransactionError::InvalidGasLimit(OutOfBounds{min: Some(U256::from(self.gas_required(&schedule))), max: None, found: self.gas})))
} else { } else {
Ok(self) Ok(self)
} }
@ -157,7 +207,8 @@ impl Decodable for Transaction {
v: try!(u16::decode(&d[6])) as u8, v: try!(u16::decode(&d[6])) as u8,
r: try!(Decodable::decode(&d[7])), r: try!(Decodable::decode(&d[7])),
s: try!(Decodable::decode(&d[8])), 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.value, U256::from(0x0au64));
assert_eq!(t.sender().unwrap(), address_from_hex("0f65fe9276bc9a24ae7083ae28e2660ef72df99e")); 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());
}