ruby-ethereum 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|