Пример #1
0
        public DataChain(int chainId, uint chainIndex, Node.Node node, CoreChain coreChain, ServiceChain serviceChain, ChainKeyStore keyStore, int attachementKey) : base(ChainType.Data, chainId, chainIndex, node)
        {
            _coreChain    = coreChain;
            _serviceChain = serviceChain;
            Attachements  = node.AttachementManager;

            ChainKeyIndex = keyStore.KeyIndex;
            ChainKey      = keyStore.DecryptedKey;
            KeyStore      = keyStore;

            AttachementKey = attachementKey;

            var endPoints = new HashSet <AvailableEndPoint>();

            var chainInfo = _coreChain.GetChainInfo(ChainId);

            if (chainInfo != null)
            {
                var cep = chainInfo.GetPublicEndpoints();
                foreach (var ep in cep)
                {
                    var e = new Uri(ep);
                    if (IsValidAvailableEndPoint(e))
                    {
                        endPoints.Add(new AvailableEndPoint(e));
                    }
                }
            }

            AvailableEndpoints = new List <AvailableEndPoint>(endPoints);
        }
Пример #2
0
        public ServiceChain(int chainId, Node.Node node, CoreChain coreChain, ChainKeyStore keyStore) : base(ChainType.Service, chainId, 0, node)
        {
            CoreChain = coreChain;

            var endPoints = new HashSet <AvailableEndPoint>();

            _chainKeyIndex = keyStore.KeyIndex;
            _chainKey      = keyStore.DecryptedKey;
            KeyStore       = keyStore;

            var chainInfo = CoreChain.GetChainInfo(ChainId);

            if (chainInfo != null)
            {
                var cep = chainInfo.GetPublicEndpoints();
                foreach (var ep in cep)
                {
                    var e = new Uri(ep);
                    if (IsValidAvailableEndPoint(e))
                    {
                        endPoints.Add(new AvailableEndPoint(e));
                    }
                }
            }

            AvailableEndpoints = new List <AvailableEndPoint>(endPoints);
        }
Пример #3
0
        public async Task <bool> SetChainAccount(ChainKeyStore account, string password)
        {
            if (await UnlockAccount(account, password, KeyStoreTypes.Chain))
            {
                CurrentChainAccount = account;
            }

            return(account == null || CurrentCoreAccount != null);
        }
Пример #4
0
 public GenesisBlockResult(CoreBlock block, BlockSignatures signature, Key networkPublicKey, ChainKeyStore voteKey, string votePassword, Dictionary <int, List <ServiceTransaction> > serviceTransactions)
 {
     Block               = block;
     Signature           = signature;
     NetworkPublicKey    = networkPublicKey;
     NetworkVoteKey      = voteKey;
     NetworkVotePassword = votePassword;
     ServiceTransactions = serviceTransactions;
 }
Пример #5
0
        public async Task <KeyStore> StoreAccount(string name, PublicChainKey publicChainKey, Key key, string password)
        {
            return(await Task.Run(async() =>
            {
                var keyStore = new ChainKeyStore(name, publicChainKey, key, password);

                await StoreAccount(keyStore);
                await keyStore.DecryptKeyAsync(password, false);
                return keyStore;
            }));
        }
Пример #6
0
 public ChainKeyInfo(ChainKeyStore chainKeyStore, int attachementKey)
 {
     ChainKeyStore  = chainKeyStore;
     AttachementKey = attachementKey;
 }
