lib/eth/tx.rb
module Eth class Tx include RLP::Sedes::Serializable extend Sedes set_serializable_fields({ nonce: big_endian_int, gas_price: big_endian_int, gas_limit: big_endian_int, to: address, value: big_endian_int, data: binary, v: big_endian_int, r: big_endian_int, s: big_endian_int }) attr_writer :signature def self.decode(data) data = Utils.hex_to_bin(data) if data.match(/\A\h+\Z/) deserialize(RLP.decode data) end def initialize(*args) fields = {v: 0, r: 0, s: 0}.merge parse_field_args(args) fields[:to] = Utils.normalize_address(fields[:to]) serializable_initialize fields check_transaction_validity end def unsigned_encoded RLP.encode self, sedes: Tx.exclude([:v, :r, :s]) end def encoded RLP.encode self end def hex bin_to_hex encoded end def sign(key) self.signature = key.sign(unsigned_encoded) self.vrs = Utils.v_r_s_for signature self end def to_h self.class.serializable_fields.keys.inject({}) do |hash, field| hash[field] = send field hash end end def from return @from if @from @from ||= OpenSsl.recover_compact(signature_hash, signature) if signature end def signature return @signature if @signature self.signature = [v, r, s].map do |integer| Utils.int_to_base256 integer end.join if [v, r, s].all? end private def check_transaction_validity if [gas_price, gas_limit, value, nonce].max > Ethereum::Base::UINT_MAX raise Ethereum::Base::InvalidTransaction, "Values way too high!" elsif gas_limit < intrinsic_gas_used raise Ethereum::Base::InvalidTransaction, "Gas limit too low" end end def vrs=(vrs) self.v = vrs[0] self.r = vrs[1] self.s = vrs[2] end def intrinsic_gas_used num_zero_bytes = data.count(Ethereum::Base::BYTE_ZERO) num_non_zero_bytes = data.size - num_zero_bytes Ethereum::Base::GTXCOST + Ethereum::Base::GTXDATAZERO * num_zero_bytes + Ethereum::Base::GTXDATANONZERO * num_non_zero_bytes end def signature_hash Utils.keccak256 unsigned_encoded end end end