def Persist(self, block): start = time.clock() sn = self._db.snapshot() accounts = DBCollection(self._db, sn, DBPrefix.ST_Account, AccountState) unspentcoins = DBCollection(self._db, sn, DBPrefix.ST_Coin, UnspentCoinState) spentcoins = DBCollection(self._db, sn, DBPrefix.ST_SpentCoin, SpentCoinState) assets = DBCollection(self._db, sn, DBPrefix.ST_Asset, AssetState) validators = DBCollection(self._db, sn, DBPrefix.ST_Validator, ValidatorState) contracts = DBCollection(self._db, sn, DBPrefix.ST_Contract, ContractState) storages = DBCollection(self._db, sn, DBPrefix.ST_Storage, StorageItem) amount_sysfee = (self.GetSysFeeAmount(block.PrevHash).value + block.TotalFees().value).to_bytes(8, 'little') try: with self._db.write_batch() as wb: wb.put(DBPrefix.DATA_Block + block.Hash.ToBytes(), amount_sysfee + block.Trim()) for tx in block.Transactions: wb.put(DBPrefix.DATA_Transaction + tx.Hash.ToBytes(), block.IndexBytes() + tx.ToArray()) #go through all outputs and add unspent coins to them unspentcoinstate = UnspentCoinState.FromTXOutputsConfirmed(tx.outputs) unspentcoins.Add(tx.Hash.ToBytes(), unspentcoinstate) #go through all the accounts in the tx outputs for output in tx.outputs: account = accounts.GetAndChange(output.AddressBytes, AccountState(output.ScriptHash)) if account.HasBalance(output.AssetId): account.AddToBalance(output.AssetId, output.Value) else: account.SetBalanceFor(output.AssetId, output.Value) #go through all tx inputs unique_tx_input_hashes = [] for input in tx.inputs: if not input.PrevHash in unique_tx_input_hashes: unique_tx_input_hashes.append(input.PrevHash) for txhash in unique_tx_input_hashes: prevTx, height = self.GetTransaction(txhash.ToBytes()) coin_refs_by_hash = [coinref for coinref in tx.inputs if coinref.PrevHash.ToBytes() == txhash.ToBytes()] for input in coin_refs_by_hash: uns = unspentcoins.GetAndChange(input.PrevHash.ToBytes()) uns.OrEqValueForItemAt(input.PrevIndex, CoinState.Spent) if prevTx.outputs[input.PrevIndex].AssetId.ToBytes() == Blockchain.SystemShare().Hash.ToBytes(): sc = spentcoins.GetAndChange(input.PrevHash.ToBytes(), SpentCoinState(input.PrevHash, height, [] )) sc.Items.append( SpentCoinItem( input.PrevIndex, block.Index)) output = prevTx.outputs[input.PrevIndex] acct = accounts.GetAndChange(prevTx.outputs[input.PrevIndex].AddressBytes, AccountState(output.ScriptHash)) assetid = prevTx.outputs[input.PrevIndex].AssetId acct.SubtractFromBalance(assetid, prevTx.outputs[input.PrevIndex].Value) #do a whole lotta stuff with tx here... if tx.Type == TransactionType.RegisterTransaction: asset = AssetState(tx.Hash,tx.AssetType, tx.Name, tx.Amount, Fixed8(0),tx.Precision, Fixed8(0), Fixed8(0), UInt160(data=bytearray(20)), tx.Owner, tx.Admin, tx.Admin, block.Index + 2 * 2000000, False ) assets.Add(tx.Hash.ToBytes(), asset) elif tx.Type == TransactionType.IssueTransaction: txresults = [result for result in tx.GetTransactionResults() if result.Amount.value < 0] for result in txresults: asset = assets.GetAndChange(result.AssetId.ToBytes()) asset.Available = asset.Available - result.Amount elif tx.Type == TransactionType.ClaimTransaction: for input in tx.Claims: sc = spentcoins.TryGet(input.PrevHash.ToBytes()) if sc and sc.HasIndex(input.PrevIndex): sc.DeleteIndex(input.PrevIndex) spentcoins.GetAndChange(input.PrevHash.ToBytes()) elif tx.Type == TransactionType.EnrollmentTransaction: newvalidator = ValidatorState(pub_key=tx.PublicKey) validators.GetAndChange(tx.PublicKey.ToBytes(), newvalidator) elif tx.Type == TransactionType.PublishTransaction: contract = ContractState(tx.Code, tx.NeedStorage, tx.Name, tx.CodeVersion, tx.Author, tx.Email, tx.Description) contracts.GetAndChange(tx.Code.ScriptHash().ToBytes(), contract) elif tx.Type == TransactionType.InvocationTransaction: self.__log.debug("RUNNING INVOCATION TRASACTION!!!!!! %s %s " % (block.Index, tx.Hash.ToBytes())) script_table = CachedScriptTable(contracts) service = StateMachine(accounts, validators, assets, contracts,storages,wb) engine = ApplicationEngine( trigger_type=TriggerType.Application, container=tx, table=script_table, service=service, gas=tx.Gas, testMode=True ) engine.LoadScript(tx.Script,False) try: # drum roll? success = engine.Execute() if success: service.Commit() except Exception as e: self.__log.debug("could not execute %s " % e) else: if tx.Type != b'\x00' and tx.Type != 128: self.__log.debug("TX Not Found %s " % tx.Type) # do save all the accounts, unspent, coins, validators, assets, etc # now sawe the current sys block #filter out accounts to delete then commit for key,account in accounts.Current.items(): if not account.IsFrozen and len(account.Votes) == 0 and account.AllBalancesZeroOrLess(): accounts.Remove(key) accounts.Commit(wb,False) #filte out unspent coins to delete then commit for key, unspent in unspentcoins.Current.items(): unspentcoins.Remove(key) unspentcoins.Commit(wb) #filter out spent coins to delete then commit to db for key, spent in spentcoins.Current.items(): if len( spent.Items) == 0: spentcoins.Remove(key) spentcoins.Commit(wb) #commit validators validators.Commit(wb) #commit assets assets.Commit(wb) #commit contracts contracts.Commit(wb) #commit storages ( not implemented ) storages.Commit(wb) sn.close() wb.put(DBPrefix.SYS_CurrentBlock, block.Hash.ToBytes() + block.IndexBytes()) self._current_block_height = block.Index end = time.clock() self.__log.debug("PERSISTING BLOCK %s (cache) %s %s " % (block.Index, len(self._block_cache), end-start)) except Exception as e: print("couldnt persist block %s " % e)
def Persist(self, block): sn = self._db.snapshot() accounts = DBCollection(self._db, sn, DBPrefix.ST_Account, AccountState) unspentcoins = DBCollection(self._db, sn, DBPrefix.ST_Coin, UnspentCoinState) spentcoins = DBCollection(self._db, sn, DBPrefix.ST_SpentCoin, SpentCoinState) assets = DBCollection(self._db, sn, DBPrefix.ST_Asset, AssetState) validators = DBCollection(self._db, sn, DBPrefix.ST_Validator, ValidatorState) contracts = DBCollection(self._db, sn, DBPrefix.ST_Contract, ContractState) storages = DBCollection(self._db, sn, DBPrefix.ST_Storage, StorageItem) amount_sysfee = (self.GetSysFeeAmount(block.PrevHash).value + block.TotalFees().value).to_bytes(8, 'little') with self._db.write_batch() as wb: for tx in block.Transactions: unspentcoinstate = UnspentCoinState.FromTXOutputsConfirmed( tx.outputs) unspentcoins.Add(tx.Hash.ToBytes(), unspentcoinstate) # go through all the accounts in the tx outputs for output in tx.outputs: account = accounts.GetAndChange( output.AddressBytes, AccountState(output.ScriptHash)) if account.HasBalance(output.AssetId): account.AddToBalance(output.AssetId, output.Value) else: account.SetBalanceFor(output.AssetId, output.Value) # go through all tx inputs unique_tx_input_hashes = [] for input in tx.inputs: if not input.PrevHash in unique_tx_input_hashes: unique_tx_input_hashes.append(input.PrevHash) for txhash in unique_tx_input_hashes: prevTx, height = self.GetTransaction(txhash.ToBytes()) coin_refs_by_hash = [ coinref for coinref in tx.inputs if coinref.PrevHash.ToBytes() == txhash.ToBytes() ] for input in coin_refs_by_hash: uns = unspentcoins.GetAndChange( input.PrevHash.ToBytes()) uns.OrEqValueForItemAt(input.PrevIndex, CoinState.Spent) if prevTx.outputs[input.PrevIndex].AssetId.ToBytes( ) == Blockchain.SystemShare().Hash.ToBytes(): sc = spentcoins.GetAndChange( input.PrevHash.ToBytes(), SpentCoinState(input.PrevHash, height, [])) sc.Items.append( SpentCoinItem(input.PrevIndex, block.Index)) output = prevTx.outputs[input.PrevIndex] acct = accounts.GetAndChange( prevTx.outputs[input.PrevIndex].AddressBytes, AccountState(output.ScriptHash)) assetid = prevTx.outputs[input.PrevIndex].AssetId acct.SubtractFromBalance( assetid, prevTx.outputs[input.PrevIndex].Value) # do a whole lotta stuff with tx here... if tx.Type == TransactionType.RegisterTransaction: asset = AssetState(tx.Hash, tx.AssetType, tx.Name, tx.Amount, Fixed8(0), tx.Precision, Fixed8(0), Fixed8(0), UInt160(data=bytearray(20)), tx.Owner, tx.Admin, tx.Admin, block.Index + 2 * 2000000, False) assets.Add(tx.Hash.ToBytes(), asset) elif tx.Type == TransactionType.IssueTransaction: txresults = [ result for result in tx.GetTransactionResults() if result.Amount.value < 0 ] for result in txresults: asset = assets.GetAndChange(result.AssetId.ToBytes()) asset.Available = asset.Available - result.Amount elif tx.Type == TransactionType.ClaimTransaction: for input in tx.Claims: sc = spentcoins.TryGet(input.PrevHash.ToBytes()) if sc and sc.HasIndex(input.PrevIndex): sc.DeleteIndex(input.PrevIndex) spentcoins.GetAndChange(input.PrevHash.ToBytes()) elif tx.Type == TransactionType.EnrollmentTransaction: validator = validators.GetAndChange( tx.PublicKey, ValidatorState(pub_key=tx.PublicKey)) # print("VALIDATOR %s " % validator.ToJson()) elif tx.Type == TransactionType.PublishTransaction: contract = ContractState(tx.Code, tx.NeedStorage, tx.Name, tx.CodeVersion, tx.Author, tx.Email, tx.Description) contracts.GetAndChange(tx.Code.ScriptHash().ToBytes(), contract) elif tx.Type == TransactionType.InvocationTransaction: script_table = CachedScriptTable(contracts) service = StateMachine(accounts, validators, assets, contracts, storages, wb=wb) engine = ApplicationEngine( trigger_type=TriggerType.Application, container=tx, table=script_table, service=service, gas=tx.Gas, testMode=True) engine.LoadScript(tx.Script, False) # normally, this function does not return true/false # for testing purposes, we try to execute and if an exception is raised # we will return false, otherwise if success return true # this is different than the 'success' bool returned by engine.Execute() # the 'success' bool returned by engine.Execute() is a value indicating # wether or not the invocation was successful, and if so, we then commit # the changes made by the contract to the database try: success = engine.Execute() if success: service.Commit() return True except Exception as e: print("could not execute %s " % e) return False