//Types.Output GetUTXO(Types.Outpoint outpoint, bool IsInBlock) //{ // using (TransactionContext dbTx = _DBContext.GetTransactionContext()) // { // return GetUTXO(outpoint, dbTx, IsInBlock); // } //} public UtxoLookup UtxoLookupFactory(TransactionContext dbTx, bool isInBlock, TransactionValidation.PointedTransaction ptx = null) { return(UtxoLookup.FromConverter(t => { bool valid; var output = GetUTXO(t, dbTx, isInBlock, out valid, ptx); return output == null ? FSharpOption <Types.Output> .None : new FSharpOption <Types.Output>(output); })); }
// 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); }
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 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)); }
bool IsTransactionValid(TransactionValidation.PointedTransaction ptx) { if (!HasUtxos(ptx)) { MinerTrace.Information("could not validate tx - utxo missing"); return(false); } var utxoLookup = UtxoLookup.FromConverter(outpoint => { var outputs = _UtxoSet.Where(t => t.Item1.Equals(outpoint)).Select(t => t.Item2); return(!outputs.Any() ? FSharpOption <Types.Output> .None : new FSharpOption <Types.Output>(outputs.First())); }); var contractLookup = FSharpFunc <byte[], FSharpOption <ContractFunction> > .FromConverter(contractHash => { if (!_ActiveContracts.Contains(contractHash)) { return(FSharpOption <ContractFunction> .None); } try { var code = new GetContractCodeAction(contractHash).Publish().Result; //TODO: module name var extration = ContractExamples.FStarExecution.extract(System.Text.Encoding.ASCII.GetString(code)); if (FSharpOption <string> .get_IsNone(extration)) { MinerTrace.Information("Could not extract contract"); return(null); } var compilation = ContractExamples.FStarExecution.compile(extration.Value); if (FSharpOption <byte[]> .get_IsNone(compilation)) { MinerTrace.Information("Could not complie contract"); return(null); } return(ContractExamples.FStarExecution.deserialize(compilation.Value).Value.Item1); } catch (Exception e) { MinerTrace.Error("Could not compile contract " + Convert.ToBase64String(contractHash), e); return(null); } }); if (!TransactionValidation.validateNonCoinbaseTx( ptx, utxoLookup, contractLookup )) { MinerTrace.Information("could not validate tx"); return(false); } MinerTrace.Information("validated tx"); //TODO: memory management issues. trying to explicitly collect DynamicMethods GC.Collect(); GC.WaitForPendingFinalizers(); return(true); }