Пример #7
0
        public static async Task UpdateChain(ChainKeyStore chainKey, string password, string chainName, string chainWebsite, WalletClient.ChainKey[] chainKeys, Uri[] endPoints, PurchaseInfo[] purchases, short[] revokedChainKeys, Uri[] revokedEndPoints, int[] revokedPurchases)
        {
            if (CurrentCoreAccount == null)
            {
                return;
            }


            HeleusClientResponse result    = null;
            ChainInfo            chainInfo = null;

            if (_busy)
            {
                result = new HeleusClientResponse(HeleusClientResultTypes.Busy);
                goto end;
            }
            _busy = true;

            try
            {
                await chainKey.DecryptKeyAsync(password, true);

                await Client.SetChainAccount(chainKey, password);
            }
            catch
            {
                result = new HeleusClientResponse(HeleusClientResultTypes.PasswordError);
                goto end;
            }

            try
            {
                var transaction = Client.NewChainUpdateTransaction(true);
                if (chainKeys != null)
                {
                    foreach (var ck in chainKeys)
                    {
                        transaction.AddChainKey(ck.SignedPublicKey);
                    }
                }
                if (endPoints != null)
                {
                    foreach (var ep in endPoints)
                    {
                        transaction.AddPublicEndpoint(ep.AbsoluteUri);
                    }
                }
                if (purchases != null)
                {
                    foreach (var p in purchases)
                    {
                        transaction.AddPurchase(p);
                    }
                }

                if (revokedChainKeys != null)
                {
                    foreach (var key in revokedChainKeys)
                    {
                        transaction.RevokeChainKey(key);
                    }
                }

                if (revokedEndPoints != null)
                {
                    foreach (var ep in revokedEndPoints)
                    {
                        transaction.RemovePublicEndPoint(ep.AbsoluteUri);
                    }
                }

                if (revokedPurchases != null)
                {
                    foreach (var p in revokedPurchases)
                    {
                        transaction.RemovePurchase(p);
                    }
                }

                transaction.UpdateChainName(chainName);
                transaction.UpdateChainWebsite(chainWebsite);

                if (!await Client.SetTargetChain(Protocol.CoreChainId))
                {
                    result = new HeleusClientResponse(HeleusClientResultTypes.EndpointConnectionError);
                    goto end;
                }

                result = await Client.UpdateChain(transaction, chainKeys);

                if (result.TransactionResult == TransactionResultTypes.Ok)
                {
                    chainInfo = (await Client.DownloadChainInfo((result.Transaction as ChainInfoOperation).ChainId)).Data;
                }
            }
            catch (Exception ex)
            {
                Log.IgnoreException(ex);
            }

end:
            await UIApp.PubSub.PublishAsync(new ChainRegistrationEvent(result, chainInfo));

            if (result.ResultType != HeleusClientResultTypes.Busy)
            {
                _busy = false;
            }
        }
