// DEX #region BRK_DEX_DPOREQ public override async Task <APIResultCodes> PreSendAuthAsync(DagSystem sys, SendTransferBlock block, TransactionBlock last) { var symbol = block.Tags.ContainsKey("symbol") ? block.Tags["symbol"] : null; if (symbol == null) { return(APIResultCodes.InvalidName); } var provider = block.Tags.ContainsKey("provider") ? block.Tags["provider"] : null; if (provider == null) { return(APIResultCodes.InvalidName); } if (block.Tags.Count > 3) { return(APIResultCodes.InvalidBlockTags); } var dc = new DexClient(LyraNodeConfig.GetNetworkId()); var asts = await dc.GetSupportedExtTokenAsync(LyraNodeConfig.GetNetworkId()); var ast = asts.Asserts.Where(a => a.Symbol == symbol && a.NetworkProvider == provider) .FirstOrDefault(); if (ast == null) { return(APIResultCodes.InvalidExternalToken); } return(APIResultCodes.Success); }
public override async Task <TransactionBlock> ExtraOpsAsync(DagSystem sys, string hash) { var blocks = await sys.Storage.FindBlocksByRelatedTxAsync(hash); if (!blocks.Any(a => a is TokenWithdrawBlock)) { throw new Exception($"TokenWithdrawBlock not found."); } var burnblock = blocks.Where(a => a is TokenWithdrawBlock).First(); var burnbrk = burnblock as IBrokerAccount; var burn = burnblock as TokenWithdrawBlock; var dc = new DexClient(LyraNodeConfig.GetNetworkId()); var ret = await dc.RequestWithdrawAsync(burnbrk.OwnerAccountId, burn.ExtSymbol, burn.ExtProvider, burn.AccountID, hash, burn.WithdrawToExtAddress, burn.BurnAmount, NodeService.Dag.PosWallet.AccountId, Signatures.GetSignature(NodeService.Dag.PosWallet.PrivateKey, hash, NodeService.Dag.PosWallet.AccountId)); if (!ret.Success) { throw new Exception($"Error RequestWithdrawAsync to DEX Server: {ret.Message}"); } return(null); }
protected override async Task <APIResultCodes> AuthorizeImplAsync <T>(DagSystem sys, T tblock) { if (!(tblock is DexWalletGenesis)) { return(APIResultCodes.InvalidBlockType); } var block = tblock as DexWalletGenesis; if (block.AccountType != AccountTypes.DEX) { return(APIResultCodes.InvalidAccountType); } var dc = new DexClient(LyraNodeConfig.GetNetworkId()); var asts = await dc.GetSupportedExtTokenAsync(LyraNodeConfig.GetNetworkId()); var ast = asts.Asserts.Where(a => a.Symbol == block.ExtSymbol) .FirstOrDefault(); if (ast == null || ast.NetworkProvider != block.ExtProvider) { return(APIResultCodes.UnsupportedDexToken); } return(await Lyra.Shared.StopWatcher.TrackAsync(() => base.AuthorizeImplAsync(sys, tblock), "DexWalletGenesisAuthorizer->DexReceiveAuthorizer")); }
public override async Task <APIResultCodes> PreSendAuthAsync(DagSystem sys, SendTransferBlock block, TransactionBlock last) { var dexid = block.Tags.ContainsKey("dexid") ? block.Tags["dexid"] : null; if (dexid == null) { return(APIResultCodes.InvalidAccountId); } decimal mintamount = 0; var mintamountstr = block.Tags.ContainsKey("amount") ? block.Tags["amount"] : null; if (mintamountstr == null || !decimal.TryParse(mintamountstr, out mintamount) || mintamount <= 0) { return(APIResultCodes.InvalidAmount); } // verify if sender is dex server if (block.AccountID != LyraGlobal.GetDexServerAccountID(LyraNodeConfig.GetNetworkId())) { return(APIResultCodes.InvalidDexServer); } // verify dex wallet owner var brkr = await sys.Storage.FindLatestBlockAsync(dexid) as IBrokerAccount; if (brkr == null) { return(APIResultCodes.InvalidBrokerAcount); } return(APIResultCodes.Success); }
public override async Task <TransactionBlock> BrokerOpsAsync(DagSystem sys, SendTransferBlock send) { var blocks = await sys.Storage.FindBlocksByRelatedTxAsync(send.Hash); var pgen = blocks.FirstOrDefault(a => a is StakingGenesis); if (pgen != null) { return(null); } var sb = await sys.Storage.GetLastServiceBlockAsync(); // create a semi random account for pool. // it can be verified by other nodes. var keyStr = $"{send.Hash.Substring(0, 16)},{send.Tags["voting"]},{send.AccountID}"; var AccountId = Base58Encoding.EncodeAccountId(Encoding.ASCII.GetBytes(keyStr).Take(64).ToArray()); var start = DateTime.UtcNow.AddDays(1); if (LyraNodeConfig.GetNetworkId() == "devnet") { start = DateTime.UtcNow; // for debug } var stkGenesis = new StakingGenesis { Height = 1, Name = send.Tags["name"], OwnerAccountId = send.AccountID, AccountType = AccountTypes.Staking, AccountID = AccountId, // in fact we not use this account. Balances = new Dictionary <string, long>(), PreviousHash = sb.Hash, ServiceHash = sb.Hash, Fee = 0, FeeCode = LyraGlobal.OFFICIALTICKERCODE, FeeType = AuthorizationFeeTypes.NoFee, // pool specified config Voting = send.Tags["voting"], RelatedTx = send.Hash, Days = int.Parse(send.Tags["days"]), Start = start, CompoundMode = send.Tags["compound"] == "True" }; stkGenesis.Balances.Add(LyraGlobal.OFFICIALTICKERCODE, 0); stkGenesis.AddTag(Block.MANAGEDTAG, ""); // value is always ignored // pool blocks are service block so all service block signed by leader node stkGenesis.InitializeBlock(null, NodeService.Dag.PosWallet.PrivateKey, AccountId: NodeService.Dag.PosWallet.AccountId); return(stkGenesis); }
public TestAuthorizer(TestProbe testProbe) { //Console.WriteLine("initialize DagSystem"); Environment.SetEnvironmentVariable("LYRA_NETWORK", "xtest"); LyraNodeConfig.Init("xtest"); fakeP2P = testProbe; mockStore = new Mock <IAccountCollectionAsync>(); var keypair = Signatures.GenerateWallet(); posWallet = UT_Wallet.Restore(keypair.privateKey); var store = new MongoAccountCollection("mongodb://127.0.0.1/xunit", "xunit"); TheDagSystem = new DagSystem(null, store, null, posWallet, fakeP2P); }
protected override async Task <APIResultCodes> AuthorizeImplAsync <T>(DagSystem sys, T tblock) { if (!(tblock is TokenMintBlock)) { return(APIResultCodes.InvalidBlockType); } var block = tblock as TokenMintBlock; if (string.IsNullOrWhiteSpace(block.MintBy)) { return(APIResultCodes.InvalidTokenMint); } if (string.IsNullOrWhiteSpace(block.GenesisHash)) { return(APIResultCodes.InvalidTokenMint); } if (block.MintAmount <= 0) { return(APIResultCodes.InvalidTokenMint); } if (block.MintBy != LyraGlobal.GetDexServerAccountID(LyraNodeConfig.GetNetworkId())) { return(APIResultCodes.InvalidDexServer); } // IDexWallet interface var brkauth = new DexWalletAuthorizer(); var brkret = await brkauth.AuthorizeAsync(sys, tblock); if (brkret == APIResultCodes.Success) { return(await Lyra.Shared.StopWatcher.TrackAsync(() => base.AuthorizeImplAsync(sys, tblock), "DexTokenMintAuthorizer->TransactionAuthorizer")); } else { return(brkret); } }
public override async Task <TransactionBlock> BrokerOpsAsync(DagSystem sys, SendTransferBlock send) { var symbol = send.Tags["symbol"]; var provider = send.Tags["provider"]; // request a wallet from dex server var dc = new DexClient(LyraNodeConfig.GetNetworkId()); var r1 = await dc.CreateWalletAsync(send.AccountID, symbol, provider, send.Hash, NodeService.Dag.PosWallet.AccountId, Signatures.GetSignature(NodeService.Dag.PosWallet.PrivateKey, send.Hash, NodeService.Dag.PosWallet.AccountId) ); if (!r1.Success) { throw new Exception("DEX Server failed: " + r1.Message); } var extw = r1 as DexAddress; //Assert.IsTrue(extw.Address.StartsWith('T')); var keyStr = $"{send.Hash.Substring(0, 16)},{symbol},{provider},{send.AccountID}"; var AccountId = Base58Encoding.EncodeAccountId(Encoding.ASCII.GetBytes(keyStr).Take(64).ToArray()); var exists = await sys.Storage.FindDexWalletAsync(send.AccountID, symbol, provider); if (exists != null) { return(null); } var sb = await sys.Storage.GetLastServiceBlockAsync(); var wgen = new DexWalletGenesis { Height = 1, ServiceHash = sb.Hash, Fee = 0, FeeCode = LyraGlobal.OFFICIALTICKERCODE, FeeType = AuthorizationFeeTypes.NoFee, // transaction AccountID = AccountId, // in fact we not use this account. Balances = new Dictionary <string, long>(), // broker Name = symbol + (String.IsNullOrEmpty(provider) ? "" : $" via {provider}"), OwnerAccountId = send.AccountID, RelatedTx = send.Hash, // Dex wallet IntSymbol = $"${symbol}", ExtSymbol = symbol, ExtProvider = provider, ExtAddress = extw.Address, // genesis AccountType = AccountTypes.DEX }; wgen.AddTag(Block.MANAGEDTAG, ""); // value is always ignored wgen.InitializeBlock(null, NodeService.Dag.PosWallet.PrivateKey, AccountId: NodeService.Dag.PosWallet.AccountId); return(wgen); }
public static async Task <TransactionBlock> CNOAddStakingImplAsync(DagSystem sys, SendTransferBlock send, string relatedTx) { var block = await sys.Storage.FindBlockBySourceHashAsync(send.Hash); if (block != null) { return(null); } var sb = await sys.Storage.GetLastServiceBlockAsync(); var sendPrev = await sys.Storage.FindBlockByHashAsync(send.PreviousHash) as TransactionBlock; var lastBlock = await sys.Storage.FindLatestBlockAsync(send.DestinationAccountId); var lastStk = lastBlock as TransactionBlock; DateTime start; if (send is BenefitingBlock bnb) { start = (lastStk as IStaking).Start; } else { start = send.TimeStamp.AddDays(1); // manual add staking, start after 1 day. if (LyraNodeConfig.GetNetworkId() == "devnet") { start = send.TimeStamp; // for debug } } var stkNext = new StakingBlock { Height = lastStk.Height + 1, Name = ((IBrokerAccount)lastStk).Name, OwnerAccountId = ((IBrokerAccount)lastStk).OwnerAccountId, //AccountType = ((IOpeningBlock)lastStk).AccountType, AccountID = lastStk.AccountID, Balances = new Dictionary <string, long>(), PreviousHash = lastStk.Hash, ServiceHash = sb.Hash, Fee = 0, FeeCode = LyraGlobal.OFFICIALTICKERCODE, FeeType = AuthorizationFeeTypes.NoFee, SourceHash = send.Hash, // pool specified config Days = (lastBlock as IStaking).Days, Voting = ((IStaking)lastStk).Voting, RelatedTx = relatedTx, Start = start, CompoundMode = ((IStaking)lastStk).CompoundMode }; var chgs = send.GetBalanceChanges(sendPrev); stkNext.Balances.Add(LyraGlobal.OFFICIALTICKERCODE, lastStk.Balances[LyraGlobal.OFFICIALTICKERCODE] + chgs.Changes[LyraGlobal.OFFICIALTICKERCODE].ToBalanceLong()); stkNext.AddTag(Block.MANAGEDTAG, ""); // value is always ignored // pool blocks are service block so all service block signed by leader node stkNext.InitializeBlock(lastStk, NodeService.Dag.PosWallet.PrivateKey, AccountId: NodeService.Dag.PosWallet.AccountId); return(stkNext); }
// This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { // lyra network ID must be set early var networkId = Environment.GetEnvironmentVariable($"{LyraGlobal.OFFICIALDOMAIN.ToUpper()}_NETWORK"); if (networkId == null) { networkId = "devnet"; // for dev convenient } LyraNodeConfig.Init(networkId); // the apis services.AddScoped <INodeAPI, NodeAPI>(); services.AddScoped <INodeTransactionAPI, ApiService>(); services.AddSingleton <IHostEnv, HostEnvService>(); services.AddSingleton <IAccountCollectionAsync, MongoAccountCollection>(); services.AddMvc(); services.AddControllers(); services.AddSignalR(); // workflow need this BsonClassMap.RegisterClassMap <LyraContext>(cm => { cm.AutoMap(); cm.SetIsRootClass(false); }); //services.AddGrpc(); services.AddWorkflow(cfg => { // lyra network ID must be set early var networkId = Environment.GetEnvironmentVariable($"{LyraGlobal.OFFICIALDOMAIN.ToUpper()}_NETWORK"); if (networkId == null) { networkId = "devnet"; // for dev convenient } cfg.UseMongoDB(Neo.Settings.Default.LyraNode.Lyra.Database.DBConnect, "Workflow_" + networkId); //cfg.UseSqlite($"Data Source={fn};", true); cfg.UsePollInterval(new TimeSpan(0, 0, 0, 2)); //cfg.UseElasticsearch(new ConnectionSettings(new Uri("http://elastic:9200")), "workflows"); }); services.AddTransient <Repeator>(); services.AddTransient <ReqViewChange>(); services.AddTransient <CustomMessage>(); services.AddApiVersioning(options => { options.DefaultApiVersion = new ApiVersion(1, 0); options.AssumeDefaultVersionWhenUnspecified = true; options.ReportApiVersions = true; options.ApiVersionReader = new UrlSegmentApiVersionReader(); }); services.AddVersionedApiExplorer(options => { // add the versioned api explorer, which also adds IApiVersionDescriptionProvider service // note: the specified format code will format the version as "'v'major[.minor][-status]" options.GroupNameFormat = "'v'VVV"; // note: this option is only necessary when versioning by url segment. the SubstitutionFormat // can also be used to control the format of the API version in route templates options.SubstituteApiVersionInUrl = true; }); //services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>(); //services.AddSwaggerGen(options => options.OperationFilter<SwaggerDefaultValues>()); // Register the Swagger generator, defining 1 or more Swagger documents services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = $"{LyraGlobal.PRODUCTNAME} API", Version = "v1" }); }); }
protected override async Task <APIResultCodes> AuthorizeImplAsync <T>(DagSystem sys, T tblock) { if (!(tblock is TokenGenesisBlock)) { return(APIResultCodes.InvalidBlockType); } var block = tblock as TokenGenesisBlock; // Local node validations - before it sends it out to the authorization sample: // 1. check if the account already exists if (!(tblock is LyraTokenGenesisBlock)) { if (!await sys.Storage.AccountExistsAsync(block.AccountID)) { return(APIResultCodes.AccountDoesNotExist); // } TransactionBlock lastBlock = await sys.Storage.FindLatestBlockAsync(block.AccountID) as TransactionBlock; if (lastBlock == null) { return(APIResultCodes.CouldNotFindLatestBlock); } // check LYR balance if (lastBlock.Balances[LyraGlobal.OFFICIALTICKERCODE] != block.Balances[LyraGlobal.OFFICIALTICKERCODE] + block.Fee.ToBalanceLong()) { return(APIResultCodes.InvalidNewAccountBalance); } // check ticker name // https://www.quora.com/What-characters-are-not-allowed-in-a-domain-name var r = new Regex(@"[^\w-]|^-|-$"); if (!block.Ticker.Contains(block.DomainName + "/") || r.IsMatch(block.DomainName)) { return(APIResultCodes.InvalidDomainName); } if (r.IsMatch(block.Ticker.Replace(block.DomainName + "/", ""))) { return(APIResultCodes.InvalidTickerName); } if (block.RenewalDate > DateTime.UtcNow.Add(TimeSpan.FromDays(3660)) || block.RenewalDate < DateTime.UtcNow) { return(APIResultCodes.InvalidTokenRenewalDate); } if (string.IsNullOrWhiteSpace(block.DomainName)) { return(APIResultCodes.EmptyDomainName); } bool tokenIssuerIsSeed0 = block.AccountID == ProtocolSettings.Default.StandbyValidators[0]; if (!tokenIssuerIsSeed0 && block.AccountID != LyraGlobal.GetDexServerAccountID(LyraNodeConfig.GetNetworkId())) { if (block.DomainName.Length < 6) { return(APIResultCodes.DomainNameTooShort); } if (_reservedDomains.Any(a => a.Equals(block.DomainName, StringComparison.InvariantCultureIgnoreCase))) { return(APIResultCodes.DomainNameReserved); } } } if (await sys.Storage.WasAccountImportedAsync(block.AccountID)) { return(APIResultCodes.CannotModifyImportedAccount); } var tresult = await VerifyTransactionBlockAsync(sys, block); if (tresult != APIResultCodes.Success) { return(tresult); } // check length if (string.IsNullOrWhiteSpace(block.DomainName) || string.IsNullOrWhiteSpace(block.Ticker)) { return(APIResultCodes.InvalidTickerName); } if (block.DomainName.Length >= 64 || block.Ticker.Length >= 64) { return(APIResultCodes.InvalidTickerName); } // check if this token already exists //AccountData genesis_blocks = _accountCollection.GetAccount(AccountCollection.GENESIS_BLOCKS); //if (genesis_blocks.FindTokenGenesisBlock(testTokenGenesisBlock) != null) if (await sys.Storage.FindTokenGenesisBlockAsync(block.Hash, block.Ticker) != null) { return(APIResultCodes.TokenGenesisBlockAlreadyExists); } if (block.Fee != (await sys.Storage.GetLastServiceBlockAsync()).TokenGenerationFee) { return(APIResultCodes.InvalidFeeAmount); } if (block.NonFungibleType == NonFungibleTokenTypes.Collectible && !block.IsNonFungible) { return(APIResultCodes.InvalidNFT); } if (block.IsNonFungible) { if (!Signatures.ValidateAccountId(block.NonFungibleKey)) { return(APIResultCodes.InvalidNonFungiblePublicKey); } // Validate Collectible NFT if (block.ContractType == ContractTypes.Collectible) { if (block.Precision != 0) { return(APIResultCodes.InvalidNFT); } if (block.NonFungibleType != NonFungibleTokenTypes.Collectible) { return(APIResultCodes.InvalidNFT); } } } return(await Lyra.Shared.StopWatcher.TrackAsync(() => base.AuthorizeImplAsync(sys, tblock), "TokenGenesisAuthorizer->ReceiveTransferAuthorizer")); }
protected override async Task ExecuteAsync(CancellationToken stoppingToken) { _waitOrder = new AutoResetEvent(false); try { var networkId = LyraNodeConfig.GetNetworkId(); _log.LogInformation($"{LyraGlobal.PRODUCTNAME} {LyraGlobal.NODE_VERSION} Mode: {Neo.Settings.Default.LyraNode.Lyra.Mode}: Starting node daemon for {networkId}."); // something must be initialized first Wallet PosWallet; string lyrawalletfolder = Wallet.GetFullFolderName(networkId, "wallets"); if (!Directory.Exists(lyrawalletfolder)) { Directory.CreateDirectory(lyrawalletfolder); } var walletStore = new SecuredWalletStore(lyrawalletfolder); if (!walletStore.Exists(Neo.Settings.Default.LyraNode.Lyra.Wallet.Name)) { _log.LogInformation($"Creating wallet for {networkId}."); (var privateKey, var publicKey) = Signatures.GenerateWallet(); _log.LogInformation($"The new wallet {Neo.Settings.Default.LyraNode.Lyra.Wallet.Name} for {networkId} was created."); //Console.WriteLine($"Private Key: {privateKey}"); _log.LogInformation($"Account ID: {publicKey}"); walletStore.Create(Neo.Settings.Default.LyraNode.Lyra.Wallet.Name, Neo.Settings.Default.LyraNode.Lyra.Wallet.Password, networkId, privateKey, publicKey, ""); _log.LogInformation($"Wallet saved to: {lyrawalletfolder}{Neo.Settings.Default.LyraNode.Lyra.Wallet.Name}.lyrawallet"); } PosWallet = Wallet.Open(walletStore, Neo.Settings.Default.LyraNode.Lyra.Wallet.Name, Neo.Settings.Default.LyraNode.Lyra.Wallet.Password, LyraRestClient.Create(networkId, "", "NodeService", "1.0", LyraGlobal.SelectNode(networkId) + "Node/")); _log.LogInformation($"Staking wallet: {PosWallet.AccountId}"); PosWallet.SetVoteFor(PosWallet.AccountId); var blcokcount = await _store.GetBlockCountAsync(); if (blcokcount > 0 && networkId == "devnet") // not genesis { try { await PosWallet.SyncAsync(null); } catch { } } var localNode = DagSystem.ActorSystem.ActorOf(Neo.Network.P2P.LocalNode.Props()); Dag = new DagSystem(_hostEnv, _store, _lyraEventContext, PosWallet, localNode); _ = Task.Run(async() => await Dag.StartAsync()).ConfigureAwait(false); await Task.Delay(30000); } catch (Exception ex) { _log.LogCritical($"NodeService: Error Initialize! {ex}"); } while (!stoppingToken.IsCancellationRequested) { // do work if (_waitOrder.WaitOne(1000)) { _waitOrder.Reset(); } else { // no new order. do house keeping. } } }