Coverage Summary for Class: GenesisLoaderImpl (org.ethereum.core.genesis)

Class Class, % Method, % Line, %
GenesisLoaderImpl 100% (1/1) 90% (9/10) 76% (79/104)


1 /* 2  * This file is part of RskJ 3  * Copyright (C) 2019 RSK Labs Ltd. 4  * 5  * This program is free software: you can redistribute it and/or modify 6  * it under the terms of the GNU Lesser General Public License as published by 7  * the Free Software Foundation, either version 3 of the License, or 8  * (at your option) any later version. 9  * 10  * This program is distributed in the hope that it will be useful, 11  * but WITHOUT ANY WARRANTY; without even the implied warranty of 12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13  * GNU Lesser General Public License for more details. 14  * 15  * You should have received a copy of the GNU Lesser General Public License 16  * along with this program. If not, see <http://www.gnu.org/licenses/>. 17  */ 18  19 package org.ethereum.core.genesis; 20  21 import co.rsk.core.Coin; 22 import co.rsk.core.RskAddress; 23 import co.rsk.core.bc.BlockExecutor; 24 import co.rsk.db.StateRootHandler; 25 import co.rsk.trie.Trie; 26 import co.rsk.trie.TrieStore; 27 import com.fasterxml.jackson.databind.ObjectMapper; 28 import org.bouncycastle.util.encoders.Hex; 29 import org.ethereum.config.blockchain.upgrades.ActivationConfig; 30 import org.ethereum.config.blockchain.upgrades.ConsensusRule; 31 import org.ethereum.core.AccountState; 32 import org.ethereum.core.Genesis; 33 import org.ethereum.core.GenesisHeader; 34 import org.ethereum.core.Repository; 35 import org.ethereum.crypto.HashUtil; 36 import org.ethereum.db.MutableRepository; 37 import org.ethereum.json.Utils; 38 import org.ethereum.util.ByteUtil; 39 import org.ethereum.util.RLP; 40 import org.ethereum.vm.DataWord; 41 import org.slf4j.Logger; 42 import org.slf4j.LoggerFactory; 43  44 import java.io.InputStream; 45 import java.math.BigInteger; 46 import java.util.HashMap; 47 import java.util.Map; 48  49 /** 50  * 1. Reads the genesis configuration from storage 51  * 2. Loads the genesis initial state and sets the expected state root 52  * 3. Stores the genesis initial state trie in the global storage 53  * 4. Registers the genesis state root in the state root handler 54  */ 55 public class GenesisLoaderImpl implements GenesisLoader { 56  private static final byte[] EMPTY_LIST_HASH = HashUtil.keccak256(RLP.encodeList()); 57  private static final Logger logger = LoggerFactory.getLogger(GenesisLoaderImpl.class); 58  59  private final ActivationConfig activationConfig; 60  private final StateRootHandler stateRootHandler; 61  private final TrieStore trieStore; 62  63  private final BigInteger initialNonce; 64  private final boolean isRsk; 65  private final InputStream resourceAsStream; 66  private final boolean useRskip92Encoding; 67  private final boolean isRskip126Enabled; 68  69  public GenesisLoaderImpl( 70  ActivationConfig activationConfig, 71  StateRootHandler stateRootHandler, 72  TrieStore trieStore, 73  String genesisFile, 74  BigInteger initialNonce, 75  boolean isRsk, 76  boolean useRskip92Encoding, 77  boolean isRskip126Enabled) { 78  this( 79  activationConfig, 80  stateRootHandler, 81  trieStore, 82  GenesisLoaderImpl.class.getResourceAsStream("/genesis/" + genesisFile), 83  initialNonce, 84  isRsk, 85  useRskip92Encoding, 86  isRskip126Enabled 87  ); 88  } 89  90  public GenesisLoaderImpl( 91  ActivationConfig activationConfig, 92  StateRootHandler stateRootHandler, 93  TrieStore trieStore, 94  InputStream resourceAsStream, 95  BigInteger initialNonce, 96  boolean isRsk, 97  boolean useRskip92Encoding, 98  boolean isRskip126Enabled) { 99  this.activationConfig = activationConfig; 100  this.stateRootHandler = stateRootHandler; 101  this.trieStore = trieStore; 102  103  this.initialNonce = initialNonce; 104  this.isRsk = isRsk; 105  this.useRskip92Encoding = useRskip92Encoding; 106  this.isRskip126Enabled = isRskip126Enabled; 107  this.resourceAsStream = resourceAsStream; 108  } 109  110  @Override 111  public Genesis load() { 112  Genesis incompleteGenesis = readFromJson(); 113  Trie genesisTrie = loadGenesisTrie(incompleteGenesis); 114  updateGenesisStateRoot(genesisTrie, incompleteGenesis); 115  return incompleteGenesis; 116  } 117  118  private Genesis readFromJson() { 119  try { 120  try { 121  GenesisJson genesisJson = new ObjectMapper().readValue(resourceAsStream, GenesisJson.class); 122  Genesis genesis = mapFromJson(genesisJson); 123  genesis.flushRLP(); 124  125  return genesis; 126  } finally { 127  resourceAsStream.close(); 128  } 129  } catch (Exception e) { 130  System.err.println("Genesis block configuration is corrupted or not found ./resources/genesis/..."); 131  logger.error("Genesis block configuration is corrupted or not found ./resources/genesis/...", e); 132  System.exit(-1); 133  return null; 134  } 135  } 136  137  private Genesis mapFromJson(GenesisJson json) { 138  byte[] difficulty = Utils.parseData(json.difficulty); 139  byte[] coinbase = Utils.parseData(json.coinbase); 140  141  byte[] timestampBytes = Utils.parseData(json.timestamp); 142  long timestamp = ByteUtil.byteArrayToLong(timestampBytes); 143  144  byte[] parentHash = Utils.parseData(json.parentHash); 145  byte[] extraData = Utils.parseData(json.extraData); 146  147  byte[] gasLimitBytes = Utils.parseData(json.gasLimit); 148  long gasLimit = ByteUtil.byteArrayToLong(gasLimitBytes); 149  150  byte[] bitcoinMergedMiningHeader = null; 151  byte[] bitcoinMergedMiningMerkleProof = null; 152  byte[] bitcoinMergedMiningCoinbaseTransaction = null; 153  byte[] minGasPrice = null; 154  155  if (isRsk) { 156  bitcoinMergedMiningHeader = Utils.parseData(json.bitcoinMergedMiningHeader); 157  bitcoinMergedMiningMerkleProof = Utils.parseData(json.bitcoinMergedMiningMerkleProof); 158  bitcoinMergedMiningCoinbaseTransaction = Utils.parseData(json.bitcoinMergedMiningCoinbaseTransaction); 159  minGasPrice = Utils.parseData(json.getMinimumGasPrice()); 160  } 161  162  Map<RskAddress, AccountState> accounts = new HashMap<>(); 163  Map<RskAddress, byte[]> codes = new HashMap<>(); 164  Map<RskAddress, Map<DataWord, byte[]>> storages = new HashMap<>(); 165  Map<String, AllocatedAccount> alloc = json.getAlloc(); 166  for (Map.Entry<String, AllocatedAccount> accountEntry : alloc.entrySet()) { 167  if(!"00".equals(accountEntry.getKey())) { 168  Coin balance = new Coin(new BigInteger(accountEntry.getValue().getBalance())); 169  BigInteger accountNonce; 170  171  if (accountEntry.getValue().getNonce() != null) { 172  accountNonce = new BigInteger(accountEntry.getValue().getNonce()); 173  } else { 174  accountNonce = initialNonce; 175  } 176  177  AccountState acctState = new AccountState(accountNonce, balance); 178  Contract contract = accountEntry.getValue().getContract(); 179  180  RskAddress address = new RskAddress(accountEntry.getKey()); 181  if (contract != null) { 182  byte[] code = Hex.decode(contract.getCode()); 183  codes.put(address, code); 184  Map<DataWord, byte[]> storage = new HashMap<>(contract.getData().size()); 185  for (Map.Entry<String, String> storageData : contract.getData().entrySet()) { 186  storage.put(DataWord.valueFromHex(storageData.getKey()), Hex.decode(storageData.getValue())); 187  } 188  storages.put(address, storage); 189  } 190  accounts.put(address, acctState); 191  } 192  } 193  194  GenesisHeader header = new GenesisHeader( 195  parentHash, 196  EMPTY_LIST_HASH, 197  Genesis.getZeroHash(), 198  difficulty, 199  0, 200  ByteUtil.longToBytes(gasLimit), 201  0, 202  timestamp, 203  extraData, 204  bitcoinMergedMiningHeader, 205  bitcoinMergedMiningMerkleProof, 206  bitcoinMergedMiningCoinbaseTransaction, 207  minGasPrice, 208  useRskip92Encoding, 209  coinbase); 210  return new Genesis(isRskip126Enabled, accounts, codes, storages, header); 211  } 212  213  private Trie loadGenesisTrie(Genesis genesis) { 214  Repository repository = new MutableRepository(trieStore, new Trie(trieStore)); 215  loadGenesisInitalState(repository, genesis); 216  217  setupPrecompiledContractsStorage(repository); 218  219  repository.commit(); 220  repository.save(); 221  return repository.getTrie(); 222  } 223  224  private void updateGenesisStateRoot(Trie genesisTrie, Genesis genesis) { 225  genesis.setStateRoot(stateRootHandler.convert(genesis.getHeader(), genesisTrie).getBytes()); 226  genesis.flushRLP(); 227  stateRootHandler.register(genesis.getHeader(), genesisTrie); 228  } 229  230  /** 231  * When created, contracts are marked in storage for consistency. 232  * Here, we apply this logic to precompiled contracts depending on consensus rules 233  */ 234  private void setupPrecompiledContractsStorage(Repository repository) { 235  ActivationConfig.ForBlock genesisActivations = activationConfig.forBlock(0); 236  if (genesisActivations.isActivating(ConsensusRule.RSKIP126)) { 237  BlockExecutor.maintainPrecompiledContractStorageRoots(repository, genesisActivations); 238  } 239  } 240  241  public static void loadGenesisInitalState(Repository repository, Genesis genesis) { 242  // first we need to create the accounts, which creates also the associated ContractDetails 243  for (RskAddress accounts : genesis.getAccounts().keySet()) { 244  repository.createAccount(accounts); 245  } 246  247  // second we create contracts whom only have code modifying the preexisting ContractDetails instance 248  for (Map.Entry<RskAddress, byte[]> codeEntry : genesis.getCodes().entrySet()) { 249  RskAddress contractAddress = codeEntry.getKey(); 250  repository.setupContract(contractAddress); 251  repository.saveCode(contractAddress, codeEntry.getValue()); 252  Map<DataWord, byte[]> contractStorage = genesis.getStorages().get(contractAddress); 253  for (Map.Entry<DataWord, byte[]> storageEntry : contractStorage.entrySet()) { 254  repository.addStorageBytes(contractAddress, storageEntry.getKey(), storageEntry.getValue()); 255  } 256  } 257  258  // given the accounts had the proper storage root set from the genesis construction we update the account state 259  for (Map.Entry<RskAddress, AccountState> accountEntry : genesis.getAccounts().entrySet()) { 260  repository.updateAccountState(accountEntry.getKey(), accountEntry.getValue()); 261  } 262  } 263 }