Example #1
0
  public Program(byte[] ops, ProgramInvoke invokeData) {

    if (ops == null) ops = ByteUtil.EMPTY_BYTE_ARRAY;
    this.ops = ops;

    if (invokeData != null) {
      this.invokeData = invokeData;
      this.programAddress = invokeData.getOwnerAddress();
      this.invokeHash = invokeData.hashCode();
      this.result.setRepository(invokeData.getRepository());
    }
  }
Example #2
0
  public void spendGas(long gasValue, String cause) {
    gasLogger.info("[{}] Spent for cause: [ {} ], gas: [ {} ]", invokeHash, cause, gasValue);

    long afterSpend = invokeData.getGas().longValue() - gasValue - result.getGasUsed();
    if (afterSpend < 0) throw new OutOfGasException();
    result.spendGas(gasValue);
  }
Example #3
0
 public DataWord getDifficulty() {
   return invokeData.getDifficulty().clone();
 }
Example #4
0
 public DataWord getNumber() {
   return invokeData.getNumber().clone();
 }
Example #5
0
 public DataWord getTimestamp() {
   return invokeData.getTimestamp().clone();
 }
Example #6
0
 public DataWord getCoinbase() {
   return invokeData.getCoinbase().clone();
 }
Example #7
0
 public DataWord getPrevHash() {
   return invokeData.getPrevHash().clone();
 }
Example #8
0
 public byte[] getDataCopy(DataWord offset, DataWord length) {
   if (invokeData == null) return ByteUtil.EMPTY_BYTE_ARRAY;
   return invokeData.getDataCopy(offset, length);
 }
Example #9
0
  /**
   * That method implement internal calls and code invocations
   *
   * @param gas - gas to pay for the call, remaining gas will be refunded to the caller
   * @param toAddressDW - address to call
   * @param endowmentValue - the value that can be transfer along with the code execution
   * @param inDataOffs - start of memory to be input data to the call
   * @param inDataSize - size of memory to be input data to the call
   * @param outDataOffs - start of memory to be output of the call
   * @param outDataSize - size of memory to be output data to the call
   */
  public void callToAddress(
      DataWord gas,
      DataWord toAddressDW,
      DataWord endowmentValue,
      DataWord inDataOffs,
      DataWord inDataSize,
      DataWord outDataOffs,
      DataWord outDataSize) {

    ByteBuffer data = memoryChunk(inDataOffs, inDataSize);

    // FETCH THE SAVED STORAGE
    byte[] toAddress = toAddressDW.getLast20Bytes();

    // FETCH THE CODE
    byte[] programCode = this.result.getRepository().getCode(toAddress);

    if (logger.isInfoEnabled())
      logger.info(
          "calling for existing contract: address: [ {} ], outDataOffs: [ {} ], outDataSize: [ {} ]  ",
          Hex.toHexString(toAddress),
          outDataOffs.longValue(),
          outDataSize.longValue());

    byte[] senderAddress = this.getOwnerAddress().getLast20Bytes();

    // 2.1 PERFORM THE GAS VALUE TX
    // (THIS STAGE IS NOT REVERTED BY ANY EXCEPTION)
    if (this.getGas().longValue() - gas.longValue() < 0) {
      logger.info(
          "No gas for the internal call, \n" + "fromAddress={}, toAddress={}",
          Hex.toHexString(senderAddress),
          Hex.toHexString(toAddress));
      this.stackPushZero();
      return;
    }

    BigInteger endowment = endowmentValue.value();
    BigInteger senderBalance = result.getRepository().getBalance(senderAddress);
    if (senderBalance.compareTo(endowment) < 0) {
      stackPushZero();
      return;
    }
    result.getRepository().addBalance(senderAddress, endowment.negate());

    if (invokeData.byTestingSuite()) {
      logger.info("[testing suite] - omit real call");

      stackPushOne();

      this.getResult()
          .addCallCreate(
              data.array(),
              toAddressDW.getLast20Bytes(),
              gas.getNoLeadZeroesData(),
              endowmentValue.getNoLeadZeroesData());

      return;
    }

    //  actual gas subtract
    this.spendGas(gas.intValue(), "internal call");

    RepositoryImpl trackRepositoryImpl = result.getRepository().getTrack();
    trackRepositoryImpl.startTracking();
    trackRepositoryImpl.addBalance(toAddress, endowmentValue.value());

    ProgramInvoke programInvoke =
        ProgramInvokeFactory.createProgramInvoke(
            this,
            toAddressDW,
            endowmentValue,
            gas,
            result.getRepository().getBalance(toAddress),
            data.array(),
            trackRepositoryImpl,
            this.invokeData.getCallDeep() + 1);

    ProgramResult result = null;

    if (programCode != null && programCode.length != 0) {
      VM vm = new VM();
      Program program = new Program(programCode, programInvoke);
      vm.play(program);
      result = program.getResult();
      this.result.addDeleteAccounts(result.getDeleteAccounts());
    }

    if (result != null
        && result.getException() != null
        && result.getException() instanceof Program.OutOfGasException) {
      logger.info("contract run halted by OutOfGas: contract={}", Hex.toHexString(toAddress));

      trackRepositoryImpl.rollback();
      stackPushZero();
      return;
    }

    // 3. APPLY RESULTS: result.getHReturn() into out_memory allocated
    if (result != null) {
      ByteBuffer buffer = result.getHReturn();
      int allocSize = outDataSize.intValue();
      if (buffer != null && allocSize > 0) {
        int retSize = buffer.limit();
        int offset = outDataOffs.intValue();
        if (retSize > allocSize) {
          this.memorySave(offset, buffer.array());
        } else {
          this.memorySave(offset, allocSize, buffer.array());
        }
      }
    }

    // 4. THE FLAG OF SUCCESS IS ONE PUSHED INTO THE STACK
    trackRepositoryImpl.commit();
    stackPushOne();

    // 5. REFUND THE REMAIN GAS
    if (result != null) {
      BigInteger refundGas = gas.value().subtract(BigInteger.valueOf(result.getGasUsed()));
      if (refundGas.compareTo(BigInteger.ZERO) == 1) {

        this.refundGas(refundGas.intValue(), "remaining gas from the internal call");
        logger.info(
            "The remaining gas refunded, account: [ {} ], gas: [ {} ] ",
            Hex.toHexString(senderAddress),
            refundGas.toString());
      }
    } else {
      this.refundGas(gas.intValue(), "remaining gas from the internal call");
    }
  }
