Skip to content

Commit 6a9cabd

Browse files
committed
Add Memory class for evm and use it in eei
Signed-off-by: Sina Mahmoodi <itz.s1na@gmail.com>
1 parent 3beec0f commit 6a9cabd

File tree

3 files changed

+85
-63
lines changed

3 files changed

+85
-63
lines changed

lib/runCode.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const Block = require('ethereumjs-block')
1818
const lookupOpInfo = require('./vm/opcodes.js')
1919
const opFns = require('./vm/opFns.js')
2020
const EEI = require('./vm/eei')
21+
const Memory = require('./vm/memory')
2122
const exceptions = require('./exceptions.js')
2223
const StorageReader = require('./storageReader')
2324
const setImmediate = require('timers').setImmediate
@@ -76,7 +77,7 @@ module.exports = function (opts, cb) {
7677
gasLeft: new BN(opts.gasLimit),
7778
gasLimit: new BN(opts.gasLimit),
7879
gasPrice: opts.gasPrice,
79-
memory: [],
80+
memory: new Memory(),
8081
memoryWordCount: new BN(0),
8182
stack: [],
8283
lastReturned: [],

lib/vm/eei.js

Lines changed: 45 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -144,14 +144,12 @@ module.exports = class EEI {
144144
}
145145

146146
/**
147-
* Loads bytes from memory and returns them as a buffer. If an error occurs
148-
* a string is instead returned. The function also subtracts the amount of
149-
* gas need for memory expansion.
147+
* Loads bytes from memory and returns them as a buffer. The function
148+
* also subtracts the amount of gas need for memory expansion.
150149
* @method memLoad
151-
* @param {Object} runState
152150
* @param {BN} offset where to start reading from
153151
* @param {BN} length how far to read
154-
* @returns {Buffer|String}
152+
* @returns {Buffer}
155153
*/
156154
memLoad (offset, length) {
157155
// check to see if we have enougth gas for the mem read
@@ -166,58 +164,19 @@ module.exports = class EEI {
166164
offset = offset.toNumber()
167165
length = length.toNumber()
168166

169-
var loaded = this._runState.memory.slice(offset, offset + length)
170-
// fill the remaining lenth with zeros
171-
for (var i = loaded.length; i < length; i++) {
172-
loaded[i] = 0
173-
}
174-
return Buffer.from(loaded)
175-
}
176-
177-
/**
178-
* Subtracts the amount needed for memory usage from `runState.gasLeft`
179-
* @method subMemUsage
180-
* @param {Object} runState
181-
* @param {BN} offset
182-
* @param {BN} length
183-
* @returns {String}
184-
*/
185-
subMemUsage (offset, length) {
186-
// YP (225): access with zero length will not extend the memory
187-
if (length.isZero()) return
188-
189-
const newMemoryWordCount = offset.add(length).divCeil(new BN(32))
190-
if (newMemoryWordCount.lte(this._runState.memoryWordCount)) return
191-
192-
const words = newMemoryWordCount
193-
const fee = new BN(this._runState._common.param('gasPrices', 'memory'))
194-
const quadCoeff = new BN(this._runState._common.param('gasPrices', 'quadCoeffDiv'))
195-
// words * 3 + words ^2 / 512
196-
const cost = words.mul(fee).add(words.mul(words).div(quadCoeff))
197-
198-
if (cost.gt(this._runState.highestMemCost)) {
199-
this.useGas(cost.sub(this._runState.highestMemCost))
200-
this._runState.highestMemCost = cost
201-
}
202-
203-
this._runState.memoryWordCount = newMemoryWordCount
204-
}
205-
206-
trap (err) {
207-
throw new VmError(err)
167+
return this._runState.memory.read(offset, length)
208168
}
209169

210170
/**
211-
* Stores bytes to memory. If an error occurs a string is instead returned.
212-
* The function also subtracts the amount of gas need for memory expansion.
171+
* Stores bytes to memory. The function also subtracts the amount
172+
* of gas need for memory expansion.
213173
* @method memStore
214-
* @param {Object} runState
215174
* @param {BN} offset where to start reading from
216175
* @param {Buffer} val
217176
* @param {BN} valOffset
218177
* @param {BN} length how far to read
219178
* @param {Boolean} skipSubMem
220-
* @returns {Buffer|String}
179+
* @returns {Buffer}
221180
*/
222181
memStore (offset, val, valOffset, length, skipSubMem) {
223182
if (skipSubMem !== false) {
@@ -246,23 +205,47 @@ module.exports = class EEI {
246205
safeLen = val.length
247206
}
248207

249-
let i = 0
250-
if (safeLen > 0) {
251-
safeLen = safeLen > length ? length : safeLen
252-
for (; i < safeLen; i++) {
253-
this._runState.memory[offset + i] = val[valOffset + i]
254-
}
208+
safeLen = safeLen > length ? length : safeLen
209+
val = val.slice(valOffset)
210+
// Pad the remaining length with zeros
211+
if (val.length > 0 && safeLen < length) {
212+
val = val.fill(0, safeLen, length)
255213
}
256214

257-
/*
258-
pad the remaining length with zeros IF AND ONLY IF a value was stored
259-
(even if value offset > value length, strange spec...)
260-
*/
261-
if (val.length > 0 && i < length) {
262-
for (; i < length; i++) {
263-
this._runState.memory[offset + i] = 0
264-
}
215+
this._runState.memory.write(offset, safeLen, val.slice(valOffset))
216+
}
217+
218+
/**
219+
* Subtracts the amount needed for memory usage from `runState.gasLeft`
220+
* @method subMemUsage
221+
* @param {Object} runState
222+
* @param {BN} offset
223+
* @param {BN} length
224+
* @returns {String}
225+
*/
226+
subMemUsage (offset, length) {
227+
// YP (225): access with zero length will not extend the memory
228+
if (length.isZero()) return
229+
230+
const newMemoryWordCount = offset.add(length).divCeil(new BN(32))
231+
if (newMemoryWordCount.lte(this._runState.memoryWordCount)) return
232+
233+
const words = newMemoryWordCount
234+
const fee = new BN(this._runState._common.param('gasPrices', 'memory'))
235+
const quadCoeff = new BN(this._runState._common.param('gasPrices', 'quadCoeffDiv'))
236+
// words * 3 + words ^2 / 512
237+
const cost = words.mul(fee).add(words.mul(words).div(quadCoeff))
238+
239+
if (cost.gt(this._runState.highestMemCost)) {
240+
this.useGas(cost.sub(this._runState.highestMemCost))
241+
this._runState.highestMemCost = cost
265242
}
243+
244+
this._runState.memoryWordCount = newMemoryWordCount
245+
}
246+
247+
trap (err) {
248+
throw new VmError(err)
266249
}
267250

268251
// checks if a jump is valid given a destination

lib/vm/memory.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* Memory implements a simple memory model
3+
* for the ethereum virtual machine.
4+
*/
5+
module.exports = class Memory {
6+
constructor () {
7+
this._store = []
8+
}
9+
10+
/**
11+
* Writes a byte array with length `size` to memory, starting from `offset`.
12+
* @param {Number} offset - Starting position
13+
* @param {Number} size - How many bytes to write
14+
* @param {Buffer} value - Value
15+
*/
16+
write (offset, size, value) {
17+
if (size > 0) {
18+
for (let i = 0; i < size; i++) {
19+
this._store[offset + i] = value[i]
20+
}
21+
}
22+
}
23+
24+
/**
25+
* Reads a slice of memory from `offset` till `offset + size` as a `Buffer`.
26+
* @param {Number} offset - Starting position
27+
* @param {Number} size - How many bytes to read
28+
* @returns {Buffer}
29+
*/
30+
read (offset, size) {
31+
const loaded = this._store.slice(offset, offset + size)
32+
// Fill the remaining lenth with zeros
33+
for (let i = loaded.length; i < size; i++) {
34+
loaded[i] = 0
35+
}
36+
return Buffer.from(loaded)
37+
}
38+
}

0 commit comments

Comments
 (0)