Class: Ethereum::Transaction
Overview
A transaction is stored as:
‘[nonce, gasprice, startgas, to, value, data, v, r, s]`
‘nonce` is the number of transactions already sent by that account, encoded in binary form (eg. 0 -> “”, 7 -> “x07”, 1000 -> “x03xd8”).
‘(v,r,s)` is the raw Electrum-style signature of the transaction without the signature made with the private key corresponding to the sending account, with `0 <= v <= 3`. From an Electrum-style signature (65 bytes) it is possible to extract the public key, and thereby the address, directly.
A valid transaction is one where:
-
the signature is well-formed (ie. ‘0 <= v <= 3, 0 <= r < P, 0 <= s < N, 0
<= r < P - N if v >= 2`), and
-
the sending account has enough funds to pay the fee and the value.
Class Method Summary collapse
-
.contract(nonce, gasprice, startgas, endowment, code, v = 0, r = 0, s = 0) ⇒ Object
A contract is a special transaction without the ‘to` argument.
Instance Method Summary collapse
- #==(other) ⇒ Object
-
#check_low_s ⇒ Object
This method should be called for block numbers >= config only.
-
#creates ⇒ Object
returns the address of a contract created by this tx.
- #full_hash ⇒ Object
- #hash ⇒ Object
-
#initialize(*args) ⇒ Transaction
constructor
A new instance of Transaction.
- #intrinsic_gas_used ⇒ Object
- #log_bloom ⇒ Object
- #log_bloom_b256 ⇒ Object
- #log_dict ⇒ Object
- #sender ⇒ Object
- #sender=(v) ⇒ Object
-
#sign(key) ⇒ Object
Sign this transaction with a private key.
- #to_h ⇒ Object
- #to_s ⇒ Object
Methods included from Sedes
address, big_endian_int, binary, hash32, int20, int256, int32, trie_root
Constructor Details
#initialize(*args) ⇒ Transaction
Returns a new instance of Transaction.
52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/ethereum/transaction.rb', line 52 def initialize(*args) fields = {v: 0, r: 0, s: 0}.merge parse_field_args(args) fields[:to] = Utils.normalize_address(fields[:to], allow_blank: true) serializable_initialize fields @sender = nil @logs = [] raise InvalidTransaction, "Values way too high!" if [gasprice, startgas, value, nonce].max > Constant::UINT_MAX raise InvalidTransaction, "Startgas too low" if startgas < intrinsic_gas_used logger.debug "deserialized tx #{Utils.encode_hex(full_hash)[0,8]}" end |
Class Method Details
.contract(nonce, gasprice, startgas, endowment, code, v = 0, r = 0, s = 0) ⇒ Object
A contract is a special transaction without the ‘to` argument.
47 48 49 |
# File 'lib/ethereum/transaction.rb', line 47 def contract(nonce, gasprice, startgas, endowment, code, v=0, r=0, s=0) new nonce, gasprice, startgas, '', endowment, code, v, r, s end |
Instance Method Details
#==(other) ⇒ Object
174 175 176 |
# File 'lib/ethereum/transaction.rb', line 174 def ==(other) other.instance_of?(self.class) && full_hash == other.full_hash end |
#check_low_s ⇒ Object
This method should be called for block numbers >= config only. The >= operator is replaced by > because the integer division N/2 always produces the value which is by 0.5 less than the real N/2.
123 124 125 |
# File 'lib/ethereum/transaction.rb', line 123 def check_low_s raise InvalidTransaction, "Invalid signature S value!" if s > Secp256k1::N/2 || s == 0 end |
#creates ⇒ Object
returns the address of a contract created by this tx
170 171 172 |
# File 'lib/ethereum/transaction.rb', line 170 def creates Utils.mk_contract_address(sender, nonce) if [Address::BLANK, Address::ZERO].include?(to) end |
#full_hash ⇒ Object
127 128 129 |
# File 'lib/ethereum/transaction.rb', line 127 def full_hash Utils.keccak256_rlp self end |
#hash ⇒ Object
178 179 180 |
# File 'lib/ethereum/transaction.rb', line 178 def hash Utils.big_endian_to_int full_hash end |
#intrinsic_gas_used ⇒ Object
140 141 142 143 144 145 146 147 |
# File 'lib/ethereum/transaction.rb', line 140 def intrinsic_gas_used num_zero_bytes = data.count(Constant::BYTE_ZERO) num_non_zero_bytes = data.size - num_zero_bytes Opcodes::GTXCOST + Opcodes::GTXDATAZERO*num_zero_bytes + Opcodes::GTXDATANONZERO*num_non_zero_bytes end |
#log_bloom ⇒ Object
131 132 133 134 |
# File 'lib/ethereum/transaction.rb', line 131 def log_bloom bloomables = @logs.map {|l| l.bloomables } Bloom.from_array bloomables.flatten end |
#log_bloom_b256 ⇒ Object
136 137 138 |
# File 'lib/ethereum/transaction.rb', line 136 def log_bloom_b256 Bloom.b256 log_bloom end |
#log_dict ⇒ Object
160 161 162 163 164 165 |
# File 'lib/ethereum/transaction.rb', line 160 def log_dict h = to_h h[:sender] = Utils.encode_hex(h[:sender] || '') h[:to] = Utils.encode_hex(h[:to]) h end |
#sender ⇒ Object
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/ethereum/transaction.rb', line 67 def sender unless @sender if v && v > 0 raise InvalidTransaction, "Invalid signature values!" if r >= Secp256k1::N || s >= Secp256k1::N || v < 27 || v > 28 || r == 0 || s == 0 logger.debug "recovering sender" rlpdata = RLP.encode(self, sedes: UnsignedTransaction) rawhash = Utils.keccak256 rlpdata pub = nil begin pub = Secp256k1.recover_pubkey rawhash, [v,r,s] rescue raise InvalidTransaction, "Invalid signature values (x^3+7 is non-residue)" end raise InvalidTransaction, "Invalid signature (zero privkey cannot sign)" if pub[1..-1] == Constant::PUBKEY_ZERO @sender = PublicKey.new(pub).to_address end end @sender end |
#sender=(v) ⇒ Object
92 93 94 |
# File 'lib/ethereum/transaction.rb', line 92 def sender=(v) @sender = v end |
#sign(key) ⇒ Object
Sign this transaction with a private key.
A potentially already existing signature would be override.
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/ethereum/transaction.rb', line 101 def sign(key) raise InvalidTransaction, "Zero privkey cannot sign" if [0, '', Constant::PRIVKEY_ZERO, Constant::PRIVKEY_ZERO_HEX].include?(key) rawhash = Utils.keccak256 RLP.encode(self, sedes: UnsignedTransaction) key = PrivateKey.new(key).encode(:bin) vrs = Secp256k1.recoverable_sign rawhash, key self.v = vrs[0] self.r = vrs[1] self.s = vrs[2] self.sender = PrivateKey.new(key).to_address self end |
#to_h ⇒ Object
149 150 151 152 153 154 155 156 157 158 |
# File 'lib/ethereum/transaction.rb', line 149 def to_h h = {} self.class.serializable_fields.keys.each do |field| h[field] = send field end h[:sender] = sender h[:hash] = Utils.encode_hex full_hash h end |
#to_s ⇒ Object
182 183 184 |
# File 'lib/ethereum/transaction.rb', line 182 def to_s "#<#{self.class.name}:#{object_id} #{Utils.encode_hex(full_hash)[0,8]}" end |