Пример #8
0
        public NodeConfiguration(NodeConfig nodeConfig, CoreKeyConfig coreKeyConfig, ChainConfig chainConfig)
        {
            try
            {
                if (!string.IsNullOrEmpty(nodeConfig.NodePrivateKey))
                {
                    LocaleNodePrivateKey = Key.Restore(nodeConfig.NodePrivateKey);

                    if (LocaleNodePrivateKey.KeyType != Protocol.MessageKeyType)
                    {
                        Log.Warn("Stored node key has the wrong key type.");
                        LocaleNodePrivateKey = null;
                    }
                }
            }
            catch (Exception ex)
            {
                Log.Error("Stored node key is invalid.");
                Log.IgnoreException(ex);
            }

            if (LocaleNodePrivateKey == null)
            {
                Log.Trace("Generating new node key.");
                LocaleNodePrivateKey      = Key.Generate(Protocol.MessageKeyType);
                nodeConfig.NodePrivateKey = LocaleNodePrivateKey.HexString;
                Config.Save(nodeConfig);
            }

            var beacons = new List <Uri>();

            foreach (var beacon in nodeConfig.BeaconNodes)
            {
                if (!beacon.IsNullOrEmpty())
                {
                    beacons.Add(new Uri(beacon));
                }
            }
            BeaconNodes = beacons;

            var trusted = new List <Hash>();

            foreach (var nodeId in nodeConfig.TrustedNodeIds)
            {
                trusted.Add(Hash.Restore(nodeId));
            }
            TrustedNodeIds = trusted;

            var autoConnect = new List <Uri>();

            foreach (var endPoint in nodeConfig.AutoConnectNodes)
            {
                autoConnect.Add(new Uri(endPoint));
            }
            AutoConnectNodes = autoConnect;

            var keyCollector = new NodeInfoKeyCollector();

            if (coreKeyConfig != null)
            {
                try
                {
                    var keyStore = KeyStore.Restore <ChainKeyStore>(coreKeyConfig.Key);
                    keyStore.DecryptKeyAsync(coreKeyConfig.Password, true).Wait();

                    CoreKey = keyStore;

                    var keyFlags = keyStore.Flags;
                    keyCollector.AddNodeInfoKey(Protocol.CoreChainId, 0, keyStore.KeyIndex, keyFlags, keyStore.DecryptedKey);
                }
                catch (Exception ex)
                {
                    Log.Error("Core vote key or password invalid.");
                    Log.IgnoreException(ex);
                }
            }

            var chains = new List <ChainConfiguration>();

            if (chainConfig != null && chainConfig.Chains.Count > 0)
            {
                try
                {
                    foreach (var sc in chainConfig.Chains)
                    {
                        var chainKeys = new List <ChainConfiguration.ChainKeyInfo>();
                        foreach (var sk in sc.ChainKeys)
                        {
                            var chainKey = KeyStore.Restore <ChainKeyStore>(sk.ChainKey);
                            try
                            {
                                chainKey.DecryptKeyAsync(sk.ChainKeyPassword, true).Wait();
                            }
                            catch (Exception ex)
                            {
                                Log.Error($"Invalid chain key {chainKey.ChainId}/{chainKey.ChainIndex} password ({sc.Service})");
                                throw ex;
                            }

                            chainKeys.Add(new ChainConfiguration.ChainKeyInfo(chainKey, sk.AttachementKey));

                            var chainId = chainKey.ChainId;
                            if (chainKey.ChainId > Protocol.CoreChainId)
                            {
                                var keyFlags = chainKey.Flags;
                                keyCollector.AddNodeInfoKey(chainKey.ChainId, chainKey.ChainIndex, chainKey.KeyIndex, keyFlags, chainKey.DecryptedKey);
                            }
                        }

                        chains.Add(new ChainConfiguration(chainKeys, sc.Service, sc.ServiceSearchPath, sc.ServiceConfig));
                    }
                }
                catch (Exception ex)
                {
                    Log.Error($"Chain config key or password is invalid.");
                    Log.IgnoreException(ex);
                }
            }

            ChainConfigurations = chains;

            EnableClientConnections = nodeConfig.Node.EnableClientConnections;
            if (!nodeConfig.NetworkPublicKey.IsNullOrWhiteSpace())
            {
                NetworkPublicKey = Key.Restore(nodeConfig.NetworkPublicKey);
                Uri publicEndPoint = null;
                if (nodeConfig.Node.IsPublic && !string.IsNullOrWhiteSpace(nodeConfig.Node.PublicEndpoint))
                {
                    publicEndPoint = new Uri(nodeConfig.Node.PublicEndpoint);
                }

                LocaleNodeInfo = new NodeInfo(NetworkPublicKey, LocaleNodePrivateKey, publicEndPoint, keyCollector);
            }
        }
