public async Task <IActionResult> GetIdentityVerificationPolicy(string address) { // validate address if (!AddressParser.IsValidAddress(address, _network)) { return(StatusCode(StatusCodes.Status400BadRequest, $"Invalid address {address}")); } // check contract exists var numericAddress = address.ToUint160(_network); if (!_stateRepositoryRoot.IsExist(numericAddress)) { return(StatusCode(StatusCodes.Status404NotFound, $"No smart contract found at address {address}")); } var requiresIdentityVerification = RetrieveIdentityVerificationPolicy(numericAddress); return(Ok(requiresIdentityVerification)); }
public async Task <IActionResult> GetTickets(string address) { // validate address if (!AddressParser.IsValidAddress(address, _network)) { return(StatusCode(StatusCodes.Status400BadRequest, $"Invalid address {address}")); } // check contract exists var numericAddress = address.ToUint160(_network); if (!_stateRepositoryRoot.IsExist(numericAddress)) { return(StatusCode(StatusCodes.Status404NotFound, $"No smart contract found at address {address}")); } // retrieve value var tickets = RetrieveTickets(numericAddress); return(Ok(tickets)); }
public async Task <IActionResult> GetTicketReleaseFee(string address) { // validate address if (!AddressParser.IsValidAddress(address, _network)) { return(StatusCode(StatusCodes.Status400BadRequest, $"Invalid address {address}")); } // check contract exists var numericAddress = address.ToUint160(_network); if (!_stateRepositoryRoot.IsExist(numericAddress)) { return(StatusCode(StatusCodes.Status404NotFound, $"No smart contract found at address {address}")); } // retrieve value var serializedValue = _stateRepositoryRoot.GetStorageValue(numericAddress, _serializer.Serialize("ReleaseFee")); return(Ok(_serializer.ToUInt64(serializedValue))); }
public async Task <IActionResult> EndSale(string address, EndSaleRequest endSaleRequest) { // validate address if (!AddressParser.IsValidAddress(address, _network)) { return(StatusCode(StatusCodes.Status400BadRequest, $"Invalid address {address}")); } // check contract exists var numericAddress = address.ToUint160(_network); if (!_stateRepositoryRoot.IsExist(numericAddress)) { return(StatusCode(StatusCodes.Status404NotFound, $"No smart contract found at address {address}")); } if (!HasSaleEnded(numericAddress)) { return(StatusCode(StatusCodes.Status409Conflict, "Sale is not currently active or has not ended")); } // check connections if (!_connectionManager.ConnectedPeers.Any()) { _logger.LogTrace("No connected peers"); return(StatusCode(StatusCodes.Status403Forbidden, "Can't send transaction as the node requires at least one connection.")); } // build transaction var callTxResponse = _smartContractTransactionService.BuildCallTx(new BuildCallContractTransactionRequest { AccountName = endSaleRequest.AccountName, Amount = "0", ContractAddress = address, FeeAmount = "0", GasLimit = SmartContractFormatLogic.GasLimitMaximum, GasPrice = endSaleRequest.GasPrice, MethodName = nameof(TicketContract.EndSale), Outpoints = endSaleRequest.Outpoints, Parameters = Array.Empty <string>(), Password = endSaleRequest.Password, Sender = endSaleRequest.Sender, WalletName = endSaleRequest.WalletName, }); if (!callTxResponse.Success) { return(StatusCode(StatusCodes.Status400BadRequest, callTxResponse.Message)); } // broadcast transaction var transaction = _network.CreateTransaction(callTxResponse.Hex); await _broadcasterManager.BroadcastTransactionAsync(transaction); var transactionBroadCastEntry = _broadcasterManager.GetTransaction(transaction.GetHash()); // check if transaction was added to mempool if (transactionBroadCastEntry?.State == Stratis.Bitcoin.Features.Wallet.Broadcasting.State.CantBroadcast) { _logger.LogError("Exception occurred: {0}", transactionBroadCastEntry.ErrorMessage); return(StatusCode(StatusCodes.Status500InternalServerError, transactionBroadCastEntry.ErrorMessage)); } var transactionHash = transaction.GetHash().ToString(); return(Created($"/api/smartContracts/receipt?txHash={transactionHash}", transactionHash)); }
public async Task <IActionResult> BeginSale(string address, BeginSaleRequest beginSaleRequest) { // validate address if (!AddressParser.IsValidAddress(address, _network)) { return(StatusCode(StatusCodes.Status400BadRequest, $"Invalid address {address}")); } // check end of sale is in future if (beginSaleRequest.Details.EndOfSale <= (ulong)_consensusManager.Tip.Height) { return(StatusCode(StatusCodes.Status400BadRequest, $"End of sale must be in the future. Consensus height is at {_consensusManager.Tip.Height}")); } // check contract exists var numericAddress = address.ToUint160(_network); if (!_stateRepositoryRoot.IsExist(numericAddress)) { return(StatusCode(StatusCodes.Status404NotFound, $"No smart contract found at address {address}")); } if (HasActiveSale(numericAddress)) { return(StatusCode(StatusCodes.Status409Conflict, "Sale is currently active")); } // check connections if (!_connectionManager.ConnectedPeers.Any()) { _logger.LogTrace("No connected peers"); return(StatusCode(StatusCodes.Status403Forbidden, "Can't send transaction as the node requires at least one connection.")); } // retrieve tickets var serializedTickets = _stateRepositoryRoot.GetStorageValue(numericAddress, _serializer.Serialize(nameof(TicketContract.Tickets))); var tickets = _serializer.ToArray <Ticket>(serializedTickets); if (beginSaleRequest.SeatPrices.Length != tickets.Length) { return(StatusCode(StatusCodes.Status400BadRequest, "Must supply prices for all seats")); } foreach (var seatPrice in beginSaleRequest.SeatPrices) { for (int i = 0; i < tickets.Length; i++) { if (tickets[i].Seat.Equals(seatPrice.Seat)) { tickets[i].Price = seatPrice.Price; } } } var ticketsParameter = $"{Serialization.TypeIdentifiers[typeof(byte[])]}#{Serialization.ByteArrayToHex(_serializer.Serialize(tickets))}"; var showNameParameter = $"{Serialization.TypeIdentifiers[typeof(string)]}#{beginSaleRequest.Details.Name}"; var organiserParameter = $"{Serialization.TypeIdentifiers[typeof(string)]}#{beginSaleRequest.Details.Organiser}"; var timeParameter = $"{Serialization.TypeIdentifiers[typeof(ulong)]}#{beginSaleRequest.Details.Time}"; var endOfSaleParameter = $"{Serialization.TypeIdentifiers[typeof(ulong)]}#{beginSaleRequest.Details.EndOfSale}"; // build transaction var callTxResponse = _smartContractTransactionService.BuildCallTx(new BuildCallContractTransactionRequest { AccountName = beginSaleRequest.AccountName, Amount = "0", ContractAddress = address, FeeAmount = "0", GasLimit = SmartContractFormatLogic.GasLimitMaximum, GasPrice = beginSaleRequest.GasPrice, MethodName = nameof(TicketContract.BeginSale), Outpoints = beginSaleRequest.Outpoints, Parameters = new string[] { ticketsParameter, showNameParameter, organiserParameter, timeParameter, endOfSaleParameter }, Password = beginSaleRequest.Password, Sender = beginSaleRequest.Sender, WalletName = beginSaleRequest.WalletName, }); if (!callTxResponse.Success) { return(StatusCode(StatusCodes.Status400BadRequest, callTxResponse.Message)); } // broadcast transaction var transaction = _network.CreateTransaction(callTxResponse.Hex); await _broadcasterManager.BroadcastTransactionAsync(transaction); var transactionBroadCastEntry = _broadcasterManager.GetTransaction(transaction.GetHash()); // check if transaction was added to mempool if (transactionBroadCastEntry?.State == Stratis.Bitcoin.Features.Wallet.Broadcasting.State.CantBroadcast) { _logger.LogError("Exception occurred: {0}", transactionBroadCastEntry.ErrorMessage); return(StatusCode(StatusCodes.Status500InternalServerError, transactionBroadCastEntry.ErrorMessage)); } var transactionHash = transaction.GetHash().ToString(); return(Created($"/api/smartContracts/receipt?txHash={transactionHash}", transactionHash)); }
public async Task <IActionResult> ReleaseTicket(string address, ReleaseTicketRequest releaseTicketRequest) { // validate address if (!AddressParser.IsValidAddress(address, _network)) { return(StatusCode(StatusCodes.Status400BadRequest, $"Invalid address {address}")); } // check contract exists var numericAddress = address.ToUint160(_network); if (!_stateRepositoryRoot.IsExist(numericAddress)) { return(StatusCode(StatusCodes.Status404NotFound, $"No smart contract found at address {address}")); } // check for state of ticket var ticket = FindTicket(numericAddress, releaseTicketRequest.Seat); if (ticket.Equals(default(Ticket))) { return(StatusCode(StatusCodes.Status400BadRequest, $"Invalid seat {releaseTicketRequest.Seat.ToDisplayString()}")); } // check state of contract if (!HasOpenSale(numericAddress)) { return(StatusCode(StatusCodes.Status409Conflict, "Sale is not currently open")); } if (!IsRefundAvailable(numericAddress)) { return(StatusCode(StatusCodes.Status409Conflict, "Ticket release is no longer available")); } // verify ownership if (ticket.Address == Address.Zero || ticket.Address != releaseTicketRequest.Sender.ToAddress(_network)) { return(StatusCode(StatusCodes.Status400BadRequest, $"Ticket for seat {releaseTicketRequest.Seat.ToDisplayString()} not owned by {releaseTicketRequest.Sender}")); } // check connections if (!_connectionManager.ConnectedPeers.Any()) { _logger.LogTrace("No connected peers"); return(StatusCode(StatusCodes.Status403Forbidden, "Can't send transaction as the node requires at least one connection.")); } var seatBytes = _serializer.Serialize(releaseTicketRequest.Seat); var seatParameter = $"{Serialization.TypeIdentifiers[typeof(byte[])]}#{Serialization.ByteArrayToHex(seatBytes)}"; // build transaction var callTxResponse = _smartContractTransactionService.BuildCallTx(new BuildCallContractTransactionRequest { AccountName = releaseTicketRequest.AccountName, Amount = "0", ContractAddress = address, FeeAmount = "0", GasLimit = SmartContractFormatLogic.GasLimitMaximum, GasPrice = releaseTicketRequest.GasPrice, MethodName = nameof(TicketContract.ReleaseTicket), Outpoints = releaseTicketRequest.Outpoints, Parameters = new string[] { seatParameter }, Password = releaseTicketRequest.Password, Sender = releaseTicketRequest.Sender, WalletName = releaseTicketRequest.WalletName, }); if (!callTxResponse.Success) { return(StatusCode(StatusCodes.Status400BadRequest, callTxResponse.Message)); } // broadcast transaction var transaction = _network.CreateTransaction(callTxResponse.Hex); await _broadcasterManager.BroadcastTransactionAsync(transaction); var transactionBroadCastEntry = _broadcasterManager.GetTransaction(transaction.GetHash()); // check if transaction was added to mempool if (transactionBroadCastEntry?.State == Stratis.Bitcoin.Features.Wallet.Broadcasting.State.CantBroadcast) { _logger.LogError("Exception occurred: {0}", transactionBroadCastEntry.ErrorMessage); return(StatusCode(StatusCodes.Status500InternalServerError, transactionBroadCastEntry.ErrorMessage)); } var transactionHash = transaction.GetHash().ToString(); return(Created($"/api/smartContracts/receipt?txHash={transactionHash}", transactionHash)); }
public async Task <IActionResult> ReserveTicket(string address, ReserveTicketRequest reserveTicketRequest) { // validate address if (!AddressParser.IsValidAddress(address, _network)) { return(StatusCode(StatusCodes.Status400BadRequest, $"Invalid address {address}")); } // check contract exists var numericAddress = address.ToUint160(_network); if (!_stateRepositoryRoot.IsExist(numericAddress)) { return(StatusCode(StatusCodes.Status404NotFound, $"No smart contract found at address {address}")); } // check for state of ticket var ticket = FindTicket(numericAddress, reserveTicketRequest.Seat); if (ticket.Equals(default(Ticket))) { return(StatusCode(StatusCodes.Status400BadRequest, $"Invalid seat {reserveTicketRequest.Seat.ToDisplayString()}")); } // check contract state if (!HasOpenSale(numericAddress)) { return(StatusCode(StatusCodes.Status409Conflict, "Sale is not currently open")); } if (ticket.Address != Address.Zero) { return(StatusCode(StatusCodes.Status400BadRequest, $"Ticket for seat {reserveTicketRequest.Seat.ToDisplayString()} not available to purchase")); } var requiresIdentityVerification = RetrieveIdentityVerificationPolicy(numericAddress); if (requiresIdentityVerification && string.IsNullOrWhiteSpace(reserveTicketRequest.CustomerName)) { return(StatusCode(StatusCodes.Status400BadRequest, $"Customer name is required")); } // check connections if (!_connectionManager.ConnectedPeers.Any()) { _logger.LogTrace("No connected peers"); return(StatusCode(StatusCodes.Status403Forbidden, "Can't send transaction as the node requires at least one connection.")); } var seatBytes = _serializer.Serialize(reserveTicketRequest.Seat); var seatParameter = $"{Serialization.TypeIdentifiers[typeof(byte[])]}#{Serialization.ByteArrayToHex(seatBytes)}"; var secret = _stringGenerator.CreateUniqueString(15); CbcResult secretCipherResult; using (var cipherProvider = _cipherFactory.CreateCbcProvider()) { secretCipherResult = cipherProvider.Encrypt(secret); } var secretParameter = $"{Serialization.TypeIdentifiers[typeof(byte[])]}#{Serialization.ByteArrayToHex(secretCipherResult.Cipher)}"; CbcResult customerNameCipherResult = null; string customerNameParameter = null; if (requiresIdentityVerification) { using (var cipherProvider = _cipherFactory.CreateCbcProvider()) { customerNameCipherResult = cipherProvider.Encrypt(reserveTicketRequest.CustomerName); } customerNameParameter = $"{Serialization.TypeIdentifiers[typeof(byte[])]}#{Serialization.ByteArrayToHex(customerNameCipherResult.Cipher)}"; } // build transaction var parameterList = new List <string> { seatParameter, secretParameter }; if (customerNameParameter != null) { parameterList.Add(customerNameParameter); } var callTxResponse = _smartContractTransactionService.BuildCallTx(new BuildCallContractTransactionRequest { AccountName = reserveTicketRequest.AccountName, Amount = StratoshisToStrats(ticket.Price), ContractAddress = address, FeeAmount = "0", GasLimit = SmartContractFormatLogic.GasLimitMaximum, GasPrice = reserveTicketRequest.GasPrice, MethodName = nameof(TicketContract.Reserve), Outpoints = reserveTicketRequest.Outpoints, Parameters = parameterList.ToArray(), Password = reserveTicketRequest.Password, Sender = reserveTicketRequest.Sender, WalletName = reserveTicketRequest.WalletName, }); if (!callTxResponse.Success) { return(StatusCode(StatusCodes.Status400BadRequest, callTxResponse.Message)); } // broadcast transaction var transaction = _network.CreateTransaction(callTxResponse.Hex); await _broadcasterManager.BroadcastTransactionAsync(transaction); var transactionBroadCastEntry = _broadcasterManager.GetTransaction(transaction.GetHash()); // check if transaction was added to mempool if (transactionBroadCastEntry?.State == Stratis.Bitcoin.Features.Wallet.Broadcasting.State.CantBroadcast) { _logger.LogError("Exception occurred: {0}", transactionBroadCastEntry.ErrorMessage); return(StatusCode(StatusCodes.Status500InternalServerError, transactionBroadCastEntry.ErrorMessage)); } var transactionHash = transaction.GetHash().ToString(); var cbcSecretValues = new CbcSecret { Plaintext = secret, Key = secretCipherResult.Key, IV = secretCipherResult.IV }; var cbcCustomerValues = requiresIdentityVerification ? new CbcValues { Key = customerNameCipherResult.Key, IV = customerNameCipherResult.IV } : null; return(Created( $"/api/smartContracts/receipt?txHash={transactionHash}", new TicketReservationDetailsResponse { TransactionHash = transactionHash, Secret = cbcSecretValues, CustomerName = cbcCustomerValues })); }