/* This file is part of cpp-ethereum. cpp-ethereum is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Foobar is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Foobar. If not, see . */ /** @file State.cpp * @author Gav Wood * @date 2014 */ #include #include #if WIN32 #pragma warning(push) #pragma warning(disable:4244) #else #pragma GCC diagnostic ignored "-Wunused-function" #endif #include #include #include #if WIN32 #pragma warning(pop) #else #endif #include #include #include "BlockChain.h" #include "Instruction.h" #include "Exceptions.h" #include "Dagger.h" #include "State.h" using namespace std; using namespace eth; u256 const State::c_stepFee = 10000; u256 const State::c_dataFee = 20000; u256 const State::c_memoryFee = 30000; u256 const State::c_extroFee = 40000; u256 const State::c_cryptoFee = 50000; u256 const State::c_newContractFee = 60000; u256 const State::c_txFee = 0; u256 const State::c_blockReward = 1000000000; Overlay State::openDB(std::string _path, bool _killExisting) { if (_path.empty()) _path = Defaults::s_dbPath; boost::filesystem::create_directory(_path); if (_killExisting) boost::filesystem::remove_all(_path + "/state"); ldb::Options o; o.create_if_missing = true; ldb::DB* db = nullptr; ldb::DB::Open(o, _path + "/state", &db); return Overlay(db); } State::State(Address _coinbaseAddress, Overlay const& _db): m_db(_db), m_state(&m_db), m_ourAddress(_coinbaseAddress) { secp256k1_start(); m_state.init(); m_previousBlock = BlockInfo::genesis(); resetCurrent(); } void State::ensureCached(Address _a, bool _requireMemory, bool _forceCreate) const { auto it = m_cache.find(_a); if (it == m_cache.end()) { // populate basic info. string stateBack = m_state.at(_a); if (stateBack.empty() && !_forceCreate) return; RLP state(stateBack); AddressState s; if (state.isNull()) s = AddressState(0, 0); else if (state.itemCount() == 2) s = AddressState(state[0].toInt(), state[1].toInt()); else s = AddressState(state[0].toInt(), state[1].toInt(), state[2].toHash()); bool ok; tie(it, ok) = m_cache.insert(make_pair(_a, s)); } if (_requireMemory && !it->second.haveMemory()) { // Populate memory. assert(it->second.type() == AddressType::Contract); TrieDB memdb(const_cast(&m_db), it->second.oldRoot()); // promise we won't alter the overlay! :) map& mem = it->second.takeMemory(); for (auto const& i: memdb) mem[i.first] = RLP(i.second).toInt(); } } void State::commit() { for (auto const& i: m_cache) if (i.second.type() == AddressType::Dead) m_state.remove(i.first); else { RLPStream s(i.second.type() == AddressType::Contract ? 3 : 2); s << i.second.balance() << i.second.nonce(); if (i.second.type() == AddressType::Contract) { if (i.second.haveMemory()) { TrieDB memdb(&m_db); memdb.init(); for (auto const& j: i.second.memory()) if (j.second) memdb.insert(j.first, rlp(j.second)); s << memdb.root(); } else s << i.second.oldRoot(); } m_state.insert(i.first, &s.out()); } m_cache.clear(); } void State::sync(BlockChain const& _bc) { sync(_bc, _bc.currentHash()); } void State::sync(BlockChain const& _bc, h256 _block) { // BLOCK BlockInfo bi; try { auto b = _bc.block(_block); bi.populate(b); bi.verifyInternals(_bc.block(_block)); } catch (...) { // TODO: Slightly nicer handling? :-) cerr << "ERROR: Corrupt block-chain! Delete your block-chain DB and restart." << endl; exit(1); } // TODO: why are the hashes different when the essentials are the same? // cout << bi << endl; // cout << m_currentBlock << endl; if (bi == m_currentBlock) { // We mined the last block. // Our state is good - we just need to move on to next. m_previousBlock = m_currentBlock; resetCurrent(); m_currentNumber++; } else if (bi == m_previousBlock) { // No change since last sync. // Carry on as we were. } else { // New blocks available, or we've switched to a different branch. All change. // Find most recent state dump and replay what's left. // (Most recent state dump might end up being genesis.) std::vector chain; while (bi.stateRoot != BlockInfo::genesis().hash && m_db.lookup(bi.stateRoot).empty()) // while we don't have the state root of the latest block... { chain.push_back(bi.hash); // push back for later replay. bi.populate(_bc.block(bi.parentHash)); // move to parent. } m_previousBlock = bi; resetCurrent(); // Iterate through in reverse, playing back each of the blocks. for (auto it = chain.rbegin(); it != chain.rend(); ++it) playback(_bc.block(*it), true); m_currentNumber = _bc.details(_block).number + 1; resetCurrent(); } } void State::resetCurrent() { m_transactions.clear(); m_cache.clear(); m_currentBlock = BlockInfo(); m_currentBlock.coinbaseAddress = m_ourAddress; m_currentBlock.stateRoot = m_previousBlock.stateRoot; m_currentBlock.parentHash = m_previousBlock.hash; m_state.setRoot(m_currentBlock.stateRoot); } void State::sync(TransactionQueue& _tq) { // TRANSACTIONS auto ts = _tq.transactions(); for (auto const& i: ts) { if (!m_transactions.count(i.first)) { // don't have it yet! Execute it now. try { Transaction t(i.second); execute(t, t.sender()); } catch (InvalidNonce in) { if (in.required > in.candidate) // too old _tq.drop(i.first); } catch (...) { // Something else went wrong - drop it. _tq.drop(i.first); } } } } u256 State::playback(bytesConstRef _block, bool _fullCommit) { try { m_currentBlock.populate(_block); m_currentBlock.verifyInternals(_block); return playback(_block, BlockInfo(), _fullCommit); } catch (...) { // TODO: Slightly nicer handling? :-) cerr << "ERROR: Corrupt block-chain! Delete your block-chain DB and restart." << endl; exit(1); } } u256 State::playback(bytesConstRef _block, BlockInfo const& _bi, BlockInfo const& _parent, BlockInfo const& _grandParent, bool _fullCommit) { m_currentBlock = _bi; m_previousBlock = _parent; return playback(_block, _grandParent, _fullCommit); } u256 State::playback(bytesConstRef _block, BlockInfo const& _grandParent, bool _fullCommit) { if (m_currentBlock.parentHash != m_previousBlock.hash) throw InvalidParentHash(); // All ok with the block generally. Play back the transactions now... for (auto const& i: RLP(_block)[1]) execute(i.data()); // Initialise total difficulty calculation. u256 tdIncrease = m_currentBlock.difficulty; // Check uncles & apply their rewards to state. Addresses rewarded; for (auto const& i: RLP(_block)[2]) { BlockInfo uncle(i.data()); if (m_previousBlock.parentHash != uncle.parentHash) throw InvalidUncle(); if (_grandParent) uncle.verifyParent(_grandParent); tdIncrease += uncle.difficulty; rewarded.push_back(uncle.coinbaseAddress); } applyRewards(rewarded); // Commit all cached state changes to the state trie. commit(); // cout << m_state << endl << TrieDB(&m_db, m_currentBlock.stateRoot); // Hash the state trie and check against the state_root hash in m_currentBlock. if (m_currentBlock.stateRoot != rootHash()) { // Rollback the trie. m_db.rollback(); throw InvalidStateRoot(); } if (_fullCommit) { // Commit the new trie to disk. m_db.commit(); m_previousBlock = m_currentBlock; resetCurrent(); } else { m_db.rollback(); resetCurrent(); } return tdIncrease; } // @returns the block that represents the difference between m_previousBlock and m_currentBlock. // (i.e. all the transactions we executed). void State::commitToMine(BlockChain const& _bc) { RLPStream uncles; Addresses uncleAddresses; if (m_previousBlock != BlockInfo::genesis()) { // Find uncles if we're not a direct child of the genesis. auto us = _bc.details(m_previousBlock.parentHash).children; assert(us.size() >= 1); // must be at least 1 child of our grandparent - it's our own parent! uncles.appendList(us.size() - 1); // one fewer - uncles precludes our parent from the list of grandparent's children. for (auto const& u: us) if (u != m_previousBlock.hash) // ignore our own parent - it's not an uncle. { BlockInfo ubi(_bc.block(u)); ubi.fillStream(uncles, true); uncleAddresses.push_back(ubi.coinbaseAddress); } } else uncles.appendList(0); applyRewards(uncleAddresses); RLPStream txs(m_transactions.size()); for (auto const& i: m_transactions) i.second.fillStream(txs); txs.swapOut(m_currentTxs); uncles.swapOut(m_currentUncles); m_currentBlock.sha3Transactions = sha3(m_currentTxs); m_currentBlock.sha3Uncles = sha3(m_currentUncles); // Commit any and all changes to the trie that are in the cache, then update the state root accordingly. commit(); m_currentBlock.stateRoot = m_state.root(); m_currentBlock.parentHash = m_previousBlock.hash; } MineInfo State::mine(uint _msTimeout) { // Update timestamp according to clock. m_currentBlock.timestamp = time(0); // Update difficulty according to timestamp. m_currentBlock.difficulty = m_currentBlock.calculateDifficulty(m_previousBlock); // TODO: Miner class that keeps dagger between mine calls (or just non-polling mining). MineInfo ret = m_dagger.mine(/*out*/m_currentBlock.nonce, m_currentBlock.headerHashWithoutNonce(), m_currentBlock.difficulty, _msTimeout); if (ret.completed()) { // Got it! // Commit to disk. m_db.commit(); // Compile block: RLPStream ret; ret.appendList(3); m_currentBlock.fillStream(ret, true); ret.appendRaw(m_currentTxs); ret.appendRaw(m_currentUncles); ret.swapOut(m_currentBytes); m_currentBlock.hash = sha3(m_currentBytes); } else m_currentBytes.clear(); return ret; } bool State::isNormalAddress(Address _id) const { ensureCached(_id, false, false); auto it = m_cache.find(_id); if (it == m_cache.end()) return false; return it->second.type() == AddressType::Normal; } bool State::isContractAddress(Address _id) const { ensureCached(_id, false, false); auto it = m_cache.find(_id); if (it == m_cache.end()) return false; return it->second.type() == AddressType::Contract; } u256 State::balance(Address _id) const { ensureCached(_id, false, false); auto it = m_cache.find(_id); if (it == m_cache.end()) return 0; return it->second.balance(); } void State::noteSending(Address _id) { ensureCached(_id, false, false); auto it = m_cache.find(_id); if (it == m_cache.end()) m_cache[_id] = AddressState(0, 1); else it->second.incNonce(); } void State::addBalance(Address _id, u256 _amount) { ensureCached(_id, false, false); auto it = m_cache.find(_id); if (it == m_cache.end()) m_cache[_id] = AddressState(_amount, 0); else it->second.addBalance(_amount); } void State::subBalance(Address _id, bigint _amount) { ensureCached(_id, false, false); auto it = m_cache.find(_id); if (it == m_cache.end() || (bigint)it->second.balance() < _amount) throw NotEnoughCash(); else it->second.addBalance(-_amount); } u256 State::transactionsFrom(Address _id) const { ensureCached(_id, false, false); auto it = m_cache.find(_id); if (it == m_cache.end()) return 0; else return it->second.nonce(); } u256 State::contractMemory(Address _id, u256 _memory) const { ensureCached(_id, false, false); auto it = m_cache.find(_id); if (it == m_cache.end() || it->second.type() != AddressType::Contract) return 0; else if (it->second.haveMemory()) { auto mit = it->second.memory().find(_memory); if (mit == it->second.memory().end()) return 0; return mit->second; } // Memory not cached - just grab one item from the DB rather than cache the lot. TrieDB memdb(const_cast(&m_db), it->second.oldRoot()); // promise we won't change the overlay! :) return RLP(memdb.at(_memory)).toInt(); // TODO: CHECK: check if this is actually an RLP decode } bool State::execute(bytesConstRef _rlp) { // Entry point for a user-executed transaction. try { Transaction t(_rlp); execute(t, t.sender()); // Add to the user-originated transactions that we've executed. // NOTE: Here, contract-originated transactions will not get added to the transaction list. // If this is wrong, move this line into execute(Transaction const& _t, Address _sender) and // don't forget to allow unsigned transactions in the tx list if they concur with the script execution. m_transactions.insert(make_pair(t.sha3(), t)); return true; } catch (...) { return false; } } void State::applyRewards(Addresses const& _uncleAddresses) { u256 r = c_blockReward; for (auto const& i: _uncleAddresses) { addBalance(i, c_blockReward * 4 / 3); r += c_blockReward / 8; } addBalance(m_currentBlock.coinbaseAddress, r); } void State::execute(Transaction const& _t, Address _sender) { // Entry point for a contract-originated transaction. // Ignore invalid transactions. auto nonceReq = transactionsFrom(_sender); if (_t.nonce != nonceReq) throw InvalidNonce(nonceReq, _t.nonce); // Not considered invalid - just pointless. if (balance(_sender) < _t.value + _t.fee) throw NotEnoughCash(); // TODO: check fee is sufficient? // Increment associated nonce for sender. noteSending(_sender); if (_t.receiveAddress) { subBalance(_sender, _t.value + _t.fee); addBalance(_t.receiveAddress, _t.value); addBalance(m_currentBlock.coinbaseAddress, _t.fee); if (isContractAddress(_t.receiveAddress)) { MinerFeeAdder feeAdder({this, 0}); // will add fee on destruction. execute(_t.receiveAddress, _sender, _t.value, _t.fee, _t.data, &feeAdder.fee); } } else { // Try to make a new contract if (_t.fee < _t.data.size() * c_memoryFee + c_newContractFee) throw FeeTooSmall(); Address newAddress = low160(_t.sha3()); if (isContractAddress(newAddress) || isNormalAddress(newAddress)) throw ContractAddressCollision(); // All OK - set it up. m_cache[newAddress] = AddressState(0, 0, sha3(RLPNull)); auto& mem = m_cache[newAddress].takeMemory(); for (uint i = 0; i < _t.data.size(); ++i) mem[i] = _t.data[i]; subBalance(_sender, _t.value + _t.fee); addBalance(newAddress, _t.value); addBalance(m_currentBlock.coinbaseAddress, _t.fee); } } // Convert from a 256-bit integer stack/memory entry into a 160-bit Address hash. // Currently we just pull out the left (high-order in BE) 160-bits. // TODO: CHECK: check that this is correct. inline Address asAddress(u256 _item) { return left160(h256(_item)); } void State::execute(Address _myAddress, Address _txSender, u256 _txValue, u256 _txFee, u256s const& _txData, u256* _totalFee) { std::vector stack; // Set up some local functions. auto require = [&](u256 _n) { if (stack.size() < _n) throw StackTooSmall(_n, stack.size()); }; ensureCached(_myAddress, true, true); auto& myMemory = m_cache[_myAddress].takeMemory(); auto mem = [&](u256 _n) -> u256 { auto i = myMemory.find(_n); return i == myMemory.end() ? 0 : i->second; }; auto setMem = [&](u256 _n, u256 _v) { if (_v) myMemory[_n] = _v; else myMemory.erase(_n); }; u256 curPC = 0; u256 nextPC = 1; u256 stepCount = 0; for (bool stopped = false; !stopped; curPC = nextPC, nextPC = curPC + 1) { stepCount++; bigint minerFee = stepCount > 16 ? c_stepFee : 0; bigint voidFee = 0; auto rawInst = mem(curPC); if (rawInst > 0xff) throw BadInstruction(); Instruction inst = (Instruction)(uint8_t)rawInst; switch (inst) { case Instruction::STORE: require(2); if (!mem(stack.back()) && stack[stack.size() - 2]) voidFee += c_memoryFee; if (mem(stack.back()) && !stack[stack.size() - 2]) voidFee -= c_memoryFee; // continue on to... case Instruction::LOAD: minerFee += c_dataFee; break; case Instruction::EXTRO: case Instruction::BALANCE: minerFee += c_extroFee; break; case Instruction::MKTX: minerFee += c_txFee; break; case Instruction::SHA256: case Instruction::RIPEMD160: case Instruction::ECMUL: case Instruction::ECADD: case Instruction::ECSIGN: case Instruction::ECRECOVER: case Instruction::ECVALID: minerFee += c_cryptoFee; break; default: break; } if (minerFee + voidFee > balance(_myAddress)) throw NotEnoughCash(); subBalance(_myAddress, minerFee + voidFee); *_totalFee += (u256)minerFee; switch (inst) { case Instruction::ADD: //pops two items and pushes S[-1] + S[-2] mod 2^256. require(2); stack[stack.size() - 2] += stack.back(); stack.pop_back(); break; case Instruction::MUL: //pops two items and pushes S[-1] * S[-2] mod 2^256. require(2); stack[stack.size() - 2] *= stack.back(); stack.pop_back(); break; case Instruction::SUB: require(2); stack[stack.size() - 2] = stack.back() - stack[stack.size() - 2]; stack.pop_back(); break; case Instruction::DIV: require(2); stack[stack.size() - 2] = stack.back() / stack[stack.size() - 2]; stack.pop_back(); break; case Instruction::SDIV: require(2); (s256&)stack[stack.size() - 2] = (s256&)stack.back() / (s256&)stack[stack.size() - 2]; stack.pop_back(); break; case Instruction::MOD: require(2); stack[stack.size() - 2] = stack.back() % stack[stack.size() - 2]; stack.pop_back(); break; case Instruction::SMOD: require(2); (s256&)stack[stack.size() - 2] = (s256&)stack.back() % (s256&)stack[stack.size() - 2]; stack.pop_back(); break; case Instruction::EXP: { // TODO: better implementation? require(2); auto n = stack.back(); auto x = stack[stack.size() - 2]; stack.pop_back(); for (u256 i = 0; i < x; ++i) n *= n; stack.back() = n; break; } case Instruction::NEG: require(1); stack.back() = ~(stack.back() - 1); break; case Instruction::LT: require(2); stack[stack.size() - 2] = stack.back() < stack[stack.size() - 2] ? 1 : 0; stack.pop_back(); break; case Instruction::LE: require(2); stack[stack.size() - 2] = stack.back() <= stack[stack.size() - 2] ? 1 : 0; stack.pop_back(); break; case Instruction::GT: require(2); stack[stack.size() - 2] = stack.back() > stack[stack.size() - 2] ? 1 : 0; stack.pop_back(); break; case Instruction::GE: require(2); stack[stack.size() - 2] = stack.back() >= stack[stack.size() - 2] ? 1 : 0; stack.pop_back(); break; case Instruction::EQ: require(2); stack[stack.size() - 2] = stack.back() == stack[stack.size() - 2] ? 1 : 0; stack.pop_back(); break; case Instruction::NOT: require(1); stack.back() = stack.back() ? 0 : 1; stack.pop_back(); break; case Instruction::MYADDRESS: stack.push_back((u160)_myAddress); break; case Instruction::TXSENDER: stack.push_back((u160)_txSender); break; case Instruction::TXVALUE: stack.push_back(_txValue); break; case Instruction::TXFEE: stack.push_back(_txFee); break; case Instruction::TXDATAN: stack.push_back(_txData.size()); break; case Instruction::TXDATA: require(1); stack.back() = stack.back() < _txData.size() ? _txData[(uint)stack.back()] : 0; break; case Instruction::BLK_PREVHASH: stack.push_back(m_previousBlock.hash); break; case Instruction::BLK_COINBASE: stack.push_back((u160)m_currentBlock.coinbaseAddress); break; case Instruction::BLK_TIMESTAMP: stack.push_back(m_currentBlock.timestamp); break; case Instruction::BLK_NUMBER: stack.push_back(m_currentNumber); break; case Instruction::BLK_DIFFICULTY: stack.push_back(m_currentBlock.difficulty); break; case Instruction::SHA256: { uint s = (uint)min(stack.back(), (u256)(stack.size() - 1) * 32); stack.pop_back(); CryptoPP::SHA256 digest; uint i = 0; for (; s; s = (s >= 32 ? s - 32 : 0), i += 32) { bytes b = toBigEndian(stack.back()); digest.Update(b.data(), (int)min(32, s)); // b.size() == 32 stack.pop_back(); } array final; digest.TruncatedFinal(final.data(), 32); stack.push_back(fromBigEndian(final)); break; } case Instruction::RIPEMD160: { uint s = (uint)min(stack.back(), (u256)(stack.size() - 1) * 32); stack.pop_back(); CryptoPP::RIPEMD160 digest; uint i = 0; for (; s; s = (s >= 32 ? s - 32 : 0), i += 32) { bytes b = toBigEndian(stack.back()); digest.Update(b.data(), (int)min(32, s)); // b.size() == 32 stack.pop_back(); } array final; digest.TruncatedFinal(final.data(), 20); // NOTE: this aligns to right of 256-bit container (low-order bytes). // This won't work if they're treated as byte-arrays and thus left-aligned in a 256-bit container. stack.push_back((u256)fromBigEndian(final)); break; } case Instruction::ECMUL: { // ECMUL - pops three items. // If (S[-2],S[-1]) are a valid point in secp256k1, including both coordinates being less than P, pushes (S[-1],S[-2]) * S[-3], using (0,0) as the point at infinity. // Otherwise, pushes (0,0). require(3); bytes pub(1, 4); pub += toBigEndian(stack[stack.size() - 2]); pub += toBigEndian(stack.back()); stack.pop_back(); stack.pop_back(); bytes x = toBigEndian(stack.back()); stack.pop_back(); if (secp256k1_ecdsa_pubkey_verify(pub.data(), (int)pub.size())) // TODO: Check both are less than P. { secp256k1_ecdsa_pubkey_tweak_mul(pub.data(), (int)pub.size(), x.data()); stack.push_back(fromBigEndian(bytesConstRef(&pub).cropped(1, 32))); stack.push_back(fromBigEndian(bytesConstRef(&pub).cropped(33, 32))); } else { stack.push_back(0); stack.push_back(0); } break; } case Instruction::ECADD: { // ECADD - pops four items and pushes (S[-4],S[-3]) + (S[-2],S[-1]) if both points are valid, otherwise (0,0). require(4); bytes pub(1, 4); pub += toBigEndian(stack[stack.size() - 2]); pub += toBigEndian(stack.back()); stack.pop_back(); stack.pop_back(); bytes tweak(1, 4); tweak += toBigEndian(stack[stack.size() - 2]); tweak += toBigEndian(stack.back()); stack.pop_back(); stack.pop_back(); if (secp256k1_ecdsa_pubkey_verify(pub.data(),(int) pub.size()) && secp256k1_ecdsa_pubkey_verify(tweak.data(),(int) tweak.size())) { secp256k1_ecdsa_pubkey_tweak_add(pub.data(), (int)pub.size(), tweak.data()); stack.push_back(fromBigEndian(bytesConstRef(&pub).cropped(1, 32))); stack.push_back(fromBigEndian(bytesConstRef(&pub).cropped(33, 32))); } else { stack.push_back(0); stack.push_back(0); } break; } case Instruction::ECSIGN: { require(2); bytes sig(64); int v = 0; u256 msg = stack.back(); stack.pop_back(); u256 priv = stack.back(); stack.pop_back(); bytes nonce = toBigEndian(Transaction::kFromMessage(msg, priv)); if (!secp256k1_ecdsa_sign_compact(toBigEndian(msg).data(), 64, sig.data(), toBigEndian(priv).data(), nonce.data(), &v)) throw InvalidSignature(); stack.push_back(v + 27); stack.push_back(fromBigEndian(bytesConstRef(&sig).cropped(0, 32))); stack.push_back(fromBigEndian(bytesConstRef(&sig).cropped(32))); break; } case Instruction::ECRECOVER: { require(4); bytes sig = toBigEndian(stack[stack.size() - 2]) + toBigEndian(stack.back()); stack.pop_back(); stack.pop_back(); int v = (int)stack.back(); stack.pop_back(); bytes msg = toBigEndian(stack.back()); stack.pop_back(); byte pubkey[65]; int pubkeylen = 65; if (secp256k1_ecdsa_recover_compact(msg.data(), (int)msg.size(), sig.data(), pubkey, &pubkeylen, 0, v - 27)) { stack.push_back(0); stack.push_back(0); } else { stack.push_back(fromBigEndian(bytesConstRef(&pubkey[1], 32))); stack.push_back(fromBigEndian(bytesConstRef(&pubkey[33], 32))); } break; } case Instruction::ECVALID: { require(2); bytes pub(1, 4); pub += toBigEndian(stack[stack.size() - 2]); pub += toBigEndian(stack.back()); stack.pop_back(); stack.pop_back(); stack.back() = secp256k1_ecdsa_pubkey_verify(pub.data(), (int)pub.size()) ? 1 : 0; break; } case Instruction::SHA3: { uint s = (uint)min(stack.back(), (u256)(stack.size() - 1) * 32); stack.pop_back(); CryptoPP::SHA3_256 digest; uint i = 0; for (; s; s = (s >= 32 ? s - 32 : 0), i += 32) { bytes b = toBigEndian(stack.back()); digest.Update(b.data(), (int)min(32, s)); // b.size() == 32 stack.pop_back(); } array final; digest.TruncatedFinal(final.data(), 32); stack.push_back(fromBigEndian(final)); break; } case Instruction::PUSH: { stack.push_back(mem(curPC + 1)); nextPC = curPC + 2; break; } case Instruction::POP: require(1); stack.pop_back(); break; case Instruction::DUP: require(1); stack.push_back(stack.back()); break; case Instruction::DUPN: { auto s = mem(curPC + 1); if (s == 0 || s > stack.size()) throw OperandOutOfRange(1, stack.size(), s); stack.push_back(stack[stack.size() - (uint)s]); nextPC = curPC + 2; break; } case Instruction::SWAP: { require(2); auto d = stack.back(); stack.back() = stack[stack.size() - 2]; stack[stack.size() - 2] = d; break; } case Instruction::SWAPN: { require(1); auto d = stack.back(); auto s = mem(curPC + 1); if (s == 0 || s > stack.size()) throw OperandOutOfRange(1, stack.size(), s); stack.back() = stack[stack.size() - (uint)s]; stack[stack.size() - (uint)s] = d; nextPC = curPC + 2; break; } case Instruction::LOAD: require(1); stack.back() = mem(stack.back()); break; case Instruction::STORE: require(2); setMem(stack.back(), stack[stack.size() - 2]); stack.pop_back(); stack.pop_back(); break; case Instruction::JMP: require(1); nextPC = stack.back(); stack.pop_back(); break; case Instruction::JMPI: require(2); if (stack.back()) nextPC = stack[stack.size() - 2]; stack.pop_back(); stack.pop_back(); break; case Instruction::IND: stack.push_back(curPC); break; case Instruction::EXTRO: { require(2); auto memoryAddress = stack.back(); stack.pop_back(); Address contractAddress = asAddress(stack.back()); stack.back() = contractMemory(contractAddress, memoryAddress); break; } case Instruction::BALANCE: { require(1); stack.back() = balance(asAddress(stack.back())); break; } case Instruction::MKTX: { require(4); Transaction t; t.receiveAddress = asAddress(stack.back()); stack.pop_back(); t.value = stack.back(); stack.pop_back(); t.fee = stack.back(); stack.pop_back(); auto itemCount = stack.back(); stack.pop_back(); if (stack.size() < itemCount) throw OperandOutOfRange(0, stack.size(), itemCount); t.data.reserve((uint)itemCount); for (auto i = 0; i < itemCount; ++i) { t.data.push_back(stack.back()); stack.pop_back(); } t.nonce = transactionsFrom(_myAddress); execute(t, _myAddress); break; } case Instruction::SUICIDE: { require(1); Address dest = asAddress(stack.back()); u256 minusVoidFee = myMemory.size() * c_memoryFee; addBalance(dest, balance(_myAddress) + minusVoidFee); m_cache[_myAddress].kill(); // ...follow through to... } case Instruction::STOP: return; default: throw BadInstruction(); } } }