def init(self): if self.getIndexBlockByHash(self.CHAIN_HEAD_INDEX) == None: genesisBlockGasLimit = Config.getIntValue("GENESIS_BLOCK_GAS_LIMIT") genesisBlockGasUsed = Config.getIntValue("GENESIS_BLOCK_GAS_USED") genesisBlockGasPrice = Config.getIntValue("GENESIS_BLOCK_GAS_PRICE") genesisBlock = Block() genesisBlock.previousHash = Config.getBytesValue("GENESIS_BLOCK_PREVIOUS_HASH", False) genesisBlock.gasLimit = genesisBlockGasLimit genesisBlock.gasUsed = genesisBlockGasUsed genesisBlock.nonce = Config.getIntValue("GENESIS_BLOCK_NONCE") genesisBlock.bits = Config.getIntValue("GENESIS_BLOCK_DIFFICULTY_BITS", 16) genesisBlock.timestamp = Config.getIntValue("GENESIS_BLOCK_TIMESTAMP") transaction = Transaction(genesisBlockGasLimit, genesisBlockGasPrice) coinbaseData = [] coinbaseData.append(Config.getValue("GENESIS_BLOCK_COINBASE_DATA")) transaction.addCoinbaseInput(coinbaseData) genesisBlockRewards = Config.getDecimalValue("GENESIS_BLOCK_REWARDS") genesisBlockRewards = Units.toUnits(genesisBlockRewards) genesisBlockPublicAddress = Config.getValue("GENESIS_BLOCK_PUBLIC_ADDRESS") genesisBlockPublicAddress = Address.toAddressBytes(genesisBlockPublicAddress) genesisBlockScript = Script.verifySignature() transaction.addOutput(genesisBlockPublicAddress, genesisBlockScript, genesisBlockRewards) transaction.hash() genesisBlock.transactions.append(transaction) genesisBlock.merkleRoot = MerkleTree.getMerkleRoot(genesisBlock.transactions, False) genesisBlock.witnessRoot = MerkleTree.getMerkleRoot(genesisBlock.transactions, True) self.addBlock(genesisBlock)
def isCoinbase(self): if self.outpoint.txId != Config.getBytesValue( 'COINBASE_TRANSACTION_ID', False): return False if self.outpoint.outputIndex != Config.getIntValue( 'COINBASE_OUTPUT_INDEX'): return False return True
def addBlock(self, block): if self.blkValidator.validate(block): try: self.acquireBlockLock() blockHash = block.hash() blockHashBytes = DataType.serialize(blockHash) if self.index.get(blockHashBytes) == None: if not OrphanManager.hasBlock(blockHash): bits = block.bits previousChainWork = None previousHash = block.previousHash blockGasLimit = block.gasLimit blockHeight = 0 chainHeadBlock = self.getChainHeadBlock() chainHeadIndexBlock = self.getIndexBlockByHash(self.CHAIN_HEAD_INDEX) previousIndexBlock = self.getIndexBlockByHash(previousHash) if chainHeadIndexBlock == None: chainHeadIndexBlock = IndexBlock() if previousIndexBlock != None: blockHeight = previousIndexBlock.height + 1 previousChainWork = previousIndexBlock.chainWork if blockHash == Config.getBytesValue('GENESIS_BLOCK_HASH'): previousChainWork = Config.getIntValue('GENESIS_BLOCK_CHAIN_WORK', 16) ''' For case 1, adding to main branch: ''' if previousHash == chainHeadIndexBlock.previousHash or blockHash == Config.getBytesValue('GENESIS_BLOCK_HASH'): ''' For all but the coinbase transaction, apply the following: ''' if not self.blkValidator.verifyNonCoinbaseTransactions(block): return False ''' Reject if coinbase value > sum of block creation fee and transaction fees ''' if not self.blkValidator.verifyCoinbaseValue(block): return False for transaction in block.transactions: for txIn in transaction.inputs: UXTO.removeUnspentTransactionCoin(txIn.outpoint) uxtoOutputs = [] uxtoOutputs.extend(transaction.outputs) uxtoOutputs.extend(transaction.internalOutputs) txOutputSize = 0 for txOut in uxtoOutputs: if txOut.store: txOutputSize += 1 outputIndex = 0 for txOut in uxtoOutputs: UXTO.removeStaleUnspentTransactionScript(txOut) if txOut.store: coin = Coin() coin.output = txOut coin.txOutputSize = txOutputSize coin.height = blockHeight coin.coinbase = transaction.isCoinbase() UXTO.addUnspentTransactionCoin(Outpoint(transaction.hash(), outputIndex), coin) outputIndex += 1 ''' For each transaction, "Add to wallet if mine" ''' ''' For each transaction in the block, delete any matching transaction from the transaction pool ''' MemoryPool.removeTransaction(transaction) chainHeadIndexBlock.chainWork = Bits.getChainworkFromBits(previousChainWork, bits) chainHeadIndexBlock.previousHash = blockHash chainHeadIndexBlock.gasLimit = blockGasLimit chainHeadIndexBlock.height = blockHeight self.index.set(self.CHAIN_HEAD_INDEX, chainHeadIndexBlock.serialize()) else: blockChainWork = Bits.getChainworkFromBits(previousChainWork, bits) chainHeadWork = chainHeadIndexBlock.chainWork hasNewMainChain = blockChainWork > chainHeadWork if hasNewMainChain: ''' For case 3, a side branch becoming the main branch: ''' else: ''' For case 2, adding to a side branch, we don't do anything. ''' if hasNewMainChain: ''' Find the fork block on the main branch which this side branch forks off of ''' forkBlockHash = self.searchForkBlockHash(previousIndexBlock, chainHeadIndexBlock) ''' Redefine the main branch to only go up to this fork block We will set new main chain head below ''' isNewMainChainValid = True ''' For each block on the side branch, from the child of the fork block to the leaf, add to the main branch: ''' prevBlock = self.getBlockByHash(block.previousHash) while prevBlock.hash() != forkBlockHash: ''' Do "branch" checks 3-11 ''' ''' 3) Transaction list must be non-empty ''' if not self.blkValidator.verifyTransactionsNonEmpty(prevBlock): isNewMainChainValid = False ''' 4) Block hash must satisfy claimed nBits proof of work ''' if not self.blkValidator.validateBlockBits(prevBlock.serializeHeader(), prevBlock.bits): isNewMainChainValid = False ''' 5) Block timestamp must not be more than two hours in the future ''' if not self.blkValidator.verifyFutureTimestamp(prevBlock): isNewMainChainValid = False ''' 6) First transaction must be coinbase (i.e. only 1 input, with hash=0, n=-1), the rest must not be ''' if not self.blkValidator.verifyInitialCoinbaseTransaction(prevBlock): isNewMainChainValid = False ''' 7) For each transaction, apply "tx" checks 2-4 2) Make sure neither in or out lists are empty 3) Size in bytes <= TRANSACTION_SIZE_LIMIT 4) Each output value, as well as the total, must be in legal money range ''' for transaction in prevBlock.transactions: if not self.txValidator.verifyInputOutputNonEmpty(transaction): isNewMainChainValid = False if not self.txValidator.verifyTransactionSizeLimit(transaction): isNewMainChainValid = False if not self.txValidator.verifyAllowedOutputValueRange(transaction): isNewMainChainValid = False ''' 8) For the coinbase (first) transaction, scriptSig length must be 2-100 ''' if not self.blkValidator.verifyCoinbaseWitnessLength(transaction): isNewMainChainValid = False ''' 9) Reject if sum of transaction sig opcounts > MAX_BLOCK_SIGOPS ''' if not self.blkValidator.verifyMaxBlockSigOps(transaction): isNewMainChainValid = False ''' 10) Verify Merkle hash ''' if not self.blkValidator.verifyMerkleHash(prevBlock): isNewMainChainValid = False ''' Verify Witness hash ''' if not self.blkValidator.verifyWitnessHash(prevBlock): isNewMainChainValid = False ''' 11) Check if prev block (matching prev hash) is in main branch or side branches. If not, add this to orphan blocks, then query peer we got this from for 1st missing orphan block in prev chain; done with block ''' if blockHash != Config.getBytesValue('GENESIS_BLOCK_HASH') and self.getBlockByHash(prevBlock.previousHash) == None: OrphanManager.addBlock(prevBlock) isNewMainChainValid = False ''' For all but the coinbase transaction, apply the following: ''' if not self.blkValidator.verifyNonCoinbaseTransactions(prevBlock): isNewMainChainValid = False ''' Reject if coinbase value > sum of block creation fee and transaction fees ''' if not self.blkValidator.verifyCoinbaseValue(prevBlock): isNewMainChainValid = False ''' (If we have not rejected): ''' if not isNewMainChainValid: break ''' For each transaction, "Add to wallet if mine" ''' prevBlock = self.getBlockByHash(prevBlock.previousHash) ''' If we reject at any point, leave the main branch as what it was originally, done with block ''' if isNewMainChainValid: chainHeadIndexBlock.chainWork = blockChainWork chainHeadIndexBlock.previousHash = blockHash chainHeadIndexBlock.gasLimit = blockGasLimit chainHeadIndexBlock.height = blockHeight self.index.set(self.CHAIN_HEAD_INDEX, chainHeadIndexBlock.serialize()) ''' For each block in the old main branch, from the leaf down to the child of the fork block: ''' oldBlock = chainHeadBlock while oldBlock.hash() != forkBlockHash: ''' For each non-coinbase transaction in the block: ''' for transaction in oldBlock.transactions: if not transaction.isCoinbase(): ''' Apply "tx" checks 2-9 ''' isTxValid = True ''' 2) Make sure neither in or out lists are empty ''' if not self.txValidator.verifyInputOutputNonEmpty(transaction): isTxValid = False ''' 3) Size in bytes <= TRANSACTION_SIZE_LIMIT ''' if not self.txValidator.verifyTransactionSizeLimit(transaction): isTxValid = False ''' 4) Each output value, as well as the total, must be in legal money range ''' if not self.txValidator.verifyAllowedOutputValueRange(transaction): isTxValid = False ''' 5) Make sure none of the inputs have hash=0, n=-1 (coinbase transactions) ''' if not self.txValidator.verifyInputsNonCoinbase(transaction): isTxValid = False ''' 6) size in bytes >= 100[2] ''' if not self.txValidator.verifyTransactionRequiredSize(transaction): isTxValid = False ''' sig opcount <= 2[3] 3) The number of signature operands in the signature (no, that is not redundant) for standard transactions will never exceed two 7) Reject "nonstandard" transactions: scriptSig doing anything other than pushing numbers on the stack, or script not matching the two usual forms[4] ''' if not self.txValidator.verifyAddress(transaction): isTxValid = False if not self.txValidator.verifyExtraData(transaction): isTxValid = False if not self.txValidator.verifyScript(transaction): isTxValid = False if not self.txValidator.verifyWitness(transaction): isTxValid = False ''' 8) Reject if we already have matching tx in the pool, except in step 8, only look in the transaction pool for duplicates, not the main branch ''' if not self.txValidator.verifyTransactionDuplicateInPool(transaction): isTxValid = False ''' 9) For each input, if the referenced output exists in any other tx in the pool, reject this transaction ''' if not self.txValidator.verifyTxOutputDuplicateInPool(transaction): isTxValid = False ''' Add to transaction pool if accepted, else go on to next transaction ''' if isTxValid: MemoryPool.addSignedTransaction(transaction) outputIndex = 0 for txOut in transaction.outputs: UXTO.removeUnspentTransactionCoin(Outpoint(transaction.hash(), outputIndex)) outputIndex += 1 oldBlock = self.getBlockByHash(oldBlock.previousHash) ''' For each block in the new main branch, from the child of the fork node to the leaf: ''' newMainBranchBlocks = [] prevBlock = block while prevBlock.hash() != forkBlockHash: newMainBranchBlocks.insert(0, prevBlock) prevBlock = self.getBlockByHash(prevBlock.previousHash) for newMainBranchBlock in newMainBranchBlocks: newMainBranchBlockHash = newMainBranchBlock.hash() newMainBranchIndexBlock = None if newMainBranchBlockHash == blockHash: newMainBranchIndexBlock = chainHeadIndexBlock else: newMainBranchIndexBlock = self.getIndexBlockByHash(newMainBranchBlockHash) for transaction in newMainBranchBlock.transactions: for txIn in transaction.inputs: UXTO.removeUnspentTransactionCoin(txIn.outpoint) uxtoOutputs = [] uxtoOutputs.extend(transaction.outputs) uxtoOutputs.extend(transaction.internalOutputs) txOutputSize = 0 for txOut in uxtoOutputs: if txOut.store: txOutputSize += 1 outputIndex = 0 for txOut in uxtoOutputs: UXTO.removeStaleUnspentTransactionScript(txOut) if txOut.store: coin = Coin() coin.output = txOut coin.txOutputSize = txOutputSize coin.height = newMainBranchIndexBlock.height coin.coinbase = transaction.isCoinbase() UXTO.addUnspentTransactionCoin(Outpoint(transaction.hash(), outputIndex), coin) outputIndex += 1 ''' For each transaction, "Add to wallet if mine" ''' ''' For each transaction in the block, delete any matching transaction from the transaction pool ''' MemoryPool.removeTransaction(transaction) ''' (If we have not rejected): ''' self.storage.set(blockHashBytes, block.serialize()) blockHeightKey = "{0}{1}{2}".format(self.BLOCK_HEIGHT_KEY, '_', blockHeight) blockHeightKey = DataType.serialize(blockHeightKey) self.index.set(blockHeightKey, blockHashBytes) indexBlock = IndexBlock() indexBlock.chainWork = Bits.getChainworkFromBits(previousChainWork, bits) indexBlock.previousHash = previousHash indexBlock.gasLimit = blockGasLimit indexBlock.height = blockHeight self.index.set(blockHashBytes, indexBlock.serialize()) ''' Relay block to our peers ''' Sync.inv(InventoryType.BLOCK, blockHash) ''' For each orphan block for which this block is its prev, run all these steps (including this one) recursively on that orphan ''' self.syncOrphanBlocks() return True finally: self.releaseBlockLock() ''' For each orphan block for which this block is its prev, run all these steps (including this one) recursively on that orphan ''' self.syncOrphanBlocks() ''' If we rejected, the block is not counted as part of the main branch ''' return False
def addCoinbaseInput(self, coinbaseData): txIn = Input(Config.getBytesValue('COINBASE_TRANSACTION_ID', False), Config.getIntValue('COINBASE_OUTPUT_INDEX')) txIn.witness = coinbaseData self.inputs.append(txIn)
def validate(self, block): ''' Check syntactic correctness ''' if block.version == None or block.version != Config.getIntValue("BLOCK_VERSION"): return False if block.previousHash == None or len(block.previousHash) != Config.getIntValue('HASH_LEN'): return False if block.merkleRoot == None or len(block.merkleRoot) != Config.getIntValue('HASH_LEN'): return False if block.witnessRoot == None or len(block.witnessRoot) != Config.getIntValue('HASH_LEN'): return False if block.gasLimit == None or block.gasLimit < 0: return False if block.gasUsed == None or block.gasUsed < 0: return False if block.nonce == None or block.nonce < 0: return False if block.bits == None or block.bits < 0: return False if block.timestamp == None or block.timestamp < 0: return False blkHash = block.hash() if block.previousHash == Config.getBytesValue('GENESIS_BLOCK_PREVIOUS_HASH', False): if blkHash != Config.getBytesValue('GENESIS_BLOCK_HASH'): print('Check Genesis Block Hash:', blkHash.hex()) return False ''' Reject if duplicate of block we have in any of the three categories ''' ''' 1) blocks in the main branch the transactions in these blocks are considered at least tentatively confirmed 2) blocks on side branches off the main branch these blocks have at least tentatively lost the race to be in the main branch ''' if Chain.getChain().getBlockByHash(blkHash) != None: return False ''' 3) orphan blocks these are blocks which don't link into the main branch, normally because of a missing predecessor or nth-level predecessor ''' if OrphanManager.hasBlock(blkHash): return False if not self.verifyTransactionsNonEmpty(block): return False ''' Block hash must satisfy claimed nBits proof of work ''' if not self.validateBlockBits(block.serializeHeader(), block.bits): return False if not self.verifyFutureTimestamp(block): return False if not self.verifyInitialCoinbaseTransaction(block): return False ''' Check block gas limit ''' totalTxGasLimit = 0 for transaction in block.transactions: totalTxGasLimit += transaction.gasLimit if totalTxGasLimit > block.gasLimit: return False if block.gasUsed > block.gasLimit: return False previousBlockGasLimit = None if blkHash == Config.getBytesValue('GENESIS_BLOCK_HASH'): previousBlockGasLimit = Config.getIntValue('GENESIS_BLOCK_GAS_LIMIT') else: previousIndexBlock = Chain.getChain().getIndexBlockByHash(block.previousHash) if previousIndexBlock != None: previousBlockGasLimit = previousIndexBlock.gasLimit ''' If previousBlockGasLimit is none, block is probably an orphan. ''' if previousBlockGasLimit != None: maxBlockGasLimit = previousBlockGasLimit + (previousBlockGasLimit * (1 / 1024)) maxBlockGasLimit = math.ceil(maxBlockGasLimit) # Check block gas limit increase a max of 1 / 1024 if block.gasLimit > maxBlockGasLimit: return False ''' Check block size in bytes ''' if not self.verifyBlockSizeLimit(block): return False ''' For each transaction, apply "tx" checks 2-4 2) Make sure neither in or out lists are empty 3) Size in bytes <= TRANSACTION_SIZE_LIMIT 4) Each output value, as well as the total, must be in legal money range ''' for transaction in block.transactions: if not self.txValidator.verifyInputOutputNonEmpty(transaction): return False if not self.txValidator.verifyTransactionSizeLimit(transaction): return False if not self.txValidator.verifyAllowedOutputValueRange(transaction): return False if not self.verifyCoinbaseWitnessLength(transaction): return False if not self.verifyMaxBlockSigOps(transaction): return False if not self.verifyMerkleHash(block): return False if not self.verifyWitnessHash(block): return False ''' Check if prev block (matching prev hash) is in main branch or side branches. If not, add this to orphan blocks, then query peer we got this from for 1st missing orphan block in prev chain; done with block ''' if blkHash != Config.getBytesValue('GENESIS_BLOCK_HASH') and Chain.getChain().getBlockByHash(block.previousHash) == None: OrphanManager.addBlock(block) ''' Check that nBits value matches the difficulty rules ''' chainHeadBlock = None chainHeadBlockHeight = None if blkHash == Config.getBytesValue('GENESIS_BLOCK_HASH'): chainHeadBlock = block chainHeadBlockHeight = 0 else: chainHeadBlock = Chain.getChain().getChainHeadBlock() chainHeadIndexBlock = Chain.getChain().getChainHeadIndexBlock() chainHeadBlockHeight = chainHeadIndexBlock.height newBlockBits = Bits.getNewBlockBits(chainHeadBlock, chainHeadBlockHeight) if block.bits != newBlockBits: return False ''' Reject if timestamp is the median time of the last 11 blocks or before ''' timestamps = [] prevBlock = Chain.getChain().getBlockByHash(block.previousHash) while prevBlock != None and len(timestamps) <= 11: timestamps.append(prevBlock.timestamp) prevBlock = Chain.getChain().getBlockByHash(prevBlock.previousHash) if len(timestamps) == 11: timestamps.sort() if block.timestamp <= timestamps[5]: return False ''' For certain old blocks (i.e. on initial block download) check that hash matches known values ''' return True