ruby-ethereum 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +40 -0
- data/lib/ethereum.rb +53 -0
- data/lib/ethereum/abi.rb +333 -0
- data/lib/ethereum/abi/contract_translator.rb +174 -0
- data/lib/ethereum/abi/type.rb +117 -0
- data/lib/ethereum/account.rb +72 -0
- data/lib/ethereum/address.rb +60 -0
- data/lib/ethereum/base_convert.rb +53 -0
- data/lib/ethereum/block.rb +1311 -0
- data/lib/ethereum/block_header.rb +211 -0
- data/lib/ethereum/bloom.rb +83 -0
- data/lib/ethereum/cached_block.rb +36 -0
- data/lib/ethereum/chain.rb +400 -0
- data/lib/ethereum/constant.rb +26 -0
- data/lib/ethereum/core_ext/array/safe_slice.rb +18 -0
- data/lib/ethereum/core_ext/module/lru_cache.rb +20 -0
- data/lib/ethereum/core_ext/numeric/denominations.rb +45 -0
- data/lib/ethereum/core_ext/object/truth.rb +47 -0
- data/lib/ethereum/db.rb +6 -0
- data/lib/ethereum/db/base_db.rb +9 -0
- data/lib/ethereum/db/ephem_db.rb +63 -0
- data/lib/ethereum/db/overlay_db.rb +72 -0
- data/lib/ethereum/db/refcount_db.rb +188 -0
- data/lib/ethereum/env.rb +64 -0
- data/lib/ethereum/ethash.rb +78 -0
- data/lib/ethereum/ethash_ruby.rb +38 -0
- data/lib/ethereum/ethash_ruby/cache.rb +47 -0
- data/lib/ethereum/ethash_ruby/hashimoto.rb +75 -0
- data/lib/ethereum/ethash_ruby/utils.rb +53 -0
- data/lib/ethereum/exceptions.rb +28 -0
- data/lib/ethereum/external_call.rb +173 -0
- data/lib/ethereum/fast_rlp.rb +81 -0
- data/lib/ethereum/fast_vm.rb +625 -0
- data/lib/ethereum/fast_vm/call_data.rb +44 -0
- data/lib/ethereum/fast_vm/message.rb +29 -0
- data/lib/ethereum/fast_vm/state.rb +25 -0
- data/lib/ethereum/ffi/openssl.rb +390 -0
- data/lib/ethereum/index.rb +97 -0
- data/lib/ethereum/log.rb +43 -0
- data/lib/ethereum/miner.rb +84 -0
- data/lib/ethereum/opcodes.rb +131 -0
- data/lib/ethereum/private_key.rb +92 -0
- data/lib/ethereum/pruning_trie.rb +28 -0
- data/lib/ethereum/public_key.rb +88 -0
- data/lib/ethereum/receipt.rb +53 -0
- data/lib/ethereum/secp256k1.rb +58 -0
- data/lib/ethereum/secure_trie.rb +49 -0
- data/lib/ethereum/sedes.rb +42 -0
- data/lib/ethereum/special_contract.rb +95 -0
- data/lib/ethereum/spv.rb +79 -0
- data/lib/ethereum/spv/proof.rb +31 -0
- data/lib/ethereum/spv/proof_constructor.rb +19 -0
- data/lib/ethereum/spv/proof_verifier.rb +24 -0
- data/lib/ethereum/tester.rb +14 -0
- data/lib/ethereum/tester/abi_contract.rb +65 -0
- data/lib/ethereum/tester/fixture.rb +31 -0
- data/lib/ethereum/tester/language.rb +30 -0
- data/lib/ethereum/tester/log_recorder.rb +13 -0
- data/lib/ethereum/tester/solidity_wrapper.rb +144 -0
- data/lib/ethereum/tester/state.rb +194 -0
- data/lib/ethereum/transaction.rb +196 -0
- data/lib/ethereum/transient_trie.rb +28 -0
- data/lib/ethereum/trie.rb +549 -0
- data/lib/ethereum/trie/nibble_key.rb +184 -0
- data/lib/ethereum/utils.rb +191 -0
- data/lib/ethereum/version.rb +5 -0
- data/lib/ethereum/vm.rb +606 -0
- data/lib/ethereum/vm/call_data.rb +40 -0
- data/lib/ethereum/vm/message.rb +30 -0
- data/lib/ethereum/vm/state.rb +25 -0
- metadata +284 -0
data/lib/ethereum/env.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
# -*- encoding : ascii-8bit -*-
|
2
|
+
|
3
|
+
module Ethereum
|
4
|
+
class Env
|
5
|
+
|
6
|
+
DEFAULT_CONFIG = {
|
7
|
+
# Genesis constants
|
8
|
+
genesis_difficulty: 131072,
|
9
|
+
genesis_gas_limit: 3141592,
|
10
|
+
genesis_prevhash: Constant::HASH_ZERO,
|
11
|
+
genesis_coinbase: Address::ZERO,
|
12
|
+
genesis_nonce: Utils.zpad_int(42, 8),
|
13
|
+
genesis_mixhash: Constant::HASH_ZERO,
|
14
|
+
genesis_timestamp: 0,
|
15
|
+
genesis_extra_data: Constant::BYTE_EMPTY,
|
16
|
+
genesis_initial_alloc: {},
|
17
|
+
|
18
|
+
# Gas limit adjustment algo:
|
19
|
+
#
|
20
|
+
# block.gas_limit = block.parent.gas_limit * 1023/1024 +
|
21
|
+
# (block.gas.used * 6/5) / 1024
|
22
|
+
min_gas_limit: 5000,
|
23
|
+
gaslimit_ema_factor: 1024,
|
24
|
+
gaslimit_adjmax_factor: 1024,
|
25
|
+
blklim_factor_nom: 3,
|
26
|
+
blklim_factor_den: 2,
|
27
|
+
|
28
|
+
block_reward: 5000.finney,
|
29
|
+
nephew_reward: 5000.finney/32, # block_reward/32
|
30
|
+
|
31
|
+
# GHOST constants
|
32
|
+
uncle_depth_penalty_factor: 8,
|
33
|
+
max_uncle_depth: 6, # max (block.number - uncle.number)
|
34
|
+
max_uncles: 2,
|
35
|
+
|
36
|
+
diff_adjustment_cutoff: 13,
|
37
|
+
block_diff_factor: 2048,
|
38
|
+
min_diff: 131072,
|
39
|
+
|
40
|
+
pow_epoch_length: 30000,
|
41
|
+
|
42
|
+
max_extradata_length: 32,
|
43
|
+
|
44
|
+
expdiff_period: 100000,
|
45
|
+
expdiff_free_periods: 2,
|
46
|
+
|
47
|
+
account_initial_nonce: 0,
|
48
|
+
|
49
|
+
homestead_fork_blknum: 1150000,
|
50
|
+
homestead_diff_adjustment_cutoff: 10
|
51
|
+
}.freeze
|
52
|
+
|
53
|
+
attr :db, :config, :global_config
|
54
|
+
|
55
|
+
def initialize(db, config: nil, global_config: {})
|
56
|
+
@db = db
|
57
|
+
@config = config || DEFAULT_CONFIG
|
58
|
+
@global_config = global_config
|
59
|
+
|
60
|
+
raise "invalid nephew/block reward config" unless @config[:nephew_reward] == @config[:block_reward]/32
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# -*- encoding : ascii-8bit -*-
|
2
|
+
|
3
|
+
require 'ethash'
|
4
|
+
require 'ethereum/ethash_ruby'
|
5
|
+
|
6
|
+
module Ethereum
|
7
|
+
module Ethash
|
8
|
+
|
9
|
+
EPOCH_LENGTH = ::Ethash::EPOCH_LENGTH
|
10
|
+
|
11
|
+
CACHE_BY_SEED_MAX = 32
|
12
|
+
|
13
|
+
class <<self
|
14
|
+
def seeds
|
15
|
+
@seeds ||= ["\x00"*32]
|
16
|
+
end
|
17
|
+
|
18
|
+
def get_seed(block_number)
|
19
|
+
epoch_no = block_number / EPOCH_LENGTH
|
20
|
+
while seeds.size <= epoch_no
|
21
|
+
seeds.push Ethereum::Utils.keccak256(seeds.last)
|
22
|
+
end
|
23
|
+
|
24
|
+
seeds[epoch_no]
|
25
|
+
end
|
26
|
+
|
27
|
+
def cache_by_seed
|
28
|
+
@cache_by_seed ||= {} # ordered hash
|
29
|
+
end
|
30
|
+
|
31
|
+
def cache_by_file(block_number, data=nil)
|
32
|
+
path = "/tmp/ruby_ethereum_hashimoto_cache_#{block_number}"
|
33
|
+
if data
|
34
|
+
File.open(path, 'wb') {|f| f.write Marshal.dump(data) }
|
35
|
+
else
|
36
|
+
if File.exist?(path)
|
37
|
+
File.open(path, 'rb') {|f| Marshal.load f.read }
|
38
|
+
else
|
39
|
+
nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def get_cache(blknum)
|
45
|
+
seed = get_seed blknum
|
46
|
+
|
47
|
+
if cache_by_seed.has_key?(seed)
|
48
|
+
c = cache_by_seed.delete seed # pop
|
49
|
+
cache_by_seed[seed] = c # and append at end
|
50
|
+
return c
|
51
|
+
end
|
52
|
+
|
53
|
+
if c = cache_by_file(Utils.encode_hex(seed))
|
54
|
+
cache_by_seed[seed] = c
|
55
|
+
return c
|
56
|
+
end
|
57
|
+
|
58
|
+
# Use c++ implementation or ethash_ruby
|
59
|
+
c = ::Ethash.mkcache_bytes blknum
|
60
|
+
#c = EthashRuby::Cache.new(blknum).to_a
|
61
|
+
|
62
|
+
cache_by_seed[seed] = c
|
63
|
+
cache_by_file Utils.encode_hex(seed), c
|
64
|
+
if cache_by_seed.size > CACHE_BY_SEED_MAX
|
65
|
+
cache_by_seed.delete cache_by_seed.keys.first # remove last recently accessed
|
66
|
+
end
|
67
|
+
|
68
|
+
c
|
69
|
+
end
|
70
|
+
|
71
|
+
def hashimoto_light(blknum, cache, mining_hash, bin_nonce)
|
72
|
+
nonce = Utils.big_endian_to_int(bin_nonce)
|
73
|
+
::Ethash.hashimoto_light blknum, cache, mining_hash, nonce
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# -*- encoding : ascii-8bit -*-
|
2
|
+
|
3
|
+
require 'ethereum/ethash_ruby/utils'
|
4
|
+
require 'ethereum/ethash_ruby/cache'
|
5
|
+
require 'ethereum/ethash_ruby/hashimoto'
|
6
|
+
|
7
|
+
module Ethereum
|
8
|
+
module EthashRuby
|
9
|
+
|
10
|
+
EPOCH_LENGTH = 30000 # blocks per epoch
|
11
|
+
ACCESSES = 64 # number of accesses in hashimoto loop
|
12
|
+
|
13
|
+
DATASET_BYTES_INIT = 2**30 # bytes in dataset at genesis
|
14
|
+
DATASET_BYTES_GROWTH = 2**23 # growth per epoch (~ 7GB per year)
|
15
|
+
DATASET_PARENTS = 256 # number of parents of each dataset element
|
16
|
+
|
17
|
+
CACHE_BYTES_INIT = 2**24 # size of the dataset relative to the cache
|
18
|
+
CACHE_BYTES_GROWTH = 2**17 # size of the dataset relative to the cache
|
19
|
+
CACHE_ROUNDS = 3 # number of rounds in cache production
|
20
|
+
|
21
|
+
WORD_BYTES = 4 # bytes in word
|
22
|
+
MIX_BYTES = 128 # width of mix
|
23
|
+
HASH_BYTES = 64 # hash length in bytes
|
24
|
+
|
25
|
+
FNV_PRIME = 0x01000193
|
26
|
+
|
27
|
+
class <<self
|
28
|
+
def hashimoto_light(*args)
|
29
|
+
::Ethereum::Ethash::Hashimoto.new.light(*args)
|
30
|
+
end
|
31
|
+
|
32
|
+
def get_cache(*args)
|
33
|
+
::Ethereum::Ethash::Cache.get(*args)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# -*- encoding : ascii-8bit -*-
|
2
|
+
|
3
|
+
require 'prime'
|
4
|
+
|
5
|
+
module Ethereum
|
6
|
+
module EthashRuby
|
7
|
+
|
8
|
+
class Cache
|
9
|
+
include Utils
|
10
|
+
|
11
|
+
def initialize(block_number)
|
12
|
+
@block_number = block_number
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_a
|
16
|
+
n = size / HASH_BYTES
|
17
|
+
|
18
|
+
o = [keccak512(seed)]
|
19
|
+
(1...n).each {|i| o.push keccak512(o.last) }
|
20
|
+
|
21
|
+
CACHE_ROUNDS.times do
|
22
|
+
n.times do |i|
|
23
|
+
v = o[i][0] % n
|
24
|
+
xor = o[(i-1+n) % n].zip(o[v]).map {|(a,b)| a^b }
|
25
|
+
o[i] = keccak512 xor
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
o
|
30
|
+
end
|
31
|
+
|
32
|
+
def seed
|
33
|
+
@seed ||= self.class.get_seed(@block_number)
|
34
|
+
end
|
35
|
+
|
36
|
+
def size
|
37
|
+
sz = CACHE_BYTES_INIT + CACHE_BYTES_GROWTH * (@block_number / EPOCH_LENGTH)
|
38
|
+
sz -= HASH_BYTES
|
39
|
+
|
40
|
+
sz -= 2 * HASH_BYTES while !Prime.prime?(sz / HASH_BYTES)
|
41
|
+
sz
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# -*- encoding : ascii-8bit -*-
|
2
|
+
|
3
|
+
require 'prime'
|
4
|
+
|
5
|
+
module Ethereum
|
6
|
+
module EthashRuby
|
7
|
+
|
8
|
+
class Hashimoto
|
9
|
+
include Utils
|
10
|
+
|
11
|
+
def light(block_number, cache, header, nonce)
|
12
|
+
run header, nonce, get_full_size(block_number) do |x|
|
13
|
+
calc_dataset_item(cache, x)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def run(header, nonce, full_size, &dataset_lookup)
|
18
|
+
n = full_size / HASH_BYTES
|
19
|
+
w = MIX_BYTES / WORD_BYTES
|
20
|
+
mixhashes = MIX_BYTES / HASH_BYTES
|
21
|
+
|
22
|
+
s = keccak512(header + nonce.reverse)
|
23
|
+
|
24
|
+
mix = []
|
25
|
+
mixhashes.times { mix.concat s }
|
26
|
+
|
27
|
+
ACCESSES.times do |i|
|
28
|
+
p = fnv(i ^ s[0], mix[i % w]) % (n / mixhashes) * mixhashes
|
29
|
+
|
30
|
+
newdata = []
|
31
|
+
mixhashes.times {|j| newdata.concat dataset_lookup.call(p + j) }
|
32
|
+
mix = mix.zip(newdata).map {|(a,b)| fnv(a, b) }
|
33
|
+
end
|
34
|
+
|
35
|
+
cmix = []
|
36
|
+
(mix.size / WORD_BYTES).times do |i|
|
37
|
+
i *= WORD_BYTES
|
38
|
+
cmix.push fnv(fnv(fnv(mix[i], mix[i+1]), mix[i+2]), mix[i+3])
|
39
|
+
end
|
40
|
+
|
41
|
+
{ mixhash: serialize_hash(cmix),
|
42
|
+
result: serialize_hash(keccak256(s + cmix)) }
|
43
|
+
end
|
44
|
+
|
45
|
+
def calc_dataset_item(cache, i)
|
46
|
+
n = cache.size
|
47
|
+
r = HASH_BYTES / WORD_BYTES
|
48
|
+
|
49
|
+
mix = cache[i % n].dup
|
50
|
+
mix[0] ^= i
|
51
|
+
mix = keccak512 mix
|
52
|
+
|
53
|
+
DATASET_PARENTS.times do |j|
|
54
|
+
cache_index = fnv(i ^ j, mix[j % r])
|
55
|
+
mix = mix.zip(cache[cache_index % n]).map {|(v1,v2)| fnv(v1, v2) }
|
56
|
+
end
|
57
|
+
|
58
|
+
keccak512(mix)
|
59
|
+
end
|
60
|
+
|
61
|
+
def fnv(v1, v2)
|
62
|
+
(v1 * FNV_PRIME ^ v2) % Constant::TT32
|
63
|
+
end
|
64
|
+
|
65
|
+
def get_full_size(block_number)
|
66
|
+
sz = DATASET_BYTES_INIT + DATASET_BYTES_GROWTH * (block_number / EPOCH_LENGTH)
|
67
|
+
sz -= MIX_BYTES
|
68
|
+
sz -= 2 * MIX_BYTES while !Prime.prime?(sz / MIX_BYTES)
|
69
|
+
sz
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# -*- encoding : ascii-8bit -*-
|
2
|
+
|
3
|
+
module Ethereum
|
4
|
+
module EthashRuby
|
5
|
+
module Utils
|
6
|
+
|
7
|
+
# sha3 hash function, outputs 64 bytes
|
8
|
+
def keccak512(x)
|
9
|
+
hash_words(x) do |v|
|
10
|
+
Ethereum::Utils.keccak512(v)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def keccak256(x)
|
15
|
+
hash_words(x) do |v|
|
16
|
+
Ethereum::Utils.keccak256(v)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def hash_words(x, &block)
|
21
|
+
x = serialize_hash(x) if x.instance_of?(Array)
|
22
|
+
y = block.call(x)
|
23
|
+
deserialize_hash(y)
|
24
|
+
end
|
25
|
+
|
26
|
+
def serialize_hash(h)
|
27
|
+
h.map {|x| zpad(encode_int(x), WORD_BYTES) }.join
|
28
|
+
end
|
29
|
+
|
30
|
+
def deserialize_hash(h)
|
31
|
+
(h.size / WORD_BYTES).times.map do |i|
|
32
|
+
i *= WORD_BYTES
|
33
|
+
decode_int h[i, WORD_BYTES]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def encode_int(i)
|
38
|
+
# `pack('L<`) will introduce leading zeros
|
39
|
+
Ethereum::Utils.int_to_big_endian(i).reverse
|
40
|
+
end
|
41
|
+
|
42
|
+
# Assumes little endian bit ordering (same as Intel architectures)
|
43
|
+
def decode_int(s)
|
44
|
+
s && !s.empty? ? s.unpack('L<').first : 0
|
45
|
+
end
|
46
|
+
|
47
|
+
def zpad(s, len)
|
48
|
+
s + "\x00" * [0, len - s.size].max
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding : ascii-8bit -*-
|
2
|
+
|
3
|
+
module Ethereum
|
4
|
+
|
5
|
+
class DeprecatedError < StandardError; end
|
6
|
+
class ChecksumError < StandardError; end
|
7
|
+
class FormatError < StandardError; end
|
8
|
+
class ValidationError < StandardError; end
|
9
|
+
class ValueError < StandardError; end
|
10
|
+
class AssertError < StandardError; end
|
11
|
+
|
12
|
+
class UnknownParentError < StandardError; end
|
13
|
+
class InvalidBlock < ValidationError; end
|
14
|
+
class InvalidUncles < ValidationError; end
|
15
|
+
|
16
|
+
class InvalidTransaction < ValidationError; end
|
17
|
+
class UnsignedTransactionError < InvalidTransaction; end
|
18
|
+
class InvalidNonce < InvalidTransaction; end
|
19
|
+
class InsufficientStartGas < InvalidTransaction; end
|
20
|
+
class InsufficientBalance < InvalidTransaction; end
|
21
|
+
class BlockGasLimitReached < InvalidTransaction; end
|
22
|
+
|
23
|
+
class InvalidSPVProof < ValidationError; end
|
24
|
+
|
25
|
+
class ContractCreationFailed < StandardError; end
|
26
|
+
class TransactionFailed < StandardError; end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
# -*- encoding : ascii-8bit -*-
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
module Ethereum
|
6
|
+
|
7
|
+
##
|
8
|
+
# External calls that can be made from inside the VM. To use the EVM with a
|
9
|
+
# different blockchain system, database, set parameters for testing, just
|
10
|
+
# swap out the functions here.
|
11
|
+
#
|
12
|
+
class ExternalCall
|
13
|
+
|
14
|
+
extend Forwardable
|
15
|
+
def_delegators :@block, :get_code, :get_balance, :set_balance, :get_storage_data, :set_storage_data, :add_refund, :account_exists
|
16
|
+
|
17
|
+
def initialize(block, tx)
|
18
|
+
@block = block
|
19
|
+
@tx = tx
|
20
|
+
end
|
21
|
+
|
22
|
+
def log_storage(x)
|
23
|
+
@block.account_to_dict(x)[:storage]
|
24
|
+
end
|
25
|
+
|
26
|
+
def add_suicide(x)
|
27
|
+
@block.suicides.push x
|
28
|
+
end
|
29
|
+
|
30
|
+
def block_hash(x)
|
31
|
+
d = @block.number - x
|
32
|
+
if d > 0 && d <= 256
|
33
|
+
@block.get_ancestor_hash d
|
34
|
+
else
|
35
|
+
Constant::BYTE_EMPTY
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def block_coinbase
|
40
|
+
@block.coinbase
|
41
|
+
end
|
42
|
+
|
43
|
+
def block_timestamp
|
44
|
+
@block.timestamp
|
45
|
+
end
|
46
|
+
|
47
|
+
def block_number
|
48
|
+
@block.number
|
49
|
+
end
|
50
|
+
|
51
|
+
def block_difficulty
|
52
|
+
@block.difficulty
|
53
|
+
end
|
54
|
+
|
55
|
+
def block_gas_limit
|
56
|
+
@block.gas_limit
|
57
|
+
end
|
58
|
+
|
59
|
+
def log(addr, topics, data)
|
60
|
+
@block.add_log Log.new(addr, topics, data)
|
61
|
+
end
|
62
|
+
|
63
|
+
def tx_origin
|
64
|
+
@tx.sender
|
65
|
+
end
|
66
|
+
|
67
|
+
def tx_gasprice
|
68
|
+
@tx.gasprice
|
69
|
+
end
|
70
|
+
|
71
|
+
def post_homestead_hardfork
|
72
|
+
@block.number >= @block.config[:homestead_fork_blknum]
|
73
|
+
end
|
74
|
+
|
75
|
+
def create(msg)
|
76
|
+
log_msg.debug 'CONTRACT CREATION'
|
77
|
+
|
78
|
+
sender = Utils.normalize_address(msg.sender, allow_blank: true)
|
79
|
+
|
80
|
+
@block.increment_nonce msg.sender if tx_origin != msg.sender
|
81
|
+
|
82
|
+
nonce = Utils.encode_int(@block.get_nonce(msg.sender) - 1)
|
83
|
+
msg.to = Utils.mk_contract_address sender, nonce
|
84
|
+
|
85
|
+
balance = get_balance(msg.to)
|
86
|
+
if balance > 0
|
87
|
+
set_balance msg.to, balance
|
88
|
+
@block.set_nonce msg.to, 0
|
89
|
+
@block.set_code msg.to, Constant::BYTE_EMPTY
|
90
|
+
@block.reset_storage msg.to
|
91
|
+
end
|
92
|
+
|
93
|
+
msg.is_create = true
|
94
|
+
|
95
|
+
code = msg.data.extract_all
|
96
|
+
msg.data = VM::CallData.new [], 0, 0
|
97
|
+
snapshot = @block.snapshot
|
98
|
+
|
99
|
+
res, gas, dat = apply_msg msg, code
|
100
|
+
|
101
|
+
if res.true?
|
102
|
+
return 1, gas, msg.to if dat.empty?
|
103
|
+
|
104
|
+
gcost = dat.size * Opcodes::GCONTRACTBYTE
|
105
|
+
if gas >= gcost
|
106
|
+
gas -= gcost
|
107
|
+
else
|
108
|
+
dat = []
|
109
|
+
log_msg.debug "CONTRACT CREATION OOG", have: gas, want: gcost, block_number: @block.number
|
110
|
+
|
111
|
+
if @block.number >= @block.config[:homestead_fork_blknum]
|
112
|
+
@block.revert snapshot
|
113
|
+
return 0, 0, Constant::BYTE_EMPTY
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
@block.set_code msg.to, Utils.int_array_to_bytes(dat)
|
118
|
+
return 1, gas, msg.to
|
119
|
+
else
|
120
|
+
return 0, gas, Constant::BYTE_EMPTY
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def apply_msg(msg, code=nil)
|
125
|
+
code ||= get_code msg.code_address
|
126
|
+
|
127
|
+
log_msg.debug "MSG APPLY", sender: Utils.encode_hex(msg.sender), to: Utils.encode_hex(msg.to), gas: msg.gas, value: msg.value, data: Utils.encode_hex(msg.data.extract_all)
|
128
|
+
log_state.trace "MSG PRE STATE SENDER", account: Utils.encode_hex(msg.sender), balance: get_balance(msg.sender), state: log_storage(msg.sender)
|
129
|
+
log_state.trace "MSG PRE STATE RECIPIENT", account: Utils.encode_hex(msg.to), balance: get_balance(msg.to), state: log_storage(msg.to)
|
130
|
+
|
131
|
+
# snapshot before execution
|
132
|
+
snapshot = @block.snapshot
|
133
|
+
|
134
|
+
# transfer value
|
135
|
+
if msg.transfers_value
|
136
|
+
unless @block.transfer_value(msg.sender, msg.to, msg.value)
|
137
|
+
log_msg.debug "MSG TRANSFER FAILED", have: get_balance(msg.to), want: msg.value
|
138
|
+
return [1, msg.gas, []]
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# main loop
|
143
|
+
if SpecialContract[msg.code_address]
|
144
|
+
res, gas, dat = SpecialContract[msg.code_address].call(self, msg)
|
145
|
+
else
|
146
|
+
res, gas, dat = VM.execute self, msg, code
|
147
|
+
end
|
148
|
+
|
149
|
+
log_msg.trace "MSG APPLIED", gas_remained: gas, sender: msg.sender, to: msg.to, data: dat
|
150
|
+
log_state.trace "MSG POST STATE SENDER", account: Utils.encode_hex(msg.sender), balance: get_balance(msg.sender), state: log_storage(msg.sender)
|
151
|
+
log_state.trace "MSG POST STATE RECIPIENT", account: Utils.encode_hex(msg.to), balance: get_balance(msg.to), state: log_storage(msg.to)
|
152
|
+
|
153
|
+
if res == 0
|
154
|
+
log_msg.debug 'REVERTING'
|
155
|
+
@block.revert snapshot
|
156
|
+
end
|
157
|
+
|
158
|
+
return res, gas, dat
|
159
|
+
end
|
160
|
+
|
161
|
+
private
|
162
|
+
|
163
|
+
def log_msg
|
164
|
+
@log_msg ||= Logger.new 'eth.external_call.msg'
|
165
|
+
end
|
166
|
+
|
167
|
+
def log_state
|
168
|
+
@log_state ||= Logger.new 'eth.external_call.state'
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|