Example #10
0
 public DataWord getDataSize() {
   if (invokeData == null) return DataWord.ZERO_EMPTY_ARRAY;
   return invokeData.getDataSize().clone();
 }
Example #11
0
 public DataWord getGas() {
   if (invokeData == null) return DataWord.ZERO_EMPTY_ARRAY;
   long afterSpend = invokeData.getGas().longValue() - result.getGasUsed();
   return new DataWord(afterSpend);
 }
Example #12
0
 public DataWord getGasPrice() {
   if (invokeData == null) return DataWord.ZERO_EMPTY_ARRAY;
   return invokeData.getMinGasPrice().clone();
 }
Example #13
0
 public DataWord getCallerAddress() {
   if (invokeData == null) return DataWord.ZERO_EMPTY_ARRAY;
   return invokeData.getCallerAddress().clone();
 }
Example #14
0
 public DataWord getGaslimit() {
   return invokeData.getGaslimit().clone();
 }
Example #15
0
 public DataWord getDataValue(DataWord index) {
   if (invokeData == null) return DataWord.ZERO_EMPTY_ARRAY;
   return invokeData.getDataValue(index);
 }
Example #16
0
  public void fullTrace() {

    if (logger.isTraceEnabled() || listener != null) {

      StringBuilder stackData = new StringBuilder();
      for (int i = 0; i < stack.size(); ++i) {
        stackData.append(" ").append(stack.get(i));
        if (i < stack.size() - 1) stackData.append("\n");
      }
      if (stackData.length() > 0) stackData.insert(0, "\n");

      ContractDetails contractDetails =
          this.result.getRepository().getContractDetails(this.programAddress.getLast20Bytes());
      StringBuilder storageData = new StringBuilder();
      List<DataWord> storageKeys = new ArrayList<>(contractDetails.getStorage().keySet());
      Collections.sort((List<DataWord>) storageKeys);
      for (DataWord key : storageKeys) {
        storageData
            .append(" ")
            .append(key)
            .append(" -> ")
            .append(contractDetails.getStorage().get(key))
            .append("\n");
      }
      if (storageData.length() > 0) storageData.insert(0, "\n");

      StringBuilder memoryData = new StringBuilder();
      StringBuilder oneLine = new StringBuilder();
      for (int i = 0; memory != null && i < memory.limit(); ++i) {

        byte value = memory.get(i);
        oneLine.append(Utils.oneByteToHexString(value)).append(" ");

        if ((i + 1) % 16 == 0) {
          String tmp =
              String.format("[%4s]-[%4s]", Integer.toString(i - 15, 16), Integer.toString(i, 16))
                  .replace(" ", "0");
          memoryData.append("").append(tmp).append(" ");
          memoryData.append(oneLine);
          if (i < memory.limit()) memoryData.append("\n");
          oneLine.setLength(0);
        }
      }
      if (memoryData.length() > 0) memoryData.insert(0, "\n");

      StringBuilder opsString = new StringBuilder();
      for (int i = 0; i < ops.length; ++i) {

        String tmpString = Integer.toString(ops[i] & 0xFF, 16);
        tmpString = tmpString.length() == 1 ? "0" + tmpString : tmpString;

        if (i != pc) opsString.append(tmpString);
        else opsString.append(" >>").append(tmpString).append("");
      }
      if (pc >= ops.length) opsString.append(" >>");
      if (opsString.length() > 0) opsString.insert(0, "\n ");

      logger.trace(" -- OPS --     {}", opsString);
      logger.trace(" -- STACK --   {}", stackData);
      logger.trace(" -- MEMORY --  {}", memoryData);
      logger.trace(" -- STORAGE -- {}\n", storageData);
      logger.trace(
          "\n  Spent Gas: [ {} ]/[ {} ]\n  Left Gas:  [ {} ]\n",
          result.getGasUsed(),
          invokeData.getGas().longValue(),
          getGas().longValue());

      StringBuilder globalOutput = new StringBuilder("\n");
      if (stackData.length() > 0) stackData.append("\n");

      if (pc != 0) globalOutput.append("[Op: ").append(OpCode.code(lastOp).name()).append("]\n");

      globalOutput.append(" -- OPS --     ").append(opsString).append("\n");
      globalOutput.append(" -- STACK --   ").append(stackData).append("\n");
      globalOutput.append(" -- MEMORY --  ").append(memoryData).append("\n");
      globalOutput.append(" -- STORAGE -- ").append(storageData).append("\n");

      if (result.getHReturn() != null) {
        globalOutput.append("\n  HReturn: ").append(Hex.toHexString(result.getHReturn().array()));
      }

      // soffisticated assumption that msg.data != codedata
      // means we are calling the contract not creating it
      byte[] txData = invokeData.getDataCopy(DataWord.ZERO, getDataSize());
      if (!Arrays.equals(txData, ops)) {
        globalOutput.append("\n  msg.data: ").append(Hex.toHexString(txData));
      }
      globalOutput.append("\n\n  Spent Gas: ").append(result.getGasUsed());

      if (listener != null) {
        listener.output(globalOutput.toString());
      }
    }
  }
