private TaprootFullPubKey(ECXOnlyPubKey outputKey, bool outputParity, TaprootInternalPubKey internalKey, uint256?merkleRoot) : base(outputKey) { OutputKey = new TaprootPubKey(outputKey); OutputKeyParity = outputParity; InternalKey = internalKey; MerkleRoot = merkleRoot; }
public void ReadTLV(TLVReader reader, Network network) { if (reader.ReadU16() != TLVType) { throw new FormatException("Invalid TLV type for sign"); } ContractId = reader.ReadUInt256(); CetSigs = CetSigs.ParseFromTLV(reader); using (var r = reader.StartReadRecord()) { if (r.Type != FundingSignaturesTLVType) { throw new FormatException("Invalid TLV type for funding signatures"); } FundingSigs = new List <WitScript>(); var witnesses = r.ReadU16(); for (int i = 0; i < witnesses; i++) { var elementsCount = reader.ReadU16(); var witnessBytes = new byte[elementsCount][]; for (int y = 0; y < elementsCount; y++) { var elementSize = reader.ReadU16(); witnessBytes[y] = new byte[elementSize]; r.ReadBytes(witnessBytes[y]); } FundingSigs.Add(new WitScript(witnessBytes)); } } }
public static TaprootKeyPair CreateTaprootPair(Key key, uint256?merkleRoot) { if (key is null) { throw new ArgumentNullException(nameof(key)); } return(new TaprootKeyPair(key, key.PubKey.GetTaprootFullPubKey(merkleRoot))); }
public TaprootExecutionData(int inputIndex, uint256?tapleaf) { if (inputIndex < 0) { throw new ArgumentOutOfRangeException(nameof(inputIndex)); } InputIndex = inputIndex; HashVersion = tapleaf is null ? HashVersion.Taproot : HashVersion.Tapscript; TapleafHash = tapleaf; }
public SmartTransaction(Transaction transaction, uint height, uint256?blockHash = null, int blockIndex = 0, bool isReplacement = false, DateTimeOffset firstSeen = default) { Transaction = transaction; Transaction.PrecomputeHash(false, true); Height = height; BlockHash = blockHash; BlockIndex = blockIndex; FirstSeen = firstSeen == default ? DateTimeOffset.UtcNow : firstSeen; IsReplacement = isReplacement; }
public bool CheckTapTweak(TaprootInternalPubKey internalPubKey, uint256?merkleRoot, bool parity) { if (internalPubKey is null) { throw new ArgumentNullException(nameof(internalPubKey)); } Span <byte> tweak32 = stackalloc byte[32]; TaprootFullPubKey.ComputeTapTweak(internalPubKey, merkleRoot, tweak32); return(this.pubkey.CheckIsTweakedWith(internalPubKey.pubkey, tweak32, parity)); }
internal static void ComputeTapTweak(TaprootInternalPubKey internalKey, uint256?merkleRoot, Span <byte> tweak32) { using Secp256k1.SHA256 sha = new Secp256k1.SHA256(); sha.InitializeTagged("TapTweak"); internalKey.pubkey.WriteToSpan(tweak32); sha.Write(tweak32); if (merkleRoot is uint256) { merkleRoot.ToBytes(tweak32); sha.Write(tweak32); } sha.GetHash(tweak32); }
public static TaprootFullPubKey Create(TaprootInternalPubKey internalKey, uint256?merkleRoot) { if (internalKey is null) { throw new ArgumentNullException(nameof(internalKey)); } Span <byte> tweak32 = stackalloc byte[32]; ComputeTapTweak(internalKey, merkleRoot, tweak32); var outputKey = internalKey.pubkey.AddTweak(tweak32).ToXOnlyPubKey(out var parity); return(new TaprootFullPubKey(outputKey, parity, internalKey, merkleRoot)); }
private async Task <Repository.DLCState> GetDLC(uint256?contractId) { if (contractId is null) { // TODO: Allow to pass a hint via command line throw new CommandException("signed", "This accept message does not match any of our DLC"); } else { var dlc = await Repository.GetDLC(contractId); if (dlc is null) { throw new CommandException("signed", "This accept message does not match any of our DLC"); } return(dlc); } }
public SmartTransaction(Transaction transaction, Height height, uint256?blockHash = null, int blockIndex = 0, SmartLabel?label = null, bool isReplacement = false, DateTimeOffset firstSeen = default) { Transaction = transaction; // Because we don't modify those transactions, we can cache the hash Transaction.PrecomputeHash(false, true); Label = label ?? SmartLabel.Empty; Height = height; BlockHash = blockHash; BlockIndex = blockIndex; FirstSeen = firstSeen == default ? DateTimeOffset.UtcNow : firstSeen; IsReplacement = isReplacement; WalletInputs = new HashSet <SmartCoin>(Transaction.Inputs.Count); WalletOutputs = new HashSet <SmartCoin>(Transaction.Outputs.Count); }
public TaprootSignature SignTaprootKeySpend(uint256 hash, uint256?merkleRoot, uint256?aux, TaprootSigHash sigHash) { if (hash == null) { throw new ArgumentNullException(nameof(hash)); } AssertNotDisposed(); var eckey = _ECKey; if (PubKey.Parity) { eckey = new Secp256k1.ECPrivKey(_ECKey.sec.Negate(), _ECKey.ctx, true); } Span <byte> buf = stackalloc byte[32]; TaprootFullPubKey.ComputeTapTweak(PubKey.TaprootInternalKey, merkleRoot, buf); eckey = eckey.TweakAdd(buf); hash.ToBytes(buf); var sig = aux?.ToBytes() is byte[] auxbytes?eckey.SignBIP340(buf, auxbytes) : eckey.SignBIP340(buf); return(new TaprootSignature(new SchnorrSignature(sig), sigHash)); }
public void ReadTLV(TLVReader reader, Network network) { if (reader.ReadU16() != TLVType) { throw new FormatException("Invalid TLV type for accept"); } TemporaryContractId = reader.ReadUInt256(); TotalCollateral = Money.Satoshis(reader.ReadU64()); PubKeys = new PubKeyObject(); var pk = new byte[33]; reader.ReadBytes(pk); PubKeys.FundingKey = new PubKey(pk, true); PubKeys.PayoutAddress = reader.ReadScript().GetDestinationAddress(network); var inputLen = reader.ReadU16(); FundingInputs = new FundingInput[inputLen]; for (int i = 0; i < inputLen; i++) { FundingInputs[i] = FundingInput.ParseFromTLV(reader, network); } ChangeAddress = reader.ReadScript().GetDestinationAddress(network); CetSigs = CetSigs.ParseFromTLV(reader); }
public TaprootFullPubKey GetTaprootFullPubKey(uint256?merkleRoot) { return(TaprootFullPubKey.Create(this, merkleRoot)); }
public bool VerifyTaproot(uint256 hash, uint256?merkleRoot, SchnorrSignature signature) { return(this.GetTaprootFullPubKey(merkleRoot).OutputKey.VerifySignature(hash, signature)); }
/// <inheritdoc /> public override uint256?ReadJson(JsonReader reader, Type objectType, uint256?existingValue, bool hasExistingValue, JsonSerializer serializer) { string?value = (string?)reader.Value; return(value is null ? default : new uint256(value)); }
public async Task <(PaymentReceivedType, LNMoney)> PaymentReceived(Primitives.PaymentHash paymentHash, LNMoney amount, uint256?secret = null) { var tx = await _engine.OpenTransaction(); using var invoiceRow = await tx.GetTable(DBKeys.HashToInvoice).Get(paymentHash.ToBytes(false)); if (invoiceRow != null) { var res = PaymentRequest.Parse(await invoiceRow.ReadValueString()); if (res.IsError) { throw new NRustLightningException($"Failed to read payment request for {res.ErrorValue}"); } var req = res.ResultValue; if (req.AmountValue is null) { return(PaymentReceivedType.Ok, amount); } var intendedAmount = req.AmountValue.Value; if (amount.MilliSatoshi < intendedAmount.MilliSatoshi) { return(PaymentReceivedType.AmountTooLow, intendedAmount); } if (intendedAmount.MilliSatoshi * 2 < amount.MilliSatoshi) { return(PaymentReceivedType.AmountTooHigh, intendedAmount); } return(PaymentReceivedType.Ok, intendedAmount); } return(PaymentReceivedType.UnknownPaymentHash, amount); }
public TaprootFullPubKey GetTaprootFullPubKey(uint256?merkleRoot) { return(TaprootInternalKey.GetTaprootFullPubKey(merkleRoot)); }
public static InputRoundSignaturePair CreateInputRoundSignaturePair(Key?key = null, uint256?roundHash = null) { var rh = roundHash ?? BitcoinFactory.CreateUint256(); if (key is null) { using Key k = new(); return(new InputRoundSignaturePair( BitcoinFactory.CreateOutPoint(), k.SignCompact(rh))); } else { return(new InputRoundSignaturePair( BitcoinFactory.CreateOutPoint(), key.SignCompact(rh))); } }
/// <inheritdoc /> public override void WriteJson(JsonWriter writer, uint256?value, JsonSerializer serializer) { writer.WriteValue(value?.ToString()); }
public void ReadTLV(TLVReader reader, Network network) { if (reader.ReadU16() != TLVType) { throw new FormatException("Invalid TLV type for offer"); } reader.ReadByte(); // contract_flags ChainHash = reader.ReadUInt256(); if (network.GenesisHash != network.GenesisHash) { throw new FormatException("Invalid ChainHash"); } Span <byte> buf = stackalloc byte[64]; using (var ciRecord = reader.StartReadRecord()) { if (ciRecord.Type != TLVContractInfoType) { throw new FormatException("Invalid TLV type for contract info"); } List <ContractInfo> cis = new List <ContractInfo>(); while (!ciRecord.IsEnd) { ciRecord.ReadBytes(buf.Slice(0, 32)); var sats = ciRecord.ReadU64(); cis.Add(new Messages.ContractInfo(new DiscreteOutcome(buf.Slice(0, 32).ToArray()), Money.Satoshis(sats))); } ContractInfo = cis.ToArray(); } using (var oracleRecord = reader.StartReadRecord()) { if (oracleRecord.Type != TLVOracleInfoType) { throw new FormatException("Invalid TLV type for oracle info"); } oracleRecord.ReadBytes(buf.Slice(0, 64)); OracleInfo = OracleInfo.Create(buf); } PubKeys = new PubKeyObject(); reader.ReadBytes(buf.Slice(0, 33)); PubKeys.FundingKey = new PubKey(buf.Slice(0, 33).ToArray()); PubKeys.PayoutAddress = reader.ReadScript().GetDestinationAddress(network); if (PubKeys.PayoutAddress is null) { throw new FormatException("Invalid script"); } TotalCollateral = Money.Satoshis(reader.ReadU64()); var fiCount = reader.ReadU16(); List <FundingInput> fis = new List <FundingInput>(); while (fiCount > 0) { fis.Add(FundingInput.ParseFromTLV(reader, network)); fiCount--; } FundingInputs = fis.ToArray(); ChangeAddress = reader.ReadScript().GetDestinationAddress(network); if (ChangeAddress is null) { throw new FormatException("Invalid script"); } FeeRate = new FeeRate(Money.Satoshis(reader.ReadU64()), 1); Timeouts = new Timeouts() { ContractMaturity = reader.ReadU32(), ContractTimeout = reader.ReadU32() }; }
public static InputRoundSignaturePair CreateInputRoundSignaturePair(Key?key = null, uint256?roundHash = null) { var rh = roundHash ?? BitcoinFactory.CreateUint256(); var outpoint = BitcoinFactory.CreateOutPoint(); var coinJoinInputCommitmentData = new CoinJoinInputCommitmentData("CoinJoinCoordinatorIdentifier", rh); var signingKey = key ?? new(); return(new InputRoundSignaturePair( outpoint, OwnershipProof.GenerateCoinJoinInputProof(signingKey, coinJoinInputCommitmentData).ToBytes())); }
public static InputRoundSignaturePair[] CreateInputRoundSignaturePairs(int count, uint256?roundHash = null) { List <InputRoundSignaturePair> pairs = new(); for (int i = 0; i < count; i++) { pairs.Add(CreateInputRoundSignaturePair(null, roundHash)); } return(pairs.ToArray()); }
public static IEnumerable <InputRoundSignaturePair> CreateInputRoundSignaturePairs(int count, uint256?roundHash = null) { for (int i = 0; i < count; i++) { yield return(CreateInputRoundSignaturePair(null, roundHash)); } }
public static InputRoundSignaturePair[] CreateInputRoundSignaturePairs(IEnumerable <Key> keys, uint256?roundHash = null) { List <InputRoundSignaturePair> pairs = new(); foreach (var key in keys) { pairs.Add(CreateInputRoundSignaturePair(key, roundHash)); } return(pairs.ToArray()); }
public TaprootSignature SignTaprootKeySpend(uint256 hash, uint256?merkleRoot, TaprootSigHash sigHash) { return(SignTaprootKeySpend(hash, merkleRoot, null, sigHash)); }
public void Synchronize() { Task.Run(async() => { try { if (Interlocked.Read(ref _workerCount) >= 2) { return; } Interlocked.Increment(ref _workerCount); while (Interlocked.Read(ref _workerCount) != 1) { await Task.Delay(100).ConfigureAwait(false); } if (IsStopping) { return; } try { Interlocked.Exchange(ref _serviceStatus, Running); while (IsRunning) { try { SyncInfo syncInfo = await GetSyncInfoAsync().ConfigureAwait(false); uint currentHeight; uint256?currentHash = null; using (await IndexLock.LockAsync()) { if (Index.Count != 0) { var lastIndex = Index[^ 1]; currentHeight = lastIndex.Header.Height; currentHash = lastIndex.Header.BlockHash; } else { currentHash = StartingHeight == 0 ? uint256.Zero : await RpcClient.GetBlockHashAsync((int)StartingHeight - 1).ConfigureAwait(false); currentHeight = StartingHeight - 1; } } var coreNotSynced = !syncInfo.IsCoreSynchronized; var tipReached = syncInfo.BlockCount == currentHeight; var isTimeToRefresh = DateTimeOffset.UtcNow - syncInfo.BlockchainInfoUpdated > TimeSpan.FromMinutes(5); if (coreNotSynced || tipReached || isTimeToRefresh) { syncInfo = await GetSyncInfoAsync().ConfigureAwait(false); } // If wasabi filter height is the same as core we may be done. if (syncInfo.BlockCount == currentHeight) { // Check that core is fully synced if (syncInfo.IsCoreSynchronized && !syncInfo.InitialBlockDownload) { // Mark the process notstarted, so it can be started again // and finally block can mark it as stopped. Interlocked.Exchange(ref _serviceStatus, NotStarted); return; } else { // Knots is catching up give it a 10 seconds await Task.Delay(10000).ConfigureAwait(false); continue; } } uint nextHeight = currentHeight + 1; uint256 blockHash = await RpcClient.GetBlockHashAsync((int)nextHeight).ConfigureAwait(false); VerboseBlockInfo block = await RpcClient.GetVerboseBlockAsync(blockHash).ConfigureAwait(false); // Check if we are still on the best chain, // if not rewind filters till we find the fork. if (currentHash != block.PrevBlockHash) { Logger.LogWarning("Reorg observed on the network."); await ReorgOneAsync().ConfigureAwait(false); // Skip the current block. continue; } var scripts = FetchScripts(block); GolombRiceFilter filter; if (scripts.Any()) { filter = new GolombRiceFilterBuilder() .SetKey(block.Hash) .SetP(20) .SetM(1 << 20) .AddEntries(scripts.Select(x => x.ToCompressedBytes())) .Build(); } else { // We cannot have empty filters, because there was a bug in GolombRiceFilterBuilder that evaluates empty filters to true. // And this must be fixed in a backwards compatible way, so we create a fake filter with a random scp instead. filter = CreateDummyEmptyFilter(block.Hash); } var smartHeader = new SmartHeader(block.Hash, block.PrevBlockHash, nextHeight, block.BlockTime); var filterModel = new FilterModel(smartHeader, filter); await File.AppendAllLinesAsync(IndexFilePath, new[] { filterModel.ToLine() }).ConfigureAwait(false); using (await IndexLock.LockAsync()) { Index.Add(filterModel); } // If not close to the tip, just log debug. if (syncInfo.BlockCount - nextHeight <= 3 || nextHeight % 100 == 0) { Logger.LogInfo($"Created filter for block: {nextHeight}."); } else { Logger.LogDebug($"Created filter for block: {nextHeight}."); } LastFilterBuildTime = DateTimeOffset.UtcNow; }
public TaprootKeyPair CreateTaprootKeyPair(uint256?merkleRoot) { return(TaprootKeyPair.CreateTaprootPair(this, merkleRoot)); }
public static IEnumerable <InputRoundSignaturePair> CreateInputRoundSignaturePairs(IEnumerable <Key> keys, uint256?roundHash = null) { foreach (var key in keys) { yield return(CreateInputRoundSignaturePair(key, roundHash)); } }
public void Synchronize() { // Check permissions. using var _ = File.Open(IndexFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite); Task.Run(async() => { try { if (Interlocked.Read(ref _workerCount) >= 2) { return; } Interlocked.Increment(ref _workerCount); while (Interlocked.Read(ref _workerCount) != 1) { await Task.Delay(100).ConfigureAwait(false); } if (IsStopping) { return; } try { Interlocked.Exchange(ref _serviceStatus, Running); while (IsRunning) { try { SyncInfo syncInfo = await GetSyncInfoAsync().ConfigureAwait(false); uint currentHeight; uint256?currentHash = null; using (await IndexLock.LockAsync()) { if (Index.Count != 0) { var lastIndex = Index[^ 1]; currentHeight = lastIndex.Header.Height; currentHash = lastIndex.Header.BlockHash; } else { currentHash = StartingHeight == 0 ? uint256.Zero : await RpcClient.GetBlockHashAsync((int)StartingHeight - 1).ConfigureAwait(false); currentHeight = StartingHeight - 1; } } var coreNotSynced = !syncInfo.IsCoreSynchronized; var tipReached = syncInfo.BlockCount == currentHeight; var isTimeToRefresh = DateTimeOffset.UtcNow - syncInfo.BlockchainInfoUpdated > TimeSpan.FromMinutes(5); if (coreNotSynced || tipReached || isTimeToRefresh) { syncInfo = await GetSyncInfoAsync().ConfigureAwait(false); } // If wasabi filter height is the same as core we may be done. if (syncInfo.BlockCount == currentHeight) { // Check that core is fully synced if (syncInfo.IsCoreSynchronized && !syncInfo.InitialBlockDownload) { // Mark the process notstarted, so it can be started again // and finally block can mark it as stopped. Interlocked.Exchange(ref _serviceStatus, NotStarted); return; } else { // Knots is catching up give it a 10 seconds await Task.Delay(10000).ConfigureAwait(false); continue; } } uint nextHeight = currentHeight + 1; uint256 blockHash = await RpcClient.GetBlockHashAsync((int)nextHeight).ConfigureAwait(false); VerboseBlockInfo block = await RpcClient.GetVerboseBlockAsync(blockHash).ConfigureAwait(false); // Check if we are still on the best chain, // if not rewind filters till we find the fork. if (currentHash != block.PrevBlockHash) { Logger.LogWarning("Reorg observed on the network."); await ReorgOneAsync().ConfigureAwait(false); // Skip the current block. continue; } var filter = BuildFilterForBlock(block); var smartHeader = new SmartHeader(block.Hash, block.PrevBlockHash, nextHeight, block.BlockTime); var filterModel = new FilterModel(smartHeader, filter); await File.AppendAllLinesAsync(IndexFilePath, new[] { filterModel.ToLine() }).ConfigureAwait(false); using (await IndexLock.LockAsync()) { Index.Add(filterModel); } // If not close to the tip, just log debug. if (syncInfo.BlockCount - nextHeight <= 3 || nextHeight % 100 == 0) { Logger.LogInfo($"Created filter for block: {nextHeight}."); } else { Logger.LogDebug($"Created filter for block: {nextHeight}."); } LastFilterBuildTime = DateTimeOffset.UtcNow; }
public async Task <(PaymentReceivedType, LNMoney)> PaymentReceived(Primitives.PaymentHash paymentHash, LNMoney amount, uint256?secret = null) { var invoice = await _repository.GetInvoice(paymentHash); if (invoice != null) { if (invoice.AmountValue is null) { return(PaymentReceivedType.Ok, amount); } var intendedAmount = invoice.AmountValue.Value; if (amount.MilliSatoshi < intendedAmount.MilliSatoshi) { return(PaymentReceivedType.AmountTooLow, intendedAmount); } if (intendedAmount.MilliSatoshi * 2 < amount.MilliSatoshi) { return(PaymentReceivedType.AmountTooHigh, intendedAmount); } return(PaymentReceivedType.Ok, intendedAmount); } return(PaymentReceivedType.UnknownPaymentHash, amount); }