contract BatchMintableToken is StandardToken, Ownable {
  event Mint(address indexed to, uint256 amount);
  event MintFinished();

  struct Batch {
      string pubKey;
      bool minted;
  }

  Batch[MAX_BATCHES] private batches;
  uint256 private mintedCount = 0;
  bool public mintingFinished = false;
  
  /* Max supply = SUPPLY_PER_BATCH * MAX_BATCHES */
  uint256 constant internal SUPPLY_PER_BATCH = 3000;
  uint256 constant internal MAX_BATCHES = 50;

 constructor(string[] memory pubKeys) public {
      require(pubKeys.length <= MAX_BATCHES, 'Too many batches specified');
      for(uint256 i=0; i < pubKeys.length; i++) {
          batches[i].pubKey = pubKeys[i];
          batches[i].minted = false;
      }
  }

  modifier canMint() {
    require(!mintingFinished, 'minting is finished');
    _;
  }

  /**
   * @dev Function to mint tokens
   * @param _batchIndex 0-based index in batches array for the current batch required to mint.
   * @param _priKey the private key which SHA256 corresponds to the public key associated to the specified batch.
   * @return A boolean that indicates if the operation was successful.
   */
  function mint(uint256 _batchIndex, string memory _priKey) onlyOwner canMint public returns (bool) {
    address _to = owner;
    Batch storage batch = batches[_batchIndex];
    require(!batch.minted, 'this batch is already minted');
    require(keccak256(abi.encodePacked(batch.pubKey)) == keccak256(abi.encodePacked(sha256(abi.encodePacked(_priKey)))), 'the private key does not match with public key');
    uint256 _amount = SUPPLY_PER_BATCH;
    totalSupply_ = totalSupply_.add(_amount);
    balances[_to] = balances[_to].add(_amount);
    batch.minted = true;
    mintedCount += 1;
    if(mintedCount == batches.length) {
        finishMinting();
    }
    emit Mint(_to, _amount);
    emit Transfer(address(0), _to, _amount);
    return true;
  }

  /**
   * @dev Function to stop minting new tokens.
   * @return True if the operation was successful.
   */
  function finishMinting() onlyOwner canMint public returns (bool) {
    mintingFinished = true;
    emit MintFinished();
    return true;
  }
}
/**
 * @title Batch Mintable token
 * @dev Standard ERC20 Token, with mintable token creation arranged in batches
 */
function mint(uint256 _batchIndex, string memory _priKey) onlyOwner canMint public returns (bool) {
    address _to = owner;
    Batch storage batch = batches[_batchIndex];
    require(!batch.minted, 'this batch is already minted');
    require(keccak256(abi.encodePacked(batch.pubKey)) == keccak256(abi.encodePacked(sha256(abi.encodePacked(_priKey)))), 'the private key does not match with public key');
    uint256 _amount = SUPPLY_PER_BATCH;
    totalSupply_ = totalSupply_.add(_amount);
    balances[_to] = balances[_to].add(_amount);
    batch.minted = true;
    mintedCount += 1;
    if(mintedCount == batches.length) {
        finishMinting();
    }
    emit Mint(_to, _amount);
    emit Transfer(address(0), _to, _amount);
    return true;
  }
/**
 * @dev Function to mint tokens
 * @param _batchIndex 0-based index in batches array for the current batch required to mint.
 * @param _priKey the private key which SHA256 corresponds to the public key associated to the specified batch.
 * @return A boolean that indicates if the operation was successful.
 */