Example #17
0
  public void createContract(DataWord value, DataWord memStart, DataWord memSize) {

    if (invokeData.byTestingSuite()) {
      logger.info("[testing suite] - omit real create");
      return;
    }

    // [1] FETCH THE CODE FROM THE MEMORY
    ByteBuffer programCode = memoryChunk(memStart, memSize);

    byte[] senderAddress = this.getOwnerAddress().getLast20Bytes();
    if (logger.isInfoEnabled())
      logger.info(
          "creating a new contract inside contract run: [{}]", Hex.toHexString(senderAddress));

    //  actual gas subtract
    int gas = this.getGas().intValue();
    this.spendGas(gas, "internal call");

    // [2] CREATE THE CONTRACT ADDRESS
    byte[] nonce = result.getRepository().getNonce(senderAddress).toByteArray();
    byte[] newAddress = HashUtil.calcNewAddr(this.getOwnerAddress().getLast20Bytes(), nonce);
    result.getRepository().createAccount(newAddress);

    // [3] UPDATE THE NONCE
    // (THIS STAGE IS NOT REVERTED BY ANY EXCEPTION)
    result.getRepository().increaseNonce(senderAddress);

    // [4] TRANSFER THE BALANCE
    BigInteger endowment = value.value();
    BigInteger senderBalance = result.getRepository().getBalance(senderAddress);
    if (senderBalance.compareTo(endowment) < 0) {
      stackPushZero();
      return;
    }
    result.getRepository().addBalance(senderAddress, endowment.negate());
    result.getRepository().addBalance(newAddress, endowment);

    RepositoryImpl trackRepositoryImpl = result.getRepository().getTrack();
    trackRepositoryImpl.startTracking();

    // [5] COOK THE INVOKE AND EXECUTE
    ProgramInvoke programInvoke =
        ProgramInvokeFactory.createProgramInvoke(
            this,
            new DataWord(newAddress),
            DataWord.ZERO,
            new DataWord(gas),
            BigInteger.ZERO,
            null,
            trackRepositoryImpl,
            this.invokeData.getCallDeep() + 1);

    VM vm = new VM();
    Program program = new Program(programCode.array(), programInvoke);
    vm.play(program);
    ProgramResult result = program.getResult();
    this.result.addDeleteAccounts(result.getDeleteAccounts());

    if (result.getException() != null
        && result.getException() instanceof Program.OutOfGasException) {
      logger.info(
          "contract run halted by OutOfGas: new contract init ={}", Hex.toHexString(newAddress));

      trackRepositoryImpl.rollback();
      stackPushZero();
      return;
    }

    // 4. CREATE THE CONTRACT OUT OF RETURN
    byte[] code = result.getHReturn().array();
    trackRepositoryImpl.saveCode(newAddress, code);

    // IN SUCCESS PUSH THE ADDRESS INTO THE STACK
    stackPush(new DataWord(newAddress));
    trackRepositoryImpl.commit();

    // 5. REFUND THE REMAIN GAS
    long refundGas = gas - result.getGasUsed();
    if (refundGas > 0) {
      this.refundGas(refundGas, "remain gas from the internal call");
      if (logger.isInfoEnabled()) {

        logger.info(
            "The remaining gas is refunded, account: [ {} ], gas: [ {} ] ",
            Hex.toHexString(this.getOwnerAddress().getLast20Bytes()),
            refundGas);
      }
    }
  }