/// <summary> /// Checks if block signature is valid. /// TODO: Update this code to reflect changes made to the corresponding method in <see cref="Features.Consensus.Rules.CommonRules.PosBlockSignatureRule"/>. /// </summary> /// <param name="block">The block.</param> /// <returns><c>true</c> if the signature is valid, <c>false</c> otherwise.</returns> private bool CheckBlockSignature(SmartContractPosBlock block) { if (BlockStake.IsProofOfWork(block)) { bool res = block.BlockSignature.IsEmpty(); this.Logger.LogTrace("(-)[POW]:{0}", res); return(res); } if (block.BlockSignature.IsEmpty()) { this.Logger.LogTrace("(-)[EMPTY]:false"); return(false); } TxOut txout = block.Transactions[1].Outputs[1]; if (PayToPubkeyTemplate.Instance.CheckScriptPubKey(txout.ScriptPubKey)) { PubKey pubKey = PayToPubkeyTemplate.Instance.ExtractScriptPubKeyParameters(txout.ScriptPubKey); bool res = pubKey.Verify(block.GetHash(), new ECDSASignature(block.BlockSignature.Signature)); this.Logger.LogTrace("(-)[P2PK]:{0}", res); return(res); } // Block signing key also can be encoded in the nonspendable output. // This allows to not pollute UTXO set with useless outputs e.g. in case of multisig staking. List <Op> ops = txout.ScriptPubKey.ToOps().ToList(); if (!ops.Any()) // script.GetOp(pc, opcode, vchPushValue)) { this.Logger.LogTrace("(-)[NO_OPS]:false"); return(false); } if (ops.ElementAt(0).Code != OpcodeType.OP_RETURN) // OP_RETURN) { this.Logger.LogTrace("(-)[NO_OP_RETURN]:false"); return(false); } if (ops.Count < 2) // script.GetOp(pc, opcode, vchPushValue) { this.Logger.LogTrace("(-)[NO_SECOND_OP]:false"); return(false); } byte[] data = ops.ElementAt(1).PushData; if (!ScriptEvaluationContext.IsCompressedOrUncompressedPubKey(data)) { this.Logger.LogTrace("(-)[NO_PUSH_DATA]:false"); return(false); } bool verifyRes = new PubKey(data).Verify(block.GetHash(), new ECDSASignature(block.BlockSignature.Signature)); return(verifyRes); }
public static bool IsCanonicalBlockSignature(Block block, bool checkLowS) { if (BlockStake.IsProofOfWork(block)) { return(block.BlockSignatur.IsEmpty()); } return(checkLowS ? ScriptEvaluationContext.IsLowDerSignature(block.BlockSignatur.Signature) : ScriptEvaluationContext.IsValidSignatureEncoding(block.BlockSignatur.Signature)); }
public async Task RunAsync_ProofOfWorkBlock_ValidBlock_DoesNotThrowExceptionAsync() { var transaction = this.network.CreateTransaction(); transaction.Outputs.Add(new TxOut(Money.Zero, (IDestination)null)); this.ruleContext.ValidationContext.BlockToValidate.Transactions.Add(transaction); this.ruleContext.ValidationContext.BlockToValidate.Header.Time = (uint)1483747200; Assert.True(BlockStake.IsProofOfWork(this.ruleContext.ValidationContext.BlockToValidate)); await this.consensusRules.RegisterRule <PosCoinstakeRule>().RunAsync(this.ruleContext); }
/// <summary> /// Checks if block signature is valid. /// </summary> /// <param name="block">The block.</param> /// <returns><c>true</c> if the signature is valid, <c>false</c> otherwise.</returns> private bool CheckBlockSignature(PosBlock block) { if (BlockStake.IsProofOfWork(block)) { bool res = block.BlockSignature.IsEmpty(); this.Logger.LogTrace("(-)[POW]:{0}", res); return(res); } var consensusRules = (PosConsensusRuleEngine)this.Parent; return(consensusRules.StakeValidator.CheckStakeSignature(block.BlockSignature, block.GetHash(), block.Transactions[1])); }
public void ComputeStakeModifier(ChainBase chainIndex, ChainedBlock pindex, BlockStake blockStake) { var pindexPrev = pindex.Previous; var blockStakePrev = pindexPrev == null ? null : this.stakeChain.Get(pindexPrev.HashBlock); // compute stake modifier var stakeContext = new StakeModifierContext(); this.ComputeNextStakeModifier(chainIndex, pindexPrev, stakeContext); blockStake.SetStakeModifier(stakeContext.StakeModifier, stakeContext.GeneratedStakeModifier); blockStake.StakeModifierV2 = this.ComputeStakeModifierV2( pindexPrev, blockStakePrev, blockStake.IsProofOfWork() ? pindex.HashBlock : blockStake.PrevoutStake.Hash); }
public void RunAsync_ProofOfWorkBlockSignatureNotEmpty_ThrowsBadBlockSignatureConsensusErrorException() { this.ruleContext.ValidationContext.BlockToValidate = KnownNetworks.StratisMain.Consensus.ConsensusFactory.CreateBlock(); (this.ruleContext.ValidationContext.BlockToValidate as PosBlock).BlockSignature = new BlockSignature() { Signature = new byte[] { 0x2, 0x3 } }; Assert.True(BlockStake.IsProofOfWork(this.ruleContext.ValidationContext.BlockToValidate)); ConsensusErrorException exception = Assert.Throws <ConsensusErrorException>(() => this.consensusRules.RegisterRule <PosBlockSignatureRule>().Run(this.ruleContext)); Assert.Equal(ConsensusErrors.BadBlockSignature, exception.ConsensusError); }
public async Task RunAsync_ProofOfWorkBlockSignatureNotEmpty_ThrowsBadBlockSignatureConsensusErrorExceptionAsync() { this.ruleContext.BlockValidationContext.Block = Network.StratisMain.Consensus.ConsensusFactory.CreateBlock(); (this.ruleContext.BlockValidationContext.Block as PosBlock).BlockSignature = new BlockSignature() { Signature = new byte[] { 0x2, 0x3 } }; Assert.True(BlockStake.IsProofOfWork(this.ruleContext.BlockValidationContext.Block)); var exception = await Assert.ThrowsAsync <ConsensusErrorException>(() => this.consensusRules.RegisterRule <PosBlockSignatureRule>().RunAsync(this.ruleContext)); Assert.Equal(ConsensusErrors.BadBlockSignature, exception.ConsensusError); }
public static bool CheckBlockSignature(Block block) { if (BlockStake.IsProofOfWork(block)) { return(block.BlockSignatur.IsEmpty()); } if (block.BlockSignatur.IsEmpty()) { return(false); } var txout = block.Transactions[1].Outputs[1]; if (PayToPubkeyTemplate.Instance.CheckScriptPubKey(txout.ScriptPubKey)) { var pubKey = PayToPubkeyTemplate.Instance.ExtractScriptPubKeyParameters(txout.ScriptPubKey); return(pubKey.Verify(block.GetHash(), new ECDSASignature(block.BlockSignatur.Signature))); } if (StakeValidator.IsProtocolV3((int)block.Header.Time)) { // Block signing key also can be encoded in the nonspendable output // This allows to not pollute UTXO set with useless outputs e.g. in case of multisig staking var ops = txout.ScriptPubKey.ToOps().ToList(); if (!ops.Any()) // script.GetOp(pc, opcode, vchPushValue)) { return(false); } if (ops.ElementAt(0).Code != OpcodeType.OP_RETURN) // OP_RETURN) { return(false); } if (ops.Count < 2) // script.GetOp(pc, opcode, vchPushValue) { return(false); } var data = ops.ElementAt(1).PushData; if (!ScriptEvaluationContext.IsCompressedOrUncompressedPubKey(data)) { return(false); } return(new PubKey(data).Verify(block.GetHash(), new ECDSASignature(block.BlockSignatur.Signature))); } return(false); }
public async Task RunAsync_ProofOfWorkBlock_TransactionTimestampAfterBlockTimeStamp_ThrowsBlockTimeBeforeTrxConsensusErrorExceptionAsync() { var transaction = this.network.CreateTransaction(); transaction.Outputs.Add(new TxOut(Money.Zero, (IDestination)null)); this.ruleContext.ValidationContext.Block.Transactions.Add(transaction); this.ruleContext.ValidationContext.Block.Header.Time = (uint)1483747200; this.ruleContext.ValidationContext.Block.Transactions[0].Time = (uint)1483747201; Assert.True(BlockStake.IsProofOfWork(this.ruleContext.ValidationContext.Block)); ConsensusErrorException exception = await Assert.ThrowsAsync <ConsensusErrorException>(() => this.consensusRules.RegisterRule <PosCoinstakeRule>().RunAsync(this.ruleContext)); Assert.Equal(ConsensusErrors.BlockTimeBeforeTrx, exception.ConsensusError); }
public void CheckAndComputeStake(ContextInformation context) { var pindex = context.BlockResult.ChainedBlock; var block = context.BlockResult.Block; var blockStake = context.Stake.BlockStake; // Verify hash target and signature of coinstake tx if (BlockStake.IsProofOfStake(block)) { var pindexPrev = pindex.Previous; var prevBlockStake = this.stakeChain.Get(pindexPrev.HashBlock); if (prevBlockStake == null) { ConsensusErrors.PrevStakeNull.Throw(); } this.stakeValidator.CheckProofOfStake(context, pindexPrev, prevBlockStake, block.Transactions[1], pindex.Header.Bits.ToCompact()); } // PoW is checked in CheckBlock() if (BlockStake.IsProofOfWork(block)) { context.Stake.HashProofOfStake = pindex.Header.GetPoWHash(); } // TODO: is this the same as chain work? // compute chain trust score //pindexNew.nChainTrust = (pindexNew->pprev ? pindexNew->pprev->nChainTrust : 0) + pindexNew->GetBlockTrust(); // compute stake entropy bit for stake modifier if (!blockStake.SetStakeEntropyBit(blockStake.GetStakeEntropyBit())) { ConsensusErrors.SetStakeEntropyBitFailed.Throw(); } // Record proof hash value blockStake.HashProof = context.Stake.HashProofOfStake; // compute stake modifier this.stakeValidator.ComputeStakeModifier(this.chain, pindex, blockStake); }
public static bool IsCanonicalBlockSignature(PosBlock block, bool checkLowS) { if (BlockStake.IsProofOfWork(block)) { return(block.BlockSignature.IsEmpty()); } // A signature should have only one representation, or malleability vectors are introduced. // Therefore, an ECDSA signature, per BIP66, must be in strict DER encoding. // Additionally, the 'S' value of the signature must be the lower of the two possible values. // Recall that, for an ECDSA signature, the R and S values are both modulo N (aka the curve order). // Further, a signature (R, S) is equivalent to (R, -S mod N). // In other words there are always 2 valid S values, call them S and S'. // A small example of why S + S' = N: // N = 7 // S = 4 // ((N - S) % N) = 3 = S', therefore S + S' = 7 = N // Given S + S' = N, there will always be one S value greater than half the curve order // (N / 2), and one less than this. // The canonical signature is required to use the so-called 'low S' value, the one less than N / 2. // Therefore to get the other S' value (the complement) we calculate S' = N - S. // We can switch between the canonical and non-canonical form by calculating the complement of // whichever representation we currently have in a signature. // Using N + S will give a valid signature too, but will not give the complement, as (N + S) mod N = S. // For POS blocks that have a signature we do not append a SIGHASH type at the end of the signature. // Therefore IsValidSignatureEncoding should be called with haveSigHash = false when validating // POS blocks. return(checkLowS ? ScriptEvaluationContext.IsLowDerSignature(block.BlockSignature.Signature, false) : ScriptEvaluationContext.IsValidSignatureEncoding(block.BlockSignature.Signature, false)); }
public async Task RunAsync_ProofOfWorkBlock_BlockSignatureEmpty_DoesNotThrowExceptionAsync() { var block = new Block(); block.Transactions.Add(new Transaction()); var transaction = new Transaction(); transaction.Inputs.Add(new TxIn() { PrevOut = new OutPoint(uint256.Zero, uint.MaxValue), ScriptSig = new Script() }); transaction.Outputs.Add(new TxOut(Money.Zero, (IDestination)null)); transaction.Outputs.Add(new TxOut(Money.Zero, (IDestination)null)); block.Transactions.Add(transaction); block.BlockSignatur = new BlockSignature(); this.ruleContext.BlockValidationContext.Block = block; Assert.True(BlockStake.IsProofOfWork(this.ruleContext.BlockValidationContext.Block)); await this.consensusRules.RegisterRule <PosBlockSignatureRule>().RunAsync(this.ruleContext); }
public void RunAsync_ProofOfWorkBlock_BlockSignatureEmpty_DoesNotThrowException() { Block block = KnownNetworks.StratisMain.Consensus.ConsensusFactory.CreateBlock(); block.Transactions.Add(KnownNetworks.StratisMain.CreateTransaction()); Transaction transaction = KnownNetworks.StratisMain.CreateTransaction(); transaction.Inputs.Add(new TxIn() { PrevOut = new OutPoint(uint256.Zero, uint.MaxValue), ScriptSig = new Script() }); transaction.Outputs.Add(new TxOut(Money.Zero, (IDestination)null)); transaction.Outputs.Add(new TxOut(Money.Zero, (IDestination)null)); block.Transactions.Add(transaction); (block as PosBlock).BlockSignature = new BlockSignature(); this.ruleContext.ValidationContext.BlockToValidate = block; Assert.True(BlockStake.IsProofOfWork(this.ruleContext.ValidationContext.BlockToValidate)); this.consensusRules.RegisterRule <PosBlockSignatureRule>().Run(this.ruleContext); }
/// <summary> /// Checks and computes stake. /// </summary> /// <param name="context">Context that contains variety of information regarding blocks validation and execution.</param> /// <exception cref="ConsensusErrors.PrevStakeNull">Thrown if previous stake is not found.</exception> /// <exception cref="ConsensusErrors.SetStakeEntropyBitFailed">Thrown if failed to set stake entropy bit.</exception> private void CheckAndComputeStake(RuleContext context) { ChainedHeader chainedHeader = context.ValidationContext.ChainedHeaderToValidate; Block block = context.ValidationContext.BlockToValidate; var posRuleContext = context as PosRuleContext; if (posRuleContext.BlockStake == null) { posRuleContext.BlockStake = BlockStake.Load(context.ValidationContext.BlockToValidate); } BlockStake blockStake = posRuleContext.BlockStake; // Verify hash target and signature of coinstake tx. if (BlockStake.IsProofOfStake(block)) { ChainedHeader prevChainedHeader = chainedHeader.Previous; BlockStake prevBlockStake = this.stakeChain.Get(prevChainedHeader.HashBlock); if (prevBlockStake == null) { ConsensusErrors.PrevStakeNull.Throw(); } // Only do proof of stake validation for blocks that are after the assumevalid block or after the last checkpoint. if (!context.SkipValidation) { this.stakeValidator.CheckProofOfStake(posRuleContext, prevChainedHeader, prevBlockStake, block.Transactions[1], chainedHeader.Header.Bits.ToCompact()); } else { this.Logger.LogTrace("POS validation skipped for block at height {0}.", chainedHeader.Height); } } // PoW is checked in CheckBlock(). if (BlockStake.IsProofOfWork(block)) { posRuleContext.HashProofOfStake = chainedHeader.Header.GetPoWHash(); } // Compute stake entropy bit for stake modifier. if (!blockStake.SetStakeEntropyBit(blockStake.GetStakeEntropyBit())) { this.Logger.LogTrace("(-)[STAKE_ENTROPY_BIT_FAIL]"); ConsensusErrors.SetStakeEntropyBitFailed.Throw(); } // Record proof hash value. blockStake.HashProof = posRuleContext.HashProofOfStake; int lastCheckpointHeight = this.Parent.Checkpoints.GetLastCheckpointHeight(); if (chainedHeader.Height > lastCheckpointHeight) { // Compute stake modifier. ChainedHeader prevChainedHeader = chainedHeader.Previous; BlockStake blockStakePrev = prevChainedHeader == null ? null : this.stakeChain.Get(prevChainedHeader.HashBlock); blockStake.StakeModifierV2 = this.stakeValidator.ComputeStakeModifierV2(prevChainedHeader, blockStakePrev?.StakeModifierV2, blockStake.IsProofOfWork() ? chainedHeader.HashBlock : blockStake.PrevoutStake.Hash); } else if (chainedHeader.Height == lastCheckpointHeight) { // Copy checkpointed stake modifier. CheckpointInfo checkpoint = this.Parent.Checkpoints.GetCheckpoint(lastCheckpointHeight); blockStake.StakeModifierV2 = checkpoint.StakeModifierV2; this.Logger.LogTrace("Last checkpoint stake modifier V2 loaded: '{0}'.", blockStake.StakeModifierV2); } else { this.Logger.LogTrace("POS stake modifier computation skipped for block at height {0} because it is not above last checkpoint block height {1}.", chainedHeader.Height, lastCheckpointHeight); } }
public void CheckAndComputeStake(ContextInformation context) { this.logger.LogTrace("()"); ChainedBlock chainedBlock = context.BlockResult.ChainedBlock; Block block = context.BlockResult.Block; BlockStake blockStake = context.Stake.BlockStake; int lastCheckpointHeight = this.checkpoints.GetLastCheckpointHeight(); // Verify hash target and signature of coinstake tx. if (BlockStake.IsProofOfStake(block)) { ChainedBlock prevChainedBlock = chainedBlock.Previous; BlockStake prevBlockStake = this.stakeChain.Get(prevChainedBlock.HashBlock); if (prevBlockStake == null) { ConsensusErrors.PrevStakeNull.Throw(); } // Only do proof of stake validation for blocks after last checkpoint. if (chainedBlock.Height > lastCheckpointHeight) { this.stakeValidator.CheckProofOfStake(context, prevChainedBlock, prevBlockStake, block.Transactions[1], chainedBlock.Header.Bits.ToCompact()); } else { this.logger.LogTrace("POS validation skipped for block at height {0} because it is not above last checkpoint block height {1}.", chainedBlock.Height, lastCheckpointHeight); } } // PoW is checked in CheckBlock(). if (BlockStake.IsProofOfWork(block)) { context.Stake.HashProofOfStake = chainedBlock.Header.GetPoWHash(); } // Compute stake entropy bit for stake modifier. if (!blockStake.SetStakeEntropyBit(blockStake.GetStakeEntropyBit())) { this.logger.LogTrace("(-)[STAKE_ENTROPY_BIT_FAIL]"); ConsensusErrors.SetStakeEntropyBitFailed.Throw(); } // Record proof hash value. blockStake.HashProof = context.Stake.HashProofOfStake; if (chainedBlock.Height > lastCheckpointHeight) { // Compute stake modifier. ChainedBlock prevChainedBlock = chainedBlock.Previous; BlockStake blockStakePrev = prevChainedBlock == null ? null : this.stakeChain.Get(prevChainedBlock.HashBlock); blockStake.StakeModifierV2 = this.stakeValidator.ComputeStakeModifierV2(prevChainedBlock, blockStakePrev, blockStake.IsProofOfWork() ? chainedBlock.HashBlock : blockStake.PrevoutStake.Hash); } else if (chainedBlock.Height == lastCheckpointHeight) { // Copy checkpointed stake modifier. CheckpointInfo checkpoint = this.checkpoints.GetCheckpoint(lastCheckpointHeight); blockStake.StakeModifierV2 = checkpoint.StakeModifierV2; this.logger.LogTrace("Last checkpoint stake modifier V2 loaded: '{0}'.", blockStake.StakeModifierV2); } else { this.logger.LogTrace("POS stake modifier computation skipped for block at height {0} because it is not above last checkpoint block height {1}.", chainedBlock.Height, lastCheckpointHeight); } this.logger.LogTrace("(-)[OK]"); }