/// <summary> /// Load Utxos for the address /// </summary> /// <returns></returns> public async Task LoadUtxos() { //Utxos.Clear(); var ux = await NeblioTransactionHelpers.GetAddressUtxosObjects(Address); // add new ones foreach (var u in ux) { var utx = new NeblioBuilderUtxo(u); await utx.LoadInfo(); if (!Utxos.TryGetValue(u.Txid, out var ut)) { Utxos.TryAdd(utx.Utxo.Txid + ":" + utx.Utxo.Index.ToString(), utx); } } // remove old ones if (ux.Count > Utxos.Count) { foreach (var u in Utxos) { if (!ux.Contains(u.Value.Utxo)) { Utxos.TryRemove(u.Key, out var ur); } } } }
public static async Task <VerifyNFTTicketDto> LoadNFTTicketToVerify(OwnershipVerificationCodeDto dto, string eventId, List <string> allowedMintingAddresses) { if (string.IsNullOrEmpty(dto.TxId)) { throw new Exception("Utxo id must be provided."); } if (string.IsNullOrEmpty(dto.Signature)) { throw new Exception("Signature id must be provided."); } if (string.IsNullOrEmpty(eventId)) { throw new Exception("Event Id must be provided."); } if (allowedMintingAddresses == null || allowedMintingAddresses.Count == 0) { throw new Exception("You must provide list of allowed minting addresses."); } var msg = CreateMessage(dto.TxId); // create verification message ASAP because of time relation var txi = await NeblioTransactionHelpers.GetTransactionInfo(dto.TxId); var Time = TimeHelpers.UnixTimestampToDateTime((double)txi.Blocktime); bool isYesterdayOrOlder = DateTime.Today - Time.Date >= TimeSpan.FromDays(1); if (isYesterdayOrOlder) { throw new Exception("This ticket was used earlier than today. Or it is not used at all."); } var metadata = await NeblioTransactionHelpers.GetTransactionMetadata(NFTHelpers.TokenId, dto.TxId); if (metadata.TryGetValue("NFT", out var isnft)) { if (isnft != "true") { throw new Exception("This is not NFT"); } } if (metadata.TryGetValue("Type", out var type)) { if (type != "NFT Ticket") { throw new Exception("This is not NFT Ticket"); } } if (metadata.TryGetValue("EventId", out var nfteventId)) { if (nfteventId != eventId) { throw new Exception("Event Id on the ticket does not match the requested."); } } if (metadata.TryGetValue("Used", out var used)) { if (used != "true") { throw new Exception("This NFT Ticket is not used."); } } else { throw new Exception("This NFT Ticket is not used."); } var tx = Transaction.Parse(txi.Hex, NeblioTransactionHelpers.Network); var outDto = new VerifyNFTTicketDto(); if (tx != null && tx.Outputs.Count > 0 && tx.Inputs.Count > 0) { var outp = tx.Outputs[0]; var inpt = tx.Inputs[0]; if (outp != null && inpt != null) { var scr = outp.ScriptPubKey; var add = scr.GetDestinationAddress(NeblioTransactionHelpers.Network); var addi = inpt.ScriptSig.GetSignerAddress(NeblioTransactionHelpers.Network); if (add != addi) { throw new Exception("This ticket was not used on this address."); } var pubkey = inpt.ScriptSig.GetAllPubKeys().FirstOrDefault(); if (pubkey == null) { throw new Exception("Cannot Load the owner Public Key."); } // verify of the signature of the NFT var verres = await ECDSAProvider.VerifyMessage(msg, dto.Signature, pubkey); //var vmsg = await ECDSAProvider.VerifyMessage(msg, dto.Signature, pubkey); if (!verres.Item1) { throw new Exception("Signature of the NFT is not valid."); } // check if the NFT is still as utxo on the address var utxos = await NeblioTransactionHelpers.GetAddressUtxosObjects(add.ToString()); if (utxos.FirstOrDefault(u => (u.Txid == dto.TxId && u.Value == 10000 && u.Tokens.Count > 0 && u.Tokens.FirstOrDefault()?.Amount == 1)) == null) { throw new Exception("This ticket is not available on the address as spendable."); } // check if in previous transaction the ticket was unused var prevmeta = await NeblioTransactionHelpers.GetTransactionMetadata(NFTHelpers.TokenId, inpt.PrevOut.Hash.ToString()); if (prevmeta.TryGetValue("NFT", out var isprevnft)) { if (isprevnft != "true") { throw new Exception("This is not NFT"); } } if (prevmeta.TryGetValue("Type", out var prevtype)) { if (prevtype != "NFT Ticket") { throw new Exception("This is not NFT Ticket"); } } if (prevmeta.TryGetValue("EventId", out var prevnfteventId)) { if (prevnfteventId != eventId) { throw new Exception("Event Id on the ticket does not match the requested."); } } if (prevmeta.TryGetValue("Used", out var prevused)) { if (prevused == "true") { throw new Exception("This NFT Ticket was already used in previous transaction."); } } // todo track origin for check minting address var res = await VerifyNFTTicketOrigin(inpt.PrevOut.Hash.ToString(), eventId, allowedMintingAddresses); if (!res.Item1) { throw new Exception($"Ticket was not minted on allowed address. The origin is from: {res.Item2.Item2}"); } outDto.IsMintedByAllowedAddress = true; outDto.MintAddress = res.Item2.Item2; var nft = new TicketNFT(""); await nft.LoadLastData(metadata); // fill with already loaded the newest NFT metadata nft.Time = Time; outDto.IsSignatureValid = true; outDto.NFT = nft; outDto.OwnerAddress = add.ToString(); outDto.IsUsedOnSameAddress = true; outDto.OwnerPubKey = pubkey; outDto.TxId = dto.TxId; } } return(outDto); }