Beispiel #1
0
        // 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);
        }
Beispiel #2
0
        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);
        }
Beispiel #3
0
        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"));
        }
Beispiel #4
0
        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);
        }
Beispiel #5
0
        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);
        }
Beispiel #6
0
        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);
        }
Beispiel #7
0
        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);
            }
        }
Beispiel #8
0
        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);
        }
Beispiel #9
0
        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);
        }
Beispiel #10
0
        // 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"));
        }
Beispiel #12
0
        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.
                }
            }
        }