use of org.aion.zero.impl.types.Block in project aion by aionnetwork.
the class AionBlockchainImpl method isValid.
public boolean isValid(BlockHeader header) {
/*
* The block header should already be validated at this point by P2P or mining,
* but we are including the validation in case future import paths forget to add it.
*/
if (!this.headerValidator.validate(header, LOG)) {
return false;
}
Block[] threeGenParents = repository.getBlockStore().getThreeGenerationBlocksByHashWithInfo(header.getParentHash());
Block parentBlock = threeGenParents[0];
if (parentBlock == null) {
return false;
}
Block grandparentBlock = threeGenParents[1];
Block greatGrandparentBlock = threeGenParents[2];
if (header.getSealType() == Seal.PROOF_OF_WORK) {
if (forkUtility.isUnityForkActive(header.getNumber())) {
if (grandparentBlock == null || greatGrandparentBlock == null) {
return false;
}
return unityParentBlockHeaderValidator.validate(header, parentBlock.getHeader(), LOG, null) && unityGreatGrandParentBlockHeaderValidator.validate(grandparentBlock.getHeader(), greatGrandparentBlock.getHeader(), header, LOG);
} else {
return preUnityParentBlockHeaderValidator.validate(header, parentBlock.getHeader(), LOG, null) && preUnityGrandParentBlockHeaderValidator.validate(parentBlock.getHeader(), grandparentBlock == null ? null : grandparentBlock.getHeader(), header, LOG);
}
} else if (header.getSealType() == Seal.PROOF_OF_STAKE) {
if (!forkUtility.isUnityForkActive(header.getNumber())) {
LOG.warn("Trying to import a Staking block when the Unity fork is not active.");
return false;
}
if (grandparentBlock == null) {
LOG.warn("Staking block {} cannot find its grandparent", header.getNumber());
return false;
}
if (forkUtility.isUnityForkBlock(parentBlock.getNumber())) {
BigInteger expectedDiff = calculateFirstPoSDifficultyAtBlock(parentBlock);
if (!expectedDiff.equals(header.getDifficultyBI())) {
return false;
}
grandparentBlock = new GenesisStakingBlock(expectedDiff);
} else if (forkUtility.isNonceForkBlock(parentBlock.getNumber())) {
BigInteger expectedDiff = calculateFirstPoSDifficultyAtBlock(parentBlock);
if (!expectedDiff.equals(header.getDifficultyBI())) {
return false;
}
}
BigInteger stake = null;
try {
stake = getStakingContractHelper().getEffectiveStake(new AionAddress(AddressSpecs.computeA0Address(((StakingBlockHeader) header).getSigningPublicKey())), ((StakingBlockHeader) header).getCoinbase(), parentBlock);
} catch (Exception e) {
LOG.error("Shutdown due to a fatal error encountered while getting the effective stake.", e);
System.exit(SystemExitCodes.FATAL_VM_ERROR);
}
boolean result = unityParentBlockHeaderValidator.validate(header, parentBlock.getHeader(), LOG, stake);
if (result) {
if (forkUtility.isSignatureSwapForkActive(header.getNumber())) {
result = vrfProofValidator.validate(parentBlock.getHeader(), grandparentBlock.getHeader(), header, LOG) && difficultyValidateAfterSeedNonceFork(grandparentBlock.getHeader(), greatGrandparentBlock.getHeader(), header);
} else if (forkUtility.isNonceForkActive(header.getNumber())) {
result = nonceSeedValidator.validate(grandparentBlock.getHeader(), parentBlock.getHeader(), header, LOG) && difficultyValidateAfterSeedNonceFork(grandparentBlock.getHeader(), greatGrandparentBlock.getHeader(), header);
} else {
result = unityGreatGrandParentBlockHeaderValidator.validate(grandparentBlock.getHeader(), greatGrandparentBlock.getHeader(), header, LOG);
}
}
return result;
} else {
LOG.debug("Invalid header seal type!");
return false;
}
}
use of org.aion.zero.impl.types.Block in project aion by aionnetwork.
the class AionBlockchainImpl method getListOfHashesStartFromBlock.
@Override
public List<byte[]> getListOfHashesStartFromBlock(long blockNumber, int qty) {
// avoiding errors due to negative qty
qty = qty < 1 ? 1 : qty;
long bestNumber = bestBlock.getNumber();
if (blockNumber > bestNumber) {
return emptyList();
}
if (blockNumber + qty - 1 > bestNumber) {
qty = (int) (bestNumber - blockNumber + 1);
}
long endNumber = blockNumber + qty - 1;
Block block = getBlockByNumber(endNumber);
List<byte[]> hashes = repository.getBlockStore().getListHashesEndWith(block.getHash(), qty);
// asc order of hashes is required in the response
Collections.reverse(hashes);
return hashes;
}
use of org.aion.zero.impl.types.Block in project aion by aionnetwork.
the class AionBlockchainImpl method createNewStakingBlock.
private StakingBlock createNewStakingBlock(Block parent, List<AionTransaction> txs, byte[] newSeed, byte[] signingPublicKey, byte[] coinbase) {
BlockHeader parentHdr = parent.getHeader();
byte[] sealedSeed = newSeed;
if (!forkUtility.isUnityForkActive(parentHdr.getNumber() + 1)) {
LOG.debug("Unity fork has not been enabled! Can't create the staking blocks");
return null;
}
byte[] parentSeed;
BigInteger newDiff;
if (parentHdr.getSealType() == Seal.PROOF_OF_STAKE) {
LOG.warn("Tried to create 2 PoS blocks in a row");
return null;
} else if (parentHdr.getSealType() == Seal.PROOF_OF_WORK) {
if (forkUtility.isUnityForkBlock(parentHdr.getNumber())) {
// this is the first PoS block, use all zeroes as seed, and totalStake / 10 as difficulty
parentSeed = GENESIS_SEED;
newDiff = calculateFirstPoSDifficultyAtBlock(parent);
} else if (forkUtility.isNonceForkBlock(parentHdr.getNumber())) {
BlockHeader parentStakingBlock = getParent(parentHdr).getHeader();
parentSeed = ((StakingBlockHeader) parentStakingBlock).getSeedOrProof();
newDiff = calculateFirstPoSDifficultyAtBlock(parent);
forkUtility.setNonceForkResetDiff(newDiff);
} else {
Block[] blockFamily = repository.getBlockStore().getTwoGenerationBlocksByHashWithInfo(parentHdr.getParentHash());
Objects.requireNonNull(blockFamily[0]);
BlockHeader parentStakingBlock = blockFamily[0].getHeader();
BlockHeader parentStakingBlocksParent = blockFamily[1].getHeader();
parentSeed = ((StakingBlockHeader) parentStakingBlock).getSeedOrProof();
newDiff = chainConfiguration.getUnityDifficultyCalculator().calculateDifficulty(parentStakingBlock, parentStakingBlocksParent);
}
} else {
throw new IllegalStateException("Invalid block type");
}
long newTimestamp;
AionAddress coinbaseAddress = new AionAddress(coinbase);
if (signingPublicKey != null) {
// Create block template for the external stakers.
byte[] proofHash = null;
if (forkUtility.isSignatureSwapForkBlock(parent.getNumber() - 1)) {
if (!VRF_Ed25519.verify(parentSeed, newSeed, signingPublicKey)) {
LOG.debug("Seed verification failed! previousProof:{} newProof:{} pKey:{}", ByteUtil.toHexString(parentSeed), ByteUtil.toHexString(newSeed), ByteUtil.toHexString(signingPublicKey));
return null;
}
proofHash = VRF_Ed25519.generateProofHash(newSeed);
} else if (forkUtility.isSignatureSwapForkActive(parent.getNumber() + 1)) {
byte[] parentSeedHash = VRF_Ed25519.generateProofHash(parentSeed);
if (!VRF_Ed25519.verify(parentSeedHash, newSeed, signingPublicKey)) {
LOG.debug("Seed verification failed! previousProof:{} newProof:{} pKey:{}", ByteUtil.toHexString(parentSeed), ByteUtil.toHexString(newSeed), ByteUtil.toHexString(signingPublicKey));
return null;
}
proofHash = VRF_Ed25519.generateProofHash(newSeed);
} else {
if (!ECKeyEd25519.verify(parentSeed, newSeed, signingPublicKey)) {
LOG.debug("Seed verification failed! previousSeed:{} newSeed:{} pKey:{}", ByteUtil.toHexString(parentSeed), ByteUtil.toHexString(newSeed), ByteUtil.toHexString(signingPublicKey));
return null;
}
if (forkUtility.isNonceForkActive(parentHdr.getNumber() + 1)) {
// new seed generation
BlockHeader parentStakingBlock = getParent(parentHdr).getHeader();
// retrieve components
parentSeed = ((StakingBlockHeader) parentStakingBlock).getSeedOrProof();
byte[] signerAddress = new AionAddress(AddressSpecs.computeA0Address(signingPublicKey)).toByteArray();
;
byte[] powMineHash = ((MiningBlock) parent).getHeader().getMineHash();
byte[] powNonce = ((MiningBlock) parent).getNonce();
int lastIndex = parentSeed.length + signerAddress.length + powMineHash.length + powNonce.length;
byte[] concatenated = new byte[lastIndex + 1];
System.arraycopy(parentSeed, 0, concatenated, 0, parentSeed.length);
System.arraycopy(signerAddress, 0, concatenated, parentSeed.length, signerAddress.length);
System.arraycopy(powMineHash, 0, concatenated, parentSeed.length + signerAddress.length, powMineHash.length);
System.arraycopy(powNonce, 0, concatenated, parentSeed.length + signerAddress.length + powMineHash.length, powNonce.length);
concatenated[lastIndex] = 0;
byte[] hash1 = h256(concatenated);
concatenated[lastIndex] = 1;
byte[] hash2 = h256(concatenated);
sealedSeed = new byte[hash1.length + hash2.length];
System.arraycopy(hash1, 0, sealedSeed, 0, hash1.length);
System.arraycopy(hash2, 0, sealedSeed, hash1.length, hash2.length);
}
}
AionAddress signingAddress = new AionAddress(AddressSpecs.computeA0Address(signingPublicKey));
BigInteger stakes = null;
try {
stakes = getStakingContractHelper().getEffectiveStake(signingAddress, coinbaseAddress, parent);
} catch (Exception e) {
LOG.error("Shutdown due to a fatal error encountered while getting the effective stake.", e);
System.exit(SystemExitCodes.FATAL_VM_ERROR);
}
if (stakes.signum() < 1) {
LOG.debug("The caller {} with coinbase {} has no stake ", signingAddress.toString(), coinbase.toString());
return null;
}
long newDelta = StakingDeltaCalculator.calculateDelta(proofHash == null ? sealedSeed : proofHash, newDiff, stakes);
newTimestamp = Long.max(parent.getHeader().getTimestamp() + newDelta, parent.getHeader().getTimestamp() + 1);
} else {
newTimestamp = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis());
if (parentHdr.getTimestamp() >= newTimestamp) {
newTimestamp = parentHdr.getTimestamp() + 1;
}
}
StakingBlock block;
try {
StakingBlockHeader.Builder headerBuilder = StakingBlockHeader.Builder.newInstance().withParentHash(parent.getHash()).withCoinbase(coinbaseAddress).withNumber(parentHdr.getNumber() + 1).withTimestamp(newTimestamp).withExtraData(minerExtraData).withTxTrieRoot(calcTxTrieRoot(txs)).withEnergyLimit(energyLimitStrategy.getEnergyLimit(parentHdr)).withDifficulty(ByteUtil.bigIntegerToBytes(newDiff, DIFFICULTY_BYTES)).withDefaultStateRoot().withDefaultReceiptTrieRoot().withDefaultLogsBloom().withDefaultSignature().withDefaultSigningPublicKey();
if (signingPublicKey != null) {
headerBuilder.withSigningPublicKey(signingPublicKey);
}
if (forkUtility.isSignatureSwapForkActive(parentHdr.getNumber() + 1)) {
headerBuilder.withProof(sealedSeed);
} else {
headerBuilder.withSeed(sealedSeed);
}
block = new StakingBlock(headerBuilder.build(), txs);
} catch (Exception e) {
throw new RuntimeException(e);
}
BigInteger transactionFee = blockPreSeal(parentHdr, block);
if (transactionFee == null) {
return null;
}
if (signingPublicKey != null) {
stakingBlockTemplate.putIfAbsent(ByteArrayWrapper.wrap(block.getHeader().getMineHash()), block);
}
LOG.debug("GetBlockTemp: {}", block.toString());
return block;
}
use of org.aion.zero.impl.types.Block in project aion by aionnetwork.
the class AionBlockchainImpl method tryFastImport.
/**
* Import block without validity checks and creating the state. Cannot be used for storing the
* pivot which will not have a parent present in the database.
*
* @param block the block to be imported
* @return a result describing the status of the attempted import
*/
public FastImportResult tryFastImport(final Block block) {
lock.lock();
try {
if (block == null) {
LOG.debug("Fast sync import attempted with null block or header.");
return FastImportResult.INVALID_BLOCK;
}
if (block.getTimestamp() > (TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) + this.chainConfiguration.getConstants().getClockDriftBufferTime())) {
LOG.debug("Block {} invalid due to timestamp {}.", block.getShortHash(), block.getTimestamp());
return FastImportResult.INVALID_BLOCK;
}
// check that the block is not already known
Block known = getBlockByHash(block.getHash());
if (known != null && known.getNumber() == block.getNumber()) {
return FastImportResult.KNOWN;
} else {
// a child must be present to import the parent
Block child = getBlockByNumber(block.getNumber() + 1);
if (child == null || !Arrays.equals(child.getParentHash(), block.getHash())) {
return FastImportResult.NO_CHILD;
} else {
// the total difficulty will be updated after the chain is complete
repository.getBlockStore().saveBlock(block, ZERO, true);
LOG.debug("Fast sync block saved: number: {}, hash: {}, child: {}", block.getNumber(), block.getShortHash(), child.getShortHash());
return FastImportResult.IMPORTED;
}
}
} finally {
lock.unlock();
}
}
use of org.aion.zero.impl.types.Block in project aion by aionnetwork.
the class AionBlockchainImpl method tryToConnect.
/**
* Imports a batch of blocks.
*
* @param blockRange the block range to be imported
* @param peerDisplayId the display identifier for the peer who provided the batch
* @return a {@link Triple} containing:
* <ol>
* <li>the best block height after the imports,</li>
* <li>the set of imported hashes,</li>
* <li>the import result for the last imported block</li>
* </ol>
*/
public Triple<Long, Set<ByteArrayWrapper>, ImportResult> tryToConnect(final List<Block> blockRange, String peerDisplayId) {
lock.lock();
try {
ImportResult importResult = null;
Set<ByteArrayWrapper> imported = new HashSet<>();
for (Block block : blockRange) {
Pair<ImportResult, Long> result = tryToConnectWithTimedExecution(new BlockWrapper(block));
importResult = result.getLeft();
long importTime = result.getRight();
// printing additional information when debug is enabled
SYNC_LOG.debug("<import-status: node = {}, hash = {}, number = {}, txs = {}, block time = {}, result = {}, time elapsed = {} ns, block td = {}, chain td = {}>", peerDisplayId, block.getShortHash(), block.getNumber(), block.getTransactionsList().size(), block.getTimestamp(), importResult, importTime, block.getTotalDifficulty(), getTotalDifficulty());
if (checkKernelShutdownForCLI()) {
break;
} else if (!importResult.isStored()) {
// stop at invalid blocks
return Triple.of(bestBlock.getNumber(), imported, importResult);
} else {
imported.add(block.getHashWrapper());
}
}
return Triple.of(bestBlock.getNumber(), imported, importResult);
} finally {
lock.unlock();
checkKernelExit();
}
}
Aggregations