}; //TODO public static bool Execute(out Types.Transaction transaction, ContractArgs contractArgs, bool isWitness) { try { var fileName = HttpServerUtility.UrlTokenEncode(contractArgs.ContractHash); var contractCode = File.ReadAllText(Path.Combine(_OutputPath, Path.ChangeExtension(fileName, ".fs"))); var func = ContractExamples.Execution.compileQuotedContract(contractCode); var result = func.Invoke(new Tuple <byte[], byte[], FSharpFunc <Types.Outpoint, FSharpOption <Types.Output> > >( contractArgs.Message, contractArgs.ContractHash, FSharpFunc <Types.Outpoint, FSharpOption <Types.Output> > .FromConverter(t => contractArgs.tryFindUTXOFunc(t)))); var txSkeleton = result as Tuple <FSharpList <Types.Outpoint>, FSharpList <Types.Output>, byte[]>; transaction = txSkeleton == null || txSkeleton.Item2.Count() == 0 ? null : new Types.Transaction( Tests.tx.version, txSkeleton.Item1, ListModule.OfSeq <byte[]>(isWitness ? new byte[][] { contractArgs.Message } : new byte[][] { }), txSkeleton.Item2, FSharpOption <Types.ExtendedContract> .None //TODO: get from txSkeleton.Item3 ); return(true); } catch (Exception e) { BlockChainTrace.Error("Error executing contract", e); } transaction = null; return(false); }
public static IsContractGeneratedTxResult IsContractGeneratedTx(TransactionValidation.PointedTransaction ptx, out byte[] contractHash) { contractHash = null; foreach (var input in ptx.pInputs) { if ([email protected]) { if (contractHash == null) { contractHash = ((Types.OutputLock.ContractLock)input.Item2.@lock).contractHash; } else if (!contractHash.SequenceEqual(((Types.OutputLock.ContractLock)input.Item2.@lock).contractHash)) { return(IsContractGeneratedTxResult.Invalid); } else if (!contractHash.SequenceEqual(((Types.OutputLock.ContractLock)input.Item2.@lock).contractHash)) { BlockChainTrace.Information("Unexpected contactHash", contractHash); return(IsContractGeneratedTxResult.Invalid); } } } return(contractHash == null ? IsContractGeneratedTxResult.NotContractGenerated : IsContractGeneratedTxResult.ContractGenerated); }
public void ShouldEvictWithDependencies() { var key = Key.Create(); var tx = Utils .GetTx().AddOutput(Key.Create().Address, Consensus.Tests.zhash, 100) .Sign(key.Private).Tag("tx"); var tx1 = Utils.GetTx().AddInput(tx, 0).AddOutput(Key.Create().Address, Consensus.Tests.zhash, 1).Sign(key.Private).Tag("tx1"); var tx2 = Utils.GetTx().AddInput(tx, 0).AddOutput(Key.Create().Address, Consensus.Tests.zhash, 2).Sign(key.Private).Tag("tx2"); var contractHash = new byte[] { }; BlockChainTrace.SetTag(contractHash, "mock contract"); _BlockChain.memPool.ContractPool.AddRef(tx1.Key(), new ACSItem() { Hash = contractHash }); HandleBlock(_GenesisBlock.AddTx(tx).Tag("genesis")); Assert.That(TxState(tx), Is.EqualTo(TxStateEnum.Confirmed)); HandleBlock(_GenesisBlock.Child().AddTx(tx1).Tag("main")); Assert.That(TxState(tx1), Is.EqualTo(TxStateEnum.Confirmed)); var branch = _GenesisBlock.Child().Tag("branch"); HandleBlock(branch.Child().Tag("branch orphan")); HandleBlock(branch.AddTx(tx2).Tag("branch child")); System.Threading.Thread.Sleep(1000); Assert.That(TxState(tx2), Is.EqualTo(TxStateEnum.Confirmed)); Assert.That(TxState(tx1), Is.EqualTo(TxStateEnum.Invalid)); Assert.That(_BlockChain.memPool.TxPool.ContainsKey(tx1.Key()), Is.False); }
// remove bool IsTransactionValid(Types.Transaction tx, byte[] txHash, out TransactionValidation.PointedTransaction ptx) { if (_BlockChain.BlockStore.TxStore.ContainsKey(_DbTx, txHash) && _BlockChain.BlockStore.TxStore.Get(_DbTx, txHash).Value.InMainChain) { BlockChainTrace.Information("Tx already in store", txHash); ptx = null; return(false); } switch (_BlockChain.IsOrphanTx(_DbTx, tx, true, out ptx)) { case BlockChain.IsTxOrphanResult.Orphan: BlockChainTrace.Information("tx invalid - orphan", tx); return(false); case BlockChain.IsTxOrphanResult.Invalid: BlockChainTrace.Information("tx invalid - reference(s)", tx); return(false); } if (_BlockChain.IsDoubleSpend(_DbTx, tx, true)) { return(false); } //TODO: coinbase validation + check that witness has blocknumber if (!BlockChain.IsValidUserGeneratedTx(_DbTx, ptx)) { BlockChainTrace.Information("tx invalid - structural", ptx); return(false); } byte[] contractHash; switch (BlockChain.IsContractGeneratedTx(ptx, out contractHash)) { case BlockChain.IsContractGeneratedTxResult.ContractGenerated: if (!_BlockChain.ActiveContractSet.IsActive(_DbTx, contractHash)) { BlockChainTrace.Information("tx invalid - contract not active", tx); return(false); } var contractFunction = _BlockChain.ActiveContractSet.GetContractFunction(_DbTx, contractHash); if (!BlockChain.IsValidAutoTx(ptx, UtxoLookup, contractHash, contractFunction)) { BlockChainTrace.Information("auto-tx invalid", ptx); return(false); } break; case BlockChain.IsContractGeneratedTxResult.Invalid: BlockChainTrace.Information("tx invalid - input locks", tx); return(false); } return(true); }
void UndoBlock(Types.Block block, byte[] key) { BlockChainTrace.Information($"undoing block {block.header.blockNumber}", block); _BlockChain.BlockStore.SetLocation(_DbTx, key, LocationEnum.Branch); var blockUndoData = _BlockChain.BlockStore.GetUndoData(_DbTx, key); if (blockUndoData != null) { blockUndoData.AddedUTXO.ForEach(u => { BlockChainTrace.Information($"undo block {block.header.blockNumber}: utxo removed, amount {u.Item2.spend.amount}", block); _BlockChain.UTXOStore.Remove(_DbTx, u.Item1.txHash, u.Item1.index); }); blockUndoData.RemovedUTXO.ForEach(u => { BlockChainTrace.Information($"undo block {block.header.blockNumber}: new utxo, amount {u.Item2.spend.amount}", block); _BlockChain.UTXOStore.Put(_DbTx, u.Item1.txHash, u.Item1.index, u.Item2); }); foreach (var item in blockUndoData.ACSDeltas) { if (item.Value.LastBlock.HasValue) // restore last block - undo extend { var current = new ActiveContractSet().Get(_DbTx, item.Key); if (current != null) { current.Value.LastBlock = item.Value.LastBlock.Value; _BlockChain.ActiveContractSet.Add(_DbTx, current.Value); } else { BlockChainTrace.Error("missing ACS item!", new Exception()); } } else if (item.Value.ACSItem != null) // restore entire item - undo expire { _BlockChain.ActiveContractSet.Add(_DbTx, item.Value.ACSItem); } else // remove item - undo activate { _BlockChain.ActiveContractSet.Remove(_DbTx, item.Key); } } } block.transactions.ToList().ForEach(tx => { var txHash = Merkle.transactionHasher.Invoke(tx); UnconfirmedTxs[txHash] = tx; }); }
public ContractFunction GetContractFunction(TransactionContext dbTx, byte[] contractHash) { var acsItem = Get(dbTx, contractHash); if (acsItem == null) { return(null); } var deserialization = FSharpOption <Tuple <ContractFunction, ContractCostFunction> > .None; try { deserialization = ContractExamples.FStarExecution.deserialize(acsItem.Value.Serialized); } catch { BlockChainTrace.Information("Error deserializing contract"); } if (FSharpOption <Tuple <ContractFunction, ContractCostFunction> > .get_IsNone(deserialization) || deserialization == null) { BlockChainTrace.Information("Reserializing contract"); try { var compilation = ContractExamples.FStarExecution.compile(acsItem.Value.Extracted); if (FSharpOption <byte[]> .get_IsNone(compilation)) { return(null); } acsItem.Value.Serialized = compilation.Value; Add(dbTx, acsItem.Value); deserialization = ContractExamples.FStarExecution.deserialize(compilation.Value); if (FSharpOption <Tuple <ContractFunction, ContractCostFunction> > .get_IsNone(deserialization)) { BlockChainTrace.Error("Error deserializing contract"); return(null); } } catch (Exception e) { BlockChainTrace.Error("Error recompiling contract", e); return(null); } } return(deserialization.Value.Item1); }
// Note: when contract uses blokchain state as context, need to pass it in. public static bool ExecuteContract( byte[] contractHash, ContractFunction contractFunction, byte[] message, out Types.Transaction transaction, UtxoLookup UtxoLookup, bool isWitness) { var contractFunctionInput = new ContractFunctionInput( message, contractHash, UtxoLookup ); TransactionSkeleton transactionSkeleton = null; transaction = null; try { var serializer = new ContractExamples.FStarCompatibility.DataSerializer(Consensus.Serialization.context); Consensus.Serialization.context.Serializers.RegisterOverride(serializer); var result = contractFunction.Invoke(contractFunctionInput); if (result.IsError) { BlockChainTrace.Information("Contract resulted in error: " + result.ErrorValue); return(false); } transactionSkeleton = result.ResultValue; } catch (Exception e) { BlockChainTrace.Error("Error executing contract", e); return(false); } if (transactionSkeleton == null) { return(false); } transaction = new Types.Transaction( Tests.tx.version, transactionSkeleton.Item1, ListModule.OfSeq(isWitness ? new byte[][] { message } : new byte[][] { }), transactionSkeleton.Item2, FSharpOption <Types.ExtendedContract> .None //TODO: get from txSkeleton.Item3 ); return(true); }
bool IsValidTimeStamp() { //TODO: assert block's timestamp isn't too far in the future var result = _Bk.header.timestamp >= _BlockChain.Timestamps.Median(); if (!result) { BlockChainTrace.Information($"block {_Bk.header.blockNumber} has invalid timestamp", _BkHash); } return(result); }
bool IsValidBlockNumber() { var parentBlock = GetBlock(_Bk.header.parent).Value; #if TRACE if (parentBlock.header.blockNumber >= _Bk.header.blockNumber) { BlockChainTrace.Information($"block {_Bk.header.blockNumber}: expecting block-number greater than {parentBlock.header.blockNumber}, found {_Bk.header.blockNumber}", _BkHash); } #endif return(parentBlock.header.blockNumber < _Bk.header.blockNumber); }
public static bool Compile_old(String fsharpCode, out byte[] contractHash) { var tempSourceFile = Path.ChangeExtension(Path.GetTempFileName(), ".fs"); var process = new Process(); contractHash = GetHash(fsharpCode); File.WriteAllText(tempSourceFile, fsharpCode); if (!Directory.Exists(_OutputPath)) { Directory.CreateDirectory(_OutputPath); } if (IsRunningOnMono()) { process.StartInfo.FileName = "fsharpc"; process.StartInfo.Arguments = $"-o { GetFileName(contractHash) } -a {tempSourceFile}{DEPENCENCY_OPTION + string.Join(DEPENCENCY_OPTION, _Dependencies)}"; } else { //TODO } process.OutputDataReceived += (sender, args1) => { Console.WriteLine("## " + args1.Data); }; process.StartInfo.UseShellExecute = false; process.StartInfo.RedirectStandardOutput = true; process.OutputDataReceived += (sender, args1) => { BlockChainTrace.Information(args1.Data); }; try { process.Start(); process.BeginOutputReadLine(); process.WaitForExit(); } catch (Exception e) { BlockChainTrace.Error("process", e); } File.Delete(tempSourceFile); return(process.ExitCode == 0); }
public static bool IsValidAutoTx(TransactionValidation.PointedTransaction ptx, UtxoLookup UtxoLookup, byte[] contractHash, ContractFunction contractFunction) { var isWitness = false; var witnessIdx = -1; byte[] message = null; for (var i = 0; i < ptx.witnesses.Length; i++) { if (ptx.witnesses[i].Length > 0) { witnessIdx = i; } } if (witnessIdx == 0) { message = ptx.witnesses[0]; } else if (witnessIdx == -1) { var contractLock = ptx.pInputs[0].Item2.@lock as Types.OutputLock.ContractLock; if (contractLock == null) { BlockChainTrace.Information("expected ContractLock, tx invalid"); return(false); } message = contractLock.data; } isWitness = witnessIdx == 0; Types.Transaction tx; var isExecutionSuccessful = ExecuteContract( contractHash, contractFunction, message, out tx, UtxoLookup, isWitness ); return(isExecutionSuccessful && tx != null && TransactionValidation.unpoint(ptx).Equals(tx)); }
public bool TryActivate(TransactionContext dbTx, string contractCode, ulong kalapas, byte[] contractHash, uint blockNumber) { if (IsActive(dbTx, contractHash)) { return(false); } //TODO: module name var extration = ContractExamples.FStarExecution.extract(contractCode); if (FSharpOption <string> .get_IsNone(extration)) { BlockChainTrace.Information("Could not extract contract"); return(false); } var compilation = ContractExamples.FStarExecution.compile(extration.Value); if (FSharpOption <byte[]> .get_IsNone(compilation)) { BlockChainTrace.Information("Could not complie contract"); return(false); } var kalapasPerBlock = KalapasPerBlock(contractCode); if (kalapas < kalapasPerBlock) { return(false); } var blocks = Convert.ToUInt32(kalapas / kalapasPerBlock); Add(dbTx, new ACSItem() { Hash = contractHash, KalapasPerBlock = kalapasPerBlock, LastBlock = blockNumber + blocks, Extracted = extration.Value, Serialized = compilation.Value }); BlockChainTrace.Information($"Contract activated for {blocks} blocks", contractHash); return(true); }
void Reorg() { var originalTip = _BlockChain.Tip; Keyed <Types.Block> fork = null; var newMainChain = GetNewMainChainStartFromForkToLeaf(new Keyed <Types.Block>(_BkHash, _Bk), out fork); _BlockChain.ChainTip.Context(_DbTx).Value = fork.Key; _BlockChain.Tip = fork; _BlockChain.InitBlockTimestamps(_DbTx); var oldMainChain = GetOldMainChainStartFromLeafToFork(fork, originalTip.Key); foreach (var block in oldMainChain) { UndoBlock(block.Value, block.Key); } //append new chain foreach (var _bk in newMainChain) { var action = new BlockVerificationHelper( _BlockChain, _DbTx, _bk.Key, _bk.Value, false, true, ConfirmedTxs, UnconfirmedTxs, QueueActions); BlockChainTrace.Information($"new main chain bk {_bk.Value.header.blockNumber} {action.Result.BkResultEnum}", _bk.Value); if (action.Result.BkResultEnum == BkResultEnum.Rejected) { _BlockChain.ChainTip.Context(_DbTx).Value = originalTip.Key; _BlockChain.Tip = originalTip; _BlockChain.InitBlockTimestamps(_DbTx); BlockChainTrace.Information("reorganization undo", _bk.Value); Result = new BkResult(BkResultEnum.Rejected); return; } } }
public BlockChain(string dbName, byte[] genesisBlockHash) { memPool = new MemPool(); UTXOStore = new UTXOStore(); ActiveContractSet = new ActiveContractSet(); BlockStore = new BlockStore(); BlockNumberDifficulties = new BlockNumberDifficulties(); ChainTip = new ChainTip(); Timestamps = new BlockTimestamps(); GenesisBlockHash = genesisBlockHash; _DBContext = new DBContext(dbName); OwnResource(_DBContext); //var listener = new EventLoopMessageListener<QueueAction>(HandleQueueAction, "BlockChain listener"); //OwnResource(MessageProducer<QueueAction>.Instance.AddMessageListener(listener)); var buffer = new BufferBlock <QueueAction>(); QueueAction.Target = buffer; using (var dbTx = _DBContext.GetTransactionContext()) { var chainTip = ChainTip.Context(dbTx).Value; //TODO: check if makred as main? Tip = chainTip == null ? null : BlockStore.GetBlock(dbTx, chainTip); if (Tip != null) { BlockChainTrace.Information("Tip's block number is " + Tip.Value.header.blockNumber); //Try to validate orphans of tip, if any. this would be the case when a sync action was interrupted BlockStore.Orphans(dbTx, Tip.Key).ToList().ForEach(t => new HandleBlockAction(t.Key, t.Value, true).Publish()); } else { BlockChainTrace.Information("No tip."); } InitBlockTimestamps(dbTx); } var consumer = ConsumeAsync(buffer); }
void RemoveInvalidAutoTxs(TransactionContext dbTx) { foreach (var item in memPool.TxPool.ToList()) { var ptx = item.Value; byte[] contractHash; if (IsContractGeneratedTx(ptx, out contractHash) == IsContractGeneratedTxResult.ContractGenerated) { var utxoLookup = UtxoLookupFactory(dbTx, false, ptx); var contractFunction = ActiveContractSet.GetContractFunction(dbTx, contractHash); if (!IsValidAutoTx(ptx, utxoLookup, contractHash, contractFunction)) { BlockChainTrace.Information("invalid auto-tx removed from mempool", item.Value); memPool.TxPool.RemoveWithDependencies(item.Key); } } } }
//TODO: should asset that the block came from main? Types.Block GetBlock(byte[] key) { using (TransactionContext context = _DBContext.GetTransactionContext()) { var location = BlockStore.GetLocation(context, key); if (location == LocationEnum.Main) { try { var bk = BlockStore.GetBlock(context, key); return(bk == null ? null : bk.Value); } catch (Exception e) { BlockChainTrace.Error("GetBlock", e); return(null); } } return(null); } }
public static bool Compile(String csharpCode, out byte[] contractHash) { var provider = new CSharpCodeProvider(); var parameters = new CompilerParameters(); foreach (var dependency in _Dependencies) { parameters.ReferencedAssemblies.Add(dependency); } if (!Directory.Exists(_OutputPath)) { Directory.CreateDirectory(_OutputPath); } contractHash = GetHash(csharpCode); parameters.GenerateInMemory = false; parameters.GenerateExecutable = false; parameters.OutputAssembly = GetFileName(contractHash); var results = provider.CompileAssemblyFromSource(parameters, csharpCode); if (results.Errors.HasErrors) { StringBuilder sb = new StringBuilder(); foreach (CompilerError error in results.Errors) { sb.AppendLine(String.Format("Error ({0}): {1}", error.ErrorNumber, error.ErrorText)); } BlockChainTrace.Error(sb.ToString(), new Exception()); //throw new InvalidOperationException(sb.ToString()); return(false); } return(true); }
public void ShouldRemoveConfirmedFromMempoolWithDependencies() { var key = Key.Create(); var tx = Utils .GetTx().AddOutput(Key.Create().Address, Consensus.Tests.zhash, 100) .Sign(key.Private).Tag("tx"); var mempoolTx = Utils.GetTx().AddInput(tx, 0).AddOutput(Key.Create().Address, Consensus.Tests.zhash, 1).Sign(key.Private) .Tag("mempoolTx"); var contractHash = new byte[] { }; BlockChainTrace.SetTag(contractHash, "mock contract"); HandleBlock(_GenesisBlock.AddTx(tx).Tag("genesis")); HandleTransaction(mempoolTx); _BlockChain.memPool.ContractPool.AddRef(mempoolTx.Key(), new ACSItem() { Hash = contractHash }); Assert.That(_BlockChain.memPool.TxPool.Count, Is.EqualTo(1)); Assert.That(_BlockChain.memPool.ContractPool.Count, Is.EqualTo(1)); HandleBlock(_GenesisBlock.Child().AddTx(mempoolTx).Tag("genesis")); Assert.That(_BlockChain.memPool.TxPool.Count, Is.EqualTo(0)); Assert.That(_BlockChain.memPool.ContractPool.Count, Is.EqualTo(0)); }
public static bool Execute_old(out Types.Transaction transaction, ContractArgs contractArgs, bool isWitness) { try { var assembly = Assembly.LoadFrom(GetFileName(contractArgs.ContractHash)); var module = assembly.GetModules()[0]; var type = module.GetTypes()[0]; //************************************************** // used for CSharp based contract debugging var matchedTypes = Assembly.GetEntryAssembly() .GetModules()[0] .GetTypes() .Where(t => t.FullName == type.FullName); if (matchedTypes.Any()) { type = matchedTypes.First(); } //************************************************** // used for FSharp based contract debugging //var matchedTypes = Assembly.LoadFile("TestFSharpContracts.dll") // .GetModules()[0] // .GetTypes() // .Where(t => t.FullName == type.FullName); //if (matchedTypes.Any()) //{ // type = matchedTypes.First(); //} var method = type.GetMethod("main"); var args = new object[] { #if CSHARP_CONTRACTS new List <byte>(contractArgs.Message), contractArgs.ContractHash, contractArgs.tryFindUTXOFunc, #else contractArgs.Message, contractArgs.ContractHash, FSharpFunc <Types.Outpoint, FSharpOption <Types.Output> > .FromConverter(t => contractArgs.tryFindUTXOFunc(t)) #endif }; var result = method.Invoke(null, args); #if CSHARP_CONTRACTS var txSkeleton = result as Tuple <IEnumerable <Types.Outpoint>, IEnumerable <Types.Output>, byte[]>; #else var txSkeleton = result as Tuple <FSharpList <Types.Outpoint>, FSharpList <Types.Output>, byte[]>; #endif transaction = txSkeleton == null || txSkeleton.Item2.Count() == 0 ? null : new Types.Transaction( Tests.tx.version, #if CSHARP_CONTRACTS ListModule.OfSeq(txSkeleton.Item1), #else txSkeleton.Item1, #endif ListModule.OfSeq <byte[]>(isWitness ? new byte[][] { contractArgs.Message } : new byte[][] { }), #if CSHARP_CONTRACTS ListModule.OfSeq(txSkeleton.Item2), #else txSkeleton.Item2, #endif FSharpOption <Types.ExtendedContract> .None //TODO: get from txSkeleton.Item3 ); return(true); } catch (Exception e) { BlockChainTrace.Error("Error executing contract", e); } transaction = null; return(false); }
public BlockVerificationHelper( BlockChain blockChain, TransactionContext dbTx, byte[] bkHash, Types.Block bk, bool handleOrphan = false, bool handleBranch = false, HashDictionary <TransactionValidation.PointedTransaction> confirmedTxs = null, HashDictionary <Types.Transaction> invalidatedTxs = null, List <QueueAction> queuedActions = null ) { ConfirmedTxs = confirmedTxs ?? new HashDictionary <TransactionValidation.PointedTransaction>(); UnconfirmedTxs = invalidatedTxs ?? new HashDictionary <Types.Transaction>(); //todo: refactor to set as new obj by default QueueActions = queuedActions ?? new List <QueueAction>(); _BlockChain = blockChain; _DbTx = dbTx; _BkHash = bkHash; _Bk = bk; if (!IsValid()) { BlockChainTrace.Information($"block {_Bk.header.blockNumber} is invalid", _Bk); Result = new BkResult(BkResultEnum.Rejected); return; } if (IsInStore()) { var reject = false; byte[] missingParent = null; var location = blockChain.BlockStore.GetLocation(dbTx, bkHash); switch (location) { case LocationEnum.Branch: reject = !handleBranch; break; case LocationEnum.Orphans: missingParent = GetMissingParent(); reject = !handleOrphan && !handleBranch; break; default: reject = true; break; } if (reject) { BlockChainTrace.Information($"Block {_Bk.header.blockNumber} already in store ({location})", bk); Result = new BkResult(BkResultEnum.Rejected, missingParent); return; } } if (bk.transactions.Count() == 0) { BlockChainTrace.Information("empty tx list", bk); Result = new BkResult(BkResultEnum.Rejected); return; } if (!IsValidTime()) { BlockChainTrace.Information("invalid time", bk); Result = new BkResult(BkResultEnum.Rejected); return; } //TODO: /* * 3. Transaction list must be non - empty * 4. Block hash must satisfy claimed nBits proof of work * 5. Block timestamp must not be more than two hours in the future * 6. First transaction must be coinbase, the rest must not be * 7. For each transaction, apply "tx" checks 2 - 4 * 8. (omitted) * 9. (omitted) * 10. Verify Merkle hash */ if (IsGenesis()) { if (!IsGenesisValid()) { BlockChainTrace.Information("invalid genesis block", bk); Result = new BkResult(BkResultEnum.Rejected); return; } else { blockChain.Timestamps.Init(bk.header.timestamp); ExtendMain(QueueActions, 0, true); Result = new BkResult(BkResultEnum.Accepted); BlockChainTrace.Information("accepted genesis block", bk); return; } } if (IsOrphan()) { var missingParent = GetMissingParent(); blockChain.BlockStore.Put(dbTx, bkHash, bk, LocationEnum.Orphans, 0); BlockChainTrace.Information($"block {_Bk.header.blockNumber} added as orphan", bk); Result = new BkResult(BkResultEnum.AcceptedOrphan, missingParent); return; } //12. Check that nBits value matches the difficulty rules if (!IsValidDifficulty() || !IsValidBlockNumber() || !IsValidTimeStamp()) { BlockChainTrace.Information($"block {_Bk.header.blockNumber} rejected", bk); Result = new BkResult(BkResultEnum.Rejected); return; } //14. For certain old blocks(i.e.on initial block download) check that hash matches known values var totalWork = TotalWork(); UtxoLookup = _BlockChain.UtxoLookupFactory(_DbTx, true); if (handleBranch) // make a branch block main { if (!ExtendMain(QueueActions, totalWork)) { Result = new BkResult(BkResultEnum.Rejected); return; } } else if (!IsNewGreatestWork(totalWork)) { blockChain.BlockStore.Put(dbTx, bkHash, bk, LocationEnum.Branch, totalWork); } else if (blockChain.BlockStore.IsLocation(dbTx, bk.header.parent, LocationEnum.Main)) { if (!ExtendMain(QueueActions, totalWork)) { BlockChainTrace.Information($"block {_Bk.header.blockNumber} rejected", bk); Result = new BkResult(BkResultEnum.Rejected); return; } } else { BlockChainTrace.Information($"block {bk.header.blockNumber} extends a branch with new difficulty", bk); Reorg(); } BlockChainTrace.Information($"block {bk.header.blockNumber} accepted", bk); Result = new BkResult(BkResultEnum.Accepted); }
public static Types.Block Tag(this Types.Block block, string value) { BlockChainTrace.SetTag(block, value); return(block); }
public static Types.Transaction Tag(this Types.Transaction tx, string value) { BlockChainTrace.SetTag(tx, value); return(tx); }
bool ExtendMain(List <QueueAction> queuedActions, double totalWork, bool isGenesis = false) { if (_BlockChain.BlockStore.ContainsKey(_DbTx, _BkHash)) { _BlockChain.BlockStore.SetLocation(_DbTx, _BkHash, LocationEnum.Main); } else { _BlockChain.BlockStore.Put(_DbTx, _BkHash, _Bk, LocationEnum.Main, totalWork); } _BlockChain.Timestamps.Push(_Bk.header.timestamp); if (_Bk.header.blockNumber % 2000 == 0) { _BlockChain.BlockNumberDifficulties.Add(_DbTx.Transaction, _Bk.header.blockNumber, _BkHash); } var blockUndoData = new BlockUndoData(); var confirmedTxs = new HashDictionary <TransactionValidation.PointedTransaction>(); //TODO: lock with mempool for (var txIdx = 0; txIdx < _Bk.transactions.Count(); txIdx++) { var tx = _Bk.transactions[txIdx]; var txHash = Merkle.transactionHasher.Invoke(tx); TransactionValidation.PointedTransaction ptx; if (!isGenesis) { if ((txIdx == 0 && !IsCoinbaseTxValid(tx)) || (txIdx > 0 && IsCoinbaseTxValid(tx))) { if (txIdx == 0) { BlockChainTrace.Information("Invalid coinbase tx"); } else { BlockChainTrace.Information($"Invalid tx ({txIdx})"); } return(false); } if (!IsTransactionValid(tx, txHash, out ptx)) { return(false); } confirmedTxs[txHash] = ptx; foreach (var pInput in ptx.pInputs) { _BlockChain.UTXOStore.Remove(_DbTx, pInput.Item1); BlockChainTrace.Information($"utxo spent, amount {pInput.Item2.spend.amount}", ptx); //BlockChainTrace.Information($" of", pInput.Item1.txHash); blockUndoData.RemovedUTXO.Add(new Tuple <Types.Outpoint, Types.Output>(pInput.Item1, pInput.Item2)); } } else { ptx = TransactionValidation.toPointedTransaction( tx, ListModule.Empty <Types.Output>() ); } _BlockChain.BlockStore.TxStore.Put(_DbTx, txHash, tx, true); var contractExtendSacrifices = new HashDictionary <ulong>(); var activationSacrifice = 0UL; for (var outputIdx = 0; outputIdx < tx.outputs.Count(); outputIdx++) { var output = tx.outputs[outputIdx]; if ([email protected]) { if (!output.spend.asset.SequenceEqual(Tests.zhash)) { continue; // not Zen } var contractSacrificeLock = (Types.OutputLock.ContractSacrificeLock)output.@lock; if (contractSacrificeLock.IsHighVLock) { continue; // not current version } if (contractSacrificeLock.Item.lockData.Length > 0 && contractSacrificeLock.Item.lockData[0] != null && contractSacrificeLock.Item.lockData[0].Length > 0) { var contractKey = contractSacrificeLock.Item.lockData[0]; // output-lock-level indicated contract contractExtendSacrifices[contractKey] = (contractExtendSacrifices.ContainsKey(contractKey) ? contractExtendSacrifices[contractKey] : 0) + output.spend.amount; } else if (contractSacrificeLock.Item.lockData.Length == 0) { activationSacrifice += output.spend.amount; } } //todo: fix to exclude CSLocks&FLocks, instead of including by locktype if ([email protected] || [email protected]) { BlockChainTrace.Information($"new utxo, amount {output.spend.amount}", tx); var outpoint = new Types.Outpoint(txHash, (uint)outputIdx); _BlockChain.UTXOStore.Put(_DbTx, outpoint, output); blockUndoData.AddedUTXO.Add(new Tuple <Types.Outpoint, Types.Output>(outpoint, output)); } } if (FSharpOption <Types.ExtendedContract> .get_IsSome(tx.contract) && !tx.contract.Value.IsHighVContract) { var codeBytes = ((Types.ExtendedContract.Contract)tx.contract.Value).Item.code; var contractHash = Merkle.innerHash(codeBytes); var contractCode = System.Text.Encoding.ASCII.GetString(codeBytes); if (_BlockChain.ActiveContractSet.TryActivate(_DbTx, contractCode, activationSacrifice, contractHash, _Bk.header.blockNumber)) { blockUndoData.ACSDeltas.Add(contractHash, new ACSUndoData()); ContractsTxsStore.Add(_DbTx.Transaction, contractHash, txHash); } } foreach (var item in contractExtendSacrifices) { var currentACSItem = _BlockChain.ActiveContractSet.Get(_DbTx, item.Key); if (currentACSItem.Value != null) { if (_BlockChain.ActiveContractSet.TryExtend(_DbTx, item.Key, item.Value)) { if (!blockUndoData.ACSDeltas.ContainsKey(item.Key)) { blockUndoData.ACSDeltas.Add(item.Key, new ACSUndoData() { LastBlock = currentACSItem.Value.LastBlock }); } } } } } var expiringContracts = _BlockChain.ActiveContractSet.GetExpiringList(_DbTx, _Bk.header.blockNumber); foreach (var acsItem in expiringContracts) { if (!blockUndoData.ACSDeltas.ContainsKey(acsItem.Key)) { blockUndoData.ACSDeltas.Add(acsItem.Key, new ACSUndoData() { ACSItem = acsItem.Value }); } } if (!isGenesis) { _BlockChain.BlockStore.SetUndoData(_DbTx, _BkHash, blockUndoData); } _BlockChain.ActiveContractSet.DeactivateContracts(_DbTx, expiringContracts.Select(t => t.Key)); ValidateACS(); _BlockChain.ChainTip.Context(_DbTx).Value = _BkHash; //TODO: only update after commit _BlockChain.Tip = new Keyed <Types.Block>(_BkHash, _Bk); queuedActions.Add(new MessageAction(new BlockMessage(confirmedTxs, _Bk.header.blockNumber))); foreach (var item in confirmedTxs) { ConfirmedTxs[item.Key] = item.Value; UnconfirmedTxs.Remove(item.Key); } return(true); }
//void HandleQueueAction(QueueAction action) async Task ConsumeAsync(ISourceBlock <QueueAction> source) { while (await source.OutputAvailableAsync()) { var action = source.Receive(); try { //((dynamic)this).Handle((dynamic)action); if (action is HandleBlockAction) { ((HandleBlockAction)action).SetResult(HandleBlock(action as HandleBlockAction)); } else if (action is GetActiveContractsAction) { ((GetActiveContractsAction)action).SetResult(GetActiveContracts()); } else if (action is GetContractPointedOutputsAction) { ((GetContractPointedOutputsAction)action).SetResult(GetContractPointedOutputs( ((GetContractPointedOutputsAction)action).ContractHash)); } else if (action is HandleOrphansOfTxAction) { HandleOrphansOfTransaction(action as HandleOrphansOfTxAction); } else if (action is GetIsContractActiveAction) { ((GetIsContractActiveAction)action).SetResult(IsContractActive( ((GetIsContractActiveAction)action).ContractHash)); } //else if (action is GetUTXOAction) //((GetUTXOAction)action).SetResult(GetUTXO(((GetUTXOAction)action).Outpoint, ((GetUTXOAction)action).IsInBlock)); else if (action is GetIsConfirmedUTXOExistAction) { var outpoint = ((GetIsConfirmedUTXOExistAction)action).Outpoint; ((GetIsConfirmedUTXOExistAction)action).SetResult(IsConfirmedUTXOExist(outpoint)); } else if (action is GetContractCodeAction) { ((GetContractCodeAction)action).SetResult(GetContractCode( ((GetContractCodeAction)action).ContractHash)); } else if (action is HandleTransactionAction) { ((HandleTransactionAction)action).SetResult(HandleTransaction(((HandleTransactionAction)action).Tx, ((HandleTransactionAction)action).CheckInDb)); } else if (action is GetBlockAction) { ((GetBlockAction)action).SetResult(GetBlock(((GetBlockAction)action).BkHash)); } else if (action is GetTxAction) { ((GetTxAction)action).SetResult(GetTransaction(((GetTxAction)action).TxHash)); } else if (action is ExecuteContractAction) { var executeContractAction = ((ExecuteContractAction)action); Types.Transaction tx; var result = AssembleAutoTx(executeContractAction.ContractHash, executeContractAction.Message, out tx, executeContractAction.Message != null); ((ExecuteContractAction)action).SetResult(new Tuple <bool, Types.Transaction>(result, tx)); } //TODO: remove else if (action is GetUTXOSetAction) { var getUTXOSetAction = (GetUTXOSetAction)action; HashDictionary <List <Types.Output> > txOutputs; HashDictionary <Types.Transaction> txs; GetUTXOSet(getUTXOSetAction.Predicate, out txOutputs, out txs); getUTXOSetAction.SetResult(new Tuple <HashDictionary <List <Types.Output> >, HashDictionary <Types.Transaction> >(txOutputs, txs)); } //TODO: rename else if (action is GetUTXOSetAction2) { using (var dbTx = _DBContext.GetTransactionContext()) { ((GetUTXOSetAction2)action).SetResult(UTXOStore.All(dbTx).ToList()); } } #if TEST else if (action is GetBlockLocationAction) { using (var dbTx = _DBContext.GetTransactionContext()) { ((GetBlockLocationAction)action).SetResult(BlockStore.GetLocation(dbTx, ((GetBlockLocationAction)action).Block)); } } #endif } catch (Exception e) { #if DEBUG BlockChainTrace.Error("Blockchain request handle got an exception.\n\nOriginal caller's stack:\n\n" + action.StackTrace + "\n\nException:\n\n", e); #else BlockChainTrace.Error("Blockchain request exception", e); #endif } } }
/// <summary> /// Handles a new transaction from network or wallet. /// </summary> TxResultEnum HandleTransaction(Types.Transaction tx, bool checkInDb = true) { using (var dbTx = _DBContext.GetTransactionContext()) { TransactionValidation.PointedTransaction ptx; var txHash = Merkle.transactionHasher.Invoke(tx); lock (memPool) { if (memPool.TxPool.Contains(txHash) || memPool.ICTxPool.Contains(txHash) || memPool.OrphanTxPool.Contains(txHash)) { BlockChainTrace.Information("Tx already in mempool", txHash); return(TxResultEnum.Known); } if (checkInDb && BlockStore.TxStore.ContainsKey(dbTx, txHash) && BlockStore.TxStore.Get(dbTx, txHash).Value.InMainChain) { BlockChainTrace.Information("Tx already in store", txHash); return(TxResultEnum.Known); } Action removeDeps = () => { memPool.ICTxPool.GetDependencies(txHash).ToList().ForEach(t => t.Item1.RemoveWithDependencies(t.Item2)); memPool.OrphanTxPool.GetDependencies(txHash).ToList().ForEach(t => t.Item1.RemoveWithDependencies(t.Item2)); }; if (IsDoubleSpend(dbTx, tx, false)) { new TxMessage(txHash, null, TxStateEnum.Invalid).Publish(); removeDeps(); return(TxResultEnum.DoubleSpend); } switch (IsOrphanTx(dbTx, tx, false, out ptx)) { case IsTxOrphanResult.Orphan: BlockChainTrace.Information("tx added as orphan", tx); memPool.OrphanTxPool.Add(txHash, tx); return(TxResultEnum.Orphan); case IsTxOrphanResult.Invalid: BlockChainTrace.Information("tx refers to an output that cannot exist", tx); removeDeps(); return(TxResultEnum.Invalid); } if (IsCoinbaseTx(ptx)) { BlockChainTrace.Information("tx is coinbase", tx); return(TxResultEnum.Invalid); } if (!IsReferencedCoinbaseTxsValid(dbTx, ptx)) { BlockChainTrace.Information("referenced coinbase immature", tx); return(TxResultEnum.Invalid); } //if (!IsStructurallyValidTx(dbTx, ptx)) //{ // BlockChainTrace.Information("tx invalid - structural", ptx); // return TxResultEnum.Invalid; //} byte[] contractHash; switch (IsContractGeneratedTx(ptx, out contractHash)) { case IsContractGeneratedTxResult.ContractGenerated: if (!ActiveContractSet.IsActive(dbTx, contractHash)) { BlockChainTrace.Information("tx added to ICTx mempool", tx); BlockChainTrace.Information(" of contract", contractHash); memPool.TxPool.ICTxPool.Add(txHash, ptx); return(TxResultEnum.InactiveContract); } var utxoLookup = UtxoLookupFactory(dbTx, false, ptx); var contractFunction = ActiveContractSet.GetContractFunction(dbTx, contractHash); if (!IsValidAutoTx(ptx, utxoLookup, contractHash, contractFunction)) { BlockChainTrace.Information("auto-tx invalid", ptx); removeDeps(); return(TxResultEnum.Invalid); } break; case IsContractGeneratedTxResult.Invalid: BlockChainTrace.Information("tx invalid - input locks", tx); return(TxResultEnum.Invalid); case IsContractGeneratedTxResult.NotContractGenerated: // assume user generated if (!IsValidUserGeneratedTx(dbTx, ptx)) { BlockChainTrace.Information("tx invalid - input locks", tx); removeDeps(); return(TxResultEnum.Invalid); } break; } BlockChainTrace.Information("tx added to mempool", ptx); memPool.TxPool.Add(txHash, ptx); new TxMessage(txHash, ptx, TxStateEnum.Unconfirmed).Publish(); new HandleOrphansOfTxAction(txHash).Publish(); } return(TxResultEnum.Accepted); } }
public HashDictionary <ACSItem> GetExpiringList(TransactionContext dbTx, uint blockNumber) { #if TRACE All(dbTx).Where(t => t.Item2.LastBlock == blockNumber).ToList().ForEach(t => BlockChainTrace.Information($"contract due to expire at {blockNumber}", t.Item1)); #endif var values = new HashDictionary <ACSItem>(); foreach (var contract in All(dbTx).Where(t => t.Item2.LastBlock <= blockNumber)) { values[contract.Item1] = contract.Item2; } return(values); }