Пример #9
0
        public static GenesisBlockResult Generate(Base.Storage storage)
        {
            (var networkKeyStore, var networkKey, _) = LoadCoreAccountKey(storage, "Network Account", "Heleus Core");

            if (networkKeyStore == null)
            {
                networkKey = Key.Generate(KeyTypes.Ed25519);
                var networkPassword = Hex.ToString(Rand.NextSeed(32));
                networkKeyStore = new CoreAccountKeyStore("Heleus Core Network Account", CoreAccount.NetworkAccountId, networkKey, networkPassword);

                SaveCoreAccountKey(storage, networkKeyStore, "Network Account", networkPassword, "Heleus Core");
            }

            Console.WriteLine($"Heleus Core Network Account Key: {networkKey.PublicKey.HexString}.");

            var networkKeys      = new List <NetworkKey>();
            var networkChainKeys = new List <PublicChainKey>();

            for (var i = 0; i < 3; i++)
            {
                var keyFlags = GetCoreKeyFlags(i);
                var keyName  = $"Network {GetKeyName(keyFlags, true)}";

                (var store, var key, var storePassword) = LoadKeyStore <ChainKeyStore>(storage, null, keyName, "Heleus Core");

                if (store == null)
                {
                    key           = Key.Generate(KeyTypes.Ed25519);
                    storePassword = Hex.ToString(Rand.NextSeed(32));

                    var publicChainKey = new PublicChainKey(keyFlags, CoreChain.CoreChainId, 0, 0, (short)i, key);
                    store = new ChainKeyStore($"Heleus Core {keyName}", publicChainKey, key, storePassword);

                    SaveKeyStore(storage, store, storePassword, null, keyName, "Heleus Core");
                }

                Console.WriteLine($"Heleus Core {keyName}: {key.PublicKey.HexString}.");

                networkKeys.Add(new NetworkKey {
                    Key = key, Password = storePassword, PublicKey = store.PublicChainKey, Store = store
                });
                networkChainKeys.Add(store.PublicChainKey);
            }

            var timestamp = Time.Timestamp;

            timestamp = Protocol.GenesisTime;

            var coreOperations = new List <CoreOperation>();

            var endPoints = new List <string>();

            Console.WriteLine("Type the core chain name (default: Heleus Core)");
            var name = Program.IsDebugging ? "Heleus Core" : Console.ReadLine();

            if (string.IsNullOrWhiteSpace(name))
            {
                name = "Heleus Core";
            }
            Console.WriteLine($"Chain name: {name}");

            Console.WriteLine("Type the core chain website (default: https://heleuscore.com)");
            var website = Program.IsDebugging ? "https://heleuscore.com" : Console.ReadLine();

            if (string.IsNullOrWhiteSpace(website))
            {
                website = "https://heleuscore.com";
            }
            Console.WriteLine($"Chain Website: {website}");

            Console.WriteLine("Type the core chain endpoints. (none for none, default: https://heleusnode.heleuscore.com)");
            while (true)
            {
                if (Program.IsDebugging)
                {
                    var ips = GetLocalIPV4Addresss();
                    foreach (var ip in ips)
                    {
                        endPoints.Add($"http://{ip}:54321/");
                    }
                    break;
                }

                var ep = Console.ReadLine();
                if (string.IsNullOrWhiteSpace(ep))
                {
                    if (endPoints.Count == 0)
                    {
                        endPoints.Add("https://heleusnode.heleuscore.com");
                    }
                    break;
                }

                if (ep.IsValdiUrl(false))
                {
                    Console.WriteLine($"Added endpoint {ep}");
                    endPoints.Add(ep);
                }

                if (ep == "none")
                {
                    break;
                }
            }

            Console.WriteLine($"Chain Endpoints: {string.Join(", ", endPoints)}");

            var useGenesisInfo       = false;
            var useCoreChainEndpoint = false;

            if (storage.FileExists("genesisservices.txt"))
            {
                Console.WriteLine("Process genesisservices.txt [yes/no] (default: yes)");

                var p = Program.IsDebugging ? "yes" : Console.ReadLine();
                if (string.IsNullOrWhiteSpace(p) || p.ToLower() == "yes")
                {
                    Console.WriteLine("Processing genesisservices.txt");

                    useGenesisInfo = true;

                    if (endPoints.Count > 0)
                    {
                        Console.WriteLine("Use first core chain endpoint, not endpoint from genesisservices.txt [yes/no] (default: no)");

                        var p2 = Program.IsDebugging ? "yes" : Console.ReadLine()?.ToLower();

                        if (p2 == "yes")
                        {
                            useCoreChainEndpoint = true;
                            Console.WriteLine($"Using endpoint {endPoints[0]} for all services from genesisservices.txt");
                        }
                    }
                }
            }

            coreOperations.Add((ChainInfoOperation) new ChainInfoOperation(CoreChain.CoreChainId, CoreAccount.NetworkAccountId, name, website, timestamp, networkChainKeys, endPoints, new List <PurchaseInfo>()).UpdateOperationId(Operation.FirstTransactionId));
            coreOperations.Add((AccountOperation) new AccountOperation(CoreAccount.NetworkAccountId, networkKey.PublicKey, timestamp).UpdateOperationId(coreOperations.Count + 1)); // network account
            coreOperations.Add(((AccountUpdateOperation) new AccountUpdateOperation().UpdateOperationId(Operation.FirstTransactionId + 2)).AddAccount(CoreAccount.NetworkAccountId, coreOperations.Count + 1, long.MaxValue / 2).AddTransfer(CoreAccount.NetworkAccountId, CoreAccount.NetworkAccountId, long.MaxValue / 2, null, timestamp));

            var blockStateOperation = new BlockStateOperation();

            var nextChainId       = CoreChain.CoreChainId + 1;
            var nextTransactionId = coreOperations.Count + 1;
            var nextAccountId     = CoreAccount.NetworkAccountId + 1;

            var accounts = new List <AccountOperation>();
            var serviceTransactionsList = new Dictionary <int, List <ServiceTransaction> >();

            if (useGenesisInfo)
            {
                try
                {
                    var json = storage.ReadFileText("genesisservices.txt");
                    if (json != null)
                    {
                        var services = Newtonsoft.Json.JsonConvert.DeserializeObject <List <GenesisService> >(json);
                        if (services != null)
                        {
                            foreach (var service in services)
                            {
                                if (!service.Valid)
                                {
                                    continue;
                                }

                                var serviceKeys = new List <PublicChainKey>();
                                var ep          = useCoreChainEndpoint ? endPoints[0] : service.Endpoint;
                                var accountId   = service.AccountId;

                                if (accountId != CoreAccount.NetworkAccountId)
                                {
                                    var found = accounts.Find((a) => a.AccountId == accountId) != null;
                                    if (!found)
                                    {
                                        (var store, var key, var accountPassword) = LoadCoreAccountKey(storage, "Core Account " + accountId, "Core Accounts");
                                        if (store == null)
                                        {
                                            key             = Key.Generate(KeyTypes.Ed25519);
                                            accountPassword = Hex.ToString(Rand.NextSeed(32));
                                            store           = new CoreAccountKeyStore(service.AccountName, accountId, key, accountPassword);

                                            SaveCoreAccountKey(storage, store, "Core Account " + accountId, accountPassword, "Core Accounts");
                                        }

                                        if (accountId == nextAccountId)
                                        {
                                            var ao = new AccountOperation(accountId, key.PublicKey, timestamp);
                                            ao.UpdateOperationId(nextTransactionId);

                                            coreOperations.Add(ao);
                                            accounts.Add(ao);

                                            nextTransactionId++;
                                            nextAccountId++;
                                        }
                                        else
                                        {
                                            Console.WriteLine($"Invalid account id for {service.Title}, should be {nextAccountId}, but is {accountId}.");
                                            continue;
                                        }
                                    }
                                }

                                Console.WriteLine($"Adding Service {service.Title} with endpoint {ep}.");

                                var count = 2 + service.DataChainCount;
                                for (var i = 0; i < count; i++)
                                {
                                    var keyFlags  = GetServiceKeyFlags(i);
                                    var keyName   = $"{service.Name} {GetKeyName(keyFlags, false)}";
                                    var dataChain = i >= 2;
                                    if (dataChain)
                                    {
                                        keyName += $" (ChainIndex {i - 2})";
                                    }

                                    (var store, var key, var servicePassword) = LoadKeyStore <ChainKeyStore>(storage, null, keyName, service.Name);

                                    if (store == null)
                                    {
                                        key             = Key.Generate(KeyTypes.Ed25519);
                                        servicePassword = Hex.ToString(Rand.NextSeed(32));

                                        var signedKey = new PublicChainKey(keyFlags, nextChainId, dataChain ? (uint)i - 2 : 0, 0, (short)i, key);
                                        store = new ChainKeyStore($"{service.Name} {keyName}", signedKey, key, servicePassword);

                                        SaveKeyStore(storage, store, servicePassword, null, keyName, service.Name);
                                    }

                                    Console.WriteLine($"{service.Name} {keyName}: {key.PublicKey.HexString}.");

                                    serviceKeys.Add(store.PublicChainKey);
                                }

                                var pc = new ChainInfoOperation(nextChainId, accountId, service.Title, service.Website, timestamp, serviceKeys, new List <string> {
                                    ep
                                }, new List <PurchaseInfo>());
                                pc.UpdateOperationId(nextTransactionId);
                                coreOperations.Add(pc);

                                nextTransactionId++;

                                if (service.Revenue > 0)
                                {
                                    var rev = new ChainRevenueInfoOperation(nextChainId, service.Revenue, service.RevenueAccountFactor, timestamp);
                                    rev.UpdateOperationId(nextTransactionId);
                                    coreOperations.Add(rev);
                                    nextTransactionId++;
                                }

                                if (service.Accounts > 0)
                                {
                                    var serviceTransactions = new List <ServiceTransaction>();
                                    for (var i = 0; i < service.Accounts; i++)
                                    {
                                        (var accountStore, var accountKey, var accountPassword) = LoadCoreAccountKey(storage, "Core Account " + nextAccountId, "Core Accounts");
                                        if (accountStore == null)
                                        {
                                            accountKey      = Key.Generate(KeyTypes.Ed25519);
                                            accountPassword = Hex.ToString(Rand.NextSeed(32));
                                            accountStore    = new CoreAccountKeyStore($"Core Account {nextAccountId}", nextAccountId, accountKey, accountPassword);

                                            SaveCoreAccountKey(storage, accountStore, "Core Account " + nextAccountId, accountPassword, "Core Accounts");
                                        }

                                        (var serviceAccountStore, var serviceAccountKey, var serviceAccountPassword) = LoadKeyStore <ServiceAccountKeyStore>(storage, null, $"{service.Name} Service Account {nextAccountId}", $"{service.Name}/Service Accounts");
                                        if (serviceAccountStore == null)
                                        {
                                            serviceAccountKey      = Key.Generate(KeyTypes.Ed25519);
                                            serviceAccountPassword = Hex.ToString(Rand.NextSeed(32));

                                            var signedPublicKey = PublicServiceAccountKey.GenerateSignedPublicKey(nextAccountId, nextChainId, 0, 0, serviceAccountKey.PublicKey, accountKey);
                                            serviceAccountStore = new ServiceAccountKeyStore($"{service.Name} Service Account {nextAccountId}", signedPublicKey, serviceAccountKey, serviceAccountPassword);

                                            SaveKeyStore(storage, serviceAccountStore, serviceAccountPassword, null, $"{service.Name} Service Account {nextAccountId}", $"{service.Name}/Service Accounts");
                                        }

                                        var join = new JoinServiceTransaction(serviceAccountStore.SignedPublicKey)
                                        {
                                            SignKey = accountKey
                                        };
                                        join.ToArray();
                                        serviceTransactions.Add(join);

                                        var ao = new AccountOperation(nextAccountId, accountKey.PublicKey, timestamp);
                                        ao.UpdateOperationId(nextTransactionId);

                                        coreOperations.Add(ao);
                                        accounts.Add(ao);

                                        nextTransactionId++;
                                        nextAccountId++;
                                    }

                                    blockStateOperation.AddBlockState(nextChainId, Protocol.GenesisBlockId, 0, 0, serviceTransactions.Count);
                                    serviceTransactionsList[nextChainId] = serviceTransactions;
                                }

                                nextChainId++;
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    Log.HandleException(ex, LogLevels.Error);
                }
            }

            coreOperations.Add(blockStateOperation);
            blockStateOperation.UpdateOperationId(nextTransactionId);
            blockStateOperation.AddBlockState(Protocol.CoreChainId, Protocol.GenesisBlockId, Protocol.GenesisBlockNetworkKeyIssuer, 0, nextTransactionId);

            var block = new CoreBlock(Protocol.GenesisBlockId, Protocol.GenesisBlockNetworkKeyIssuer, 0, timestamp, nextAccountId, nextChainId, Hash.Empty(Protocol.TransactionHashType), Hash.Empty(ValidationOperation.ValidationHashType), coreOperations, new List <CoreTransaction>());

            var signatures = new BlockSignatures(block);

            signatures.AddSignature(block.Issuer, block, networkKey);

            return(new GenesisBlockResult(block, signatures, networkKey.PublicKey, networkKeys[1].Store, networkKeys[1].Password, serviceTransactionsList));
        }
Пример #10
0
        public ChainPage(ChainInfo chainInfo, ChainKeyStore chainKey) : base("ChainPage")
        {
            ChainInfo = chainInfo;
            _chainKey = chainKey;
            Subscribe <ChainRegistrationEvent>(Chain);

            if (chainInfo != null)
            {
                var endPoints = chainInfo.GetPublicEndpoints();
                foreach (var endPoint in endPoints)
                {
                    _endPoints.Add(new ChainItem <string>(ChainItemStatus.Live, endPoint));
                }
                foreach (var key in chainInfo.GetRevokeableChainKeys())
                {
                    _chainKeys.Add(new ChainKeyItem(key.IsRevoked ? ChainItemStatus.Revoked : ChainItemStatus.Live, key.Item));
                }
                foreach (var purchase in chainInfo.GetChainPurchases())
                {
                    _purchases.Add(new ChainItem <PurchaseInfo>(purchase.IsRevoked ? ChainItemStatus.Revoked : ChainItemStatus.Live, purchase.Item));
                }
            }

            AddTitleRow("Title");

            AddHeaderRow("Info");
            _name = AddEntryRow(null, "Name");
            _name.SetDetailViewIcon(Icons.Pencil);
            if (chainInfo != null)
            {
                _name.Edit.Text = chainInfo.Name;
            }
            _website = AddEntryRow(null, "Website");
            _website.SetDetailViewIcon(Icons.RowLink);
            if (chainInfo != null)
            {
                _website.Edit.Text = chainInfo.Website;
            }
            AddFooterRow();

            AddHeaderRow("ChainKeys");
            _chainKeysButton = AddButtonRow("ChainKeysButton", NewChainKey);
            Status.AddBusyView(_chainKeysButton);
            AddFooterRow();

            AddHeaderRow("EndPoints");
            _endPointsButton = AddButtonRow("EndPointsButton", NewEndPoint);
            Status.AddBusyView(_endPointsButton);
            AddFooterRow();

            /*
             * AddHeaderRow("Purchases");
             * _purchasesButton = AddButtonRow("PurchasesButton", NewPurchase);
             * Status.AddBusyView(_purchasesButton);
             * AddFooterRow();
             */

            Status.Add(_name.Edit, T("NameStatus"), (view, entry, newText, oldTex) =>
            {
                if (string.IsNullOrEmpty(newText))
                {
                    return(false);
                }

                return(true);
            }).
            Add(_website.Edit, T("WebsiteStatus"), (view, entry, newText, oldText) =>
            {
                if (string.IsNullOrEmpty(newText))
                {
                    return(true);
                }

                return(newText.IsValdiUrl(true));
            });

            _adminKeyStatus = Status.Add(T("AdminKeyStatus"), (sv) =>
            {
                foreach (var key in _chainKeys)
                {
                    if ((key.Status == ChainItemStatus.Live || key.Status == ChainItemStatus.New) && ((key.Item.Flags & PublicChainKeyFlags.ChainAdminKey) != 0))
                    {
                        return(true);
                    }
                }

                return(false);
            });

            _endPointStatus = Status.Add(T("EndPointStatus"), (sv) =>
            {
                return(_endPoints.Any((a) => a.Status == ChainItemStatus.New || a.Status == ChainItemStatus.Live));
            });

            AddIndex       = AddSubmitRow("Submit", Submit);
            AddIndexBefore = true;
            if (_chainKey != null)
            {
                _keyPassword = AddPasswordRow("", "KeyPassword");

                Status.Add(_keyPassword.Edit, T("PasswordStatus"), (sv, entry, newText, oldText) =>
                {
                    return(_chainKey.IsPasswordValid(newText));
                });
            }

            AddIndex       = null;
            AddIndexBefore = false;

            UpdateChainKeys();
            UpdateEndpoints();
            UpdatePurchases();
        }