Class: Ethereum::ExternalCall

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/ethereum/external_call.rb

Overview

External calls that can be made from inside the VM. To use the EVM with a different blockchain system, database, set parameters for testing, just swap out the functions here.

Instance Method Summary collapse

Constructor Details

#initialize(block, tx) ⇒ ExternalCall

Returns a new instance of ExternalCall.



17
18
19
20
# File 'lib/ethereum/external_call.rb', line 17

def initialize(block, tx)
  @block = block
  @tx = tx
end

Instance Method Details

#add_suicide(x) ⇒ Object



26
27
28
# File 'lib/ethereum/external_call.rb', line 26

def add_suicide(x)
  @block.suicides.push x
end

#apply_msg(msg, code = nil) ⇒ Object



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/ethereum/external_call.rb', line 124

def apply_msg(msg, code=nil)
  code ||= get_code msg.code_address

  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)
  log_state.trace "MSG PRE STATE SENDER", account: Utils.encode_hex(msg.sender), balance: get_balance(msg.sender), state: log_storage(msg.sender)
  log_state.trace "MSG PRE STATE RECIPIENT", account: Utils.encode_hex(msg.to), balance: get_balance(msg.to), state: log_storage(msg.to)

  # snapshot before execution
  snapshot = @block.snapshot

  # transfer value
  if msg.transfers_value
    unless @block.transfer_value(msg.sender, msg.to, msg.value)
      log_msg.debug "MSG TRANSFER FAILED", have: get_balance(msg.to), want: msg.value
      return [1, msg.gas, []]
    end
  end

  # main loop
  if SpecialContract[msg.code_address]
    res, gas, dat = SpecialContract[msg.code_address].call(self, msg)
  else
    res, gas, dat = VM.execute self, msg, code
  end

  log_msg.trace "MSG APPLIED", gas_remained: gas, sender: msg.sender, to: msg.to, data: dat
  log_state.trace "MSG POST STATE SENDER", account: Utils.encode_hex(msg.sender), balance: get_balance(msg.sender), state: log_storage(msg.sender)
  log_state.trace "MSG POST STATE RECIPIENT", account: Utils.encode_hex(msg.to), balance: get_balance(msg.to), state: log_storage(msg.to)

  if res == 0
    log_msg.debug 'REVERTING'
    @block.revert snapshot
  end

  return res, gas, dat
end

#block_coinbaseObject



39
40
41
# File 'lib/ethereum/external_call.rb', line 39

def block_coinbase
  @block.coinbase
end

#block_difficultyObject



51
52
53
# File 'lib/ethereum/external_call.rb', line 51

def block_difficulty
  @block.difficulty
end

#block_gas_limitObject



55
56
57
# File 'lib/ethereum/external_call.rb', line 55

def block_gas_limit
  @block.gas_limit
end

#block_hash(x) ⇒ Object



30
31
32
33
34
35
36
37
# File 'lib/ethereum/external_call.rb', line 30

def block_hash(x)
  d = @block.number - x
  if d > 0 && d <= 256
    @block.get_ancestor_hash d
  else
    Constant::BYTE_EMPTY
  end
end

#block_numberObject



47
48
49
# File 'lib/ethereum/external_call.rb', line 47

def block_number
  @block.number
end

#block_timestampObject



43
44
45
# File 'lib/ethereum/external_call.rb', line 43

def block_timestamp
  @block.timestamp
end

#create(msg) ⇒ Object



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/ethereum/external_call.rb', line 75

def create(msg)
  log_msg.debug 'CONTRACT CREATION'

  sender = Utils.normalize_address(msg.sender, allow_blank: true)

  @block.increment_nonce msg.sender if tx_origin != msg.sender

  nonce = Utils.encode_int(@block.get_nonce(msg.sender) - 1)
  msg.to = Utils.mk_contract_address sender, nonce

  balance = get_balance(msg.to)
  if balance > 0
    set_balance msg.to, balance
    @block.set_nonce msg.to, 0
    @block.set_code msg.to, Constant::BYTE_EMPTY
    @block.reset_storage msg.to
  end

  msg.is_create = true

  code = msg.data.extract_all
  msg.data = VM::CallData.new [], 0, 0
  snapshot = @block.snapshot

  res, gas, dat = apply_msg msg, code

  if res.true?
    return 1, gas, msg.to if dat.empty?

    gcost = dat.size * Opcodes::GCONTRACTBYTE
    if gas >= gcost
      gas -= gcost
    else
      dat = []
      log_msg.debug "CONTRACT CREATION OOG", have: gas, want: gcost, block_number: @block.number

      if @block.number >= @block.config[:homestead_fork_blknum]
        @block.revert snapshot
        return 0, 0, Constant::BYTE_EMPTY
      end
    end

    @block.set_code msg.to, Utils.int_array_to_bytes(dat)
    return 1, gas, msg.to
  else
    return 0, gas, Constant::BYTE_EMPTY
  end
end

#log(addr, topics, data) ⇒ Object



59
60
61
# File 'lib/ethereum/external_call.rb', line 59

def log(addr, topics, data)
  @block.add_log Log.new(addr, topics, data)
end

#log_storage(x) ⇒ Object



22
23
24
# File 'lib/ethereum/external_call.rb', line 22

def log_storage(x)
  @block.(x)[:storage]
end

#post_homestead_hardforkObject



71
72
73
# File 'lib/ethereum/external_call.rb', line 71

def post_homestead_hardfork
  @block.number >= @block.config[:homestead_fork_blknum]
end

#tx_gaspriceObject



67
68
69
# File 'lib/ethereum/external_call.rb', line 67

def tx_gasprice
  @tx.gasprice
end

#tx_originObject



63
64
65
# File 'lib/ethereum/external_call.rb', line 63

def tx_origin
  @tx.sender
end