deth
Version:
Ethereum node focused on Developer Experience
101 lines (100 loc) • 3.98 kB
JavaScript
import Account from 'ethereumjs-account';
import { assert } from 'ts-essentials';
import { keccak256 } from 'ethereumjs-util';
import { CheckpointMap } from './CheckpointMap';
import { bufferToHash } from '../../primitives';
const EMPTY_BUFFER = Buffer.from('');
/**
* Holds account, code, storage state in memory.
* Pretends to maintain trie like structure by generating fake state roots and load them as snapshots when needed
*/
export class DethStateManger {
constructor(accountsState = new CheckpointMap(), codeState = new CheckpointMap(), storageState = new CheckpointMap(), savedAccountsState = new CheckpointMap(), savedCodeState = new CheckpointMap(), savedStorageState = new CheckpointMap(), saveIndex = 0) {
this.accountsState = accountsState;
this.codeState = codeState;
this.storageState = storageState;
this.savedAccountsState = savedAccountsState;
this.savedCodeState = savedCodeState;
this.savedStorageState = savedStorageState;
this.saveIndex = saveIndex;
}
copy() {
return new DethStateManger(this.accountsState.copy(), this.codeState.copy(), this.storageState.copy(), this.savedAccountsState.copy(), this.savedCodeState.copy(), this.savedStorageState.copy(), this.saveIndex);
}
getAccount(address) {
var _a;
const account = (_a = this.accountsState.get(address), (_a !== null && _a !== void 0 ? _a : new Account()));
return new Account(account.serialize());
}
putAccount(address, account) {
this.accountsState.set(address, account);
}
putContractCode(address, code) {
const codeHashAsBuffer = keccak256(code);
const codeHash = bufferToHash(codeHashAsBuffer);
const account = this.getAccount(address);
account.codeHash = codeHashAsBuffer;
this.putAccount(address, account);
this.codeState.set(codeHash, code);
}
getContractCode(address) {
const account = this.getAccount(address);
return this.codeState.get(bufferToHash(account.codeHash)) || EMPTY_BUFFER;
}
getContractStorage(address, key) {
var _a, _b;
const s = (_a = this.storageState.get(address), (_a !== null && _a !== void 0 ? _a : {}));
return _b = s[key], (_b !== null && _b !== void 0 ? _b : EMPTY_BUFFER);
}
putContractStorage(address, key, value) {
var _a;
const s = (_a = this.storageState.get(address), (_a !== null && _a !== void 0 ? _a : {}));
const newState = { ...s, [key]: value };
this.storageState.set(address, newState);
}
clearContractStorage(address) {
this.storageState.set(address, {});
}
checkpoint() {
this.accountsState.checkpoint();
this.codeState.checkpoint();
this.storageState.checkpoint();
}
commit() {
this.accountsState.commit();
this.codeState.commit();
this.storageState.commit();
}
revert() {
this.accountsState.revert();
this.codeState.revert();
this.storageState.revert();
}
getStateRoot() {
const i = this.saveIndex++;
const hash = bufferToHash(keccak256(i));
this.savedAccountsState.set(hash, this.accountsState.copy());
this.savedCodeState.set(hash, this.codeState.copy());
this.savedStorageState.set(hash, this.storageState.copy());
return hash;
}
setStateRoot(root) {
{
const s = this.savedAccountsState.get(root);
assert(s, `state root ${root.toString()} doesnt exist`);
this.accountsState = s;
}
{
const s = this.savedCodeState.get(root);
this.codeState = s;
}
{
const s = this.savedStorageState.get(root);
this.storageState = s;
}
}
isAccountEmpty(address) {
// @todo fix, it's simplified implementation
return !!this.accountsState.get(address);
}
}