static RpcServerSettings GetRpcServerSettings(ExpressChain chain, ExpressConsensusNode node) { var ipAddress = chain.TryReadSetting <IPAddress>("rpc.BindAddress", IPAddress.TryParse, out var bindAddress) ? bindAddress : IPAddress.Loopback; var settings = new Dictionary <string, string>() { { "PluginConfiguration:Network", $"{chain.Network}" }, { "PluginConfiguration:BindAddress", $"{ipAddress}" }, { "PluginConfiguration:Port", $"{node.RpcPort}" } }; if (chain.TryReadSetting <decimal>("rpc.MaxGasInvoke", decimal.TryParse, out var maxGasInvoke)) { settings.Add("PluginConfiguration:MaxGasInvoke", $"{maxGasInvoke}"); } if (chain.TryReadSetting <decimal>("rpc.MaxFee", decimal.TryParse, out var maxFee)) { settings.Add("PluginConfiguration:MaxFee", $"{maxFee}"); } if (chain.TryReadSetting <int>("rpc.MaxIteratorResultItems", int.TryParse, out var maxIteratorResultItems) && maxIteratorResultItems > 0) { settings.Add("PluginConfiguration:MaxIteratorResultItems", $"{maxIteratorResultItems}"); } var config = new ConfigurationBuilder().AddInMemoryCollection(settings).Build(); return(RpcServerSettings.Load(config.GetSection("PluginConfiguration"))); }
public static void SignOracleResponseTransaction(ProtocolSettings settings, ExpressChain chain, Transaction tx, IReadOnlyList <ECPoint> oracleNodes) { var signatures = new Dictionary <ECPoint, byte[]>(); for (int i = 0; i < chain.ConsensusNodes.Count; i++) { var account = chain.ConsensusNodes[i].Wallet.DefaultAccount ?? throw new Exception("Invalid DefaultAccount"); var key = DevWalletAccount.FromExpressWalletAccount(settings, account).GetKey() ?? throw new Exception("Invalid KeyPair"); if (oracleNodes.Contains(key.PublicKey)) { signatures.Add(key.PublicKey, tx.Sign(key, chain.Network)); } } int m = oracleNodes.Count - (oracleNodes.Count - 1) / 3; if (signatures.Count < m) { throw new Exception("Insufficient oracle response signatures"); } var contract = Contract.CreateMultiSigContract(m, oracleNodes); var sb = new ScriptBuilder(); foreach (var kvp in signatures.OrderBy(p => p.Key).Take(m)) { sb.EmitPush(kvp.Value); } var index = tx.GetScriptHashesForVerifying(null)[0] == contract.ScriptHash ? 0 : 1; tx.Witnesses[index].InvocationScript = sb.ToArray(); }
public static string ResolvePassword(this ExpressChain chain, string name, string password) { if (string.IsNullOrEmpty(name)) { throw new ArgumentException($"{nameof(name)} parameter can't be null or empty", nameof(name)); } // if the user specified a password, use it if (!string.IsNullOrEmpty(password)) { return(password); } // if the name is a valid Neo Express account name, no password is needed if (chain.IsReservedName(name)) { return(password); } if (chain.Wallets.Any(w => name.Equals(w.Name, StringComparison.OrdinalIgnoreCase))) { return(password); } // if a password is needed but not provided, prompt the user return(McMaster.Extensions.CommandLineUtils.Prompt.GetPassword($"enter password for {name}")); }
public OnlineNode(ProtocolSettings settings, ExpressChain chain, ExpressConsensusNode node) { this.ProtocolSettings = settings; this.chain = chain; rpcClient = new RpcClient(new Uri($"http://localhost:{node.RpcPort}"), protocolSettings: settings); consensusNodesKeys = new Lazy <KeyPair[]>(() => chain.GetConsensusNodeKeys()); }
public static ExpressWalletAccount?GetAccount(this ExpressChain chain, string name) { if (chain.Wallets != null) { var wallet = chain.Wallets.SingleOrDefault(w => w.NameEquals(name)); if (wallet != null) { return(wallet.DefaultAccount); } } var node = chain.ConsensusNodes.SingleOrDefault(n => n.Wallet.NameEquals(name)); if (node != null) { return(node.Wallet.DefaultAccount); } if ("genesis".Equals(name, StringComparison.InvariantCultureIgnoreCase)) { return(chain.ConsensusNodes .Select(n => n.Wallet.Accounts.Single(a => a.Label == "MultiSigContract")) .FirstOrDefault()); } return(null); }
public static IEnumerable <DevWalletAccount> GetMultiSigAccounts(this ExpressChain chain, ExpressWalletAccount account) => chain.ConsensusNodes .Select(n => n.Wallet) .Concat(chain.Wallets) .Select(w => w.Accounts.Find(a => a.ScriptHash == account.ScriptHash)) .Where(a => a != null) .Select(DevWalletAccount.FromExpressWalletAccount !);
public static (Wallet wallet, UInt160 accountHash) GetGenesisAccount(this ExpressChain chain, ProtocolSettings settings) { Debug.Assert(chain.ConsensusNodes != null && chain.ConsensusNodes.Count > 0); var wallet = DevWallet.FromExpressWallet(settings, chain.ConsensusNodes[0].Wallet); var account = wallet.GetMultiSigAccounts().Single(); return(wallet, account.ScriptHash); }
public static ExpressChain CreateBlockchain(int count) { var wallets = new List <(DevWallet wallet, Neo.Wallets.WalletAccount account)>(count); ushort GetPortNumber(int index, ushort portNumber) => (ushort)((49000 + (index * 1000)) + portNumber); try { for (var i = 1; i <= count; i++) { var wallet = new DevWallet($"node{i}"); var account = wallet.CreateAccount(); account.IsDefault = true; wallets.Add((wallet, account)); } var keys = wallets.Select(t => t.account.GetKey().PublicKey).ToArray(); var contract = Neo.SmartContract.Contract.CreateMultiSigContract((keys.Length * 2 / 3) + 1, keys); foreach (var(wallet, account) in wallets) { var multiSigContractAccount = wallet.CreateAccount(contract, account.GetKey()); multiSigContractAccount.Label = "MultiSigContract"; } // 49152 is the first port in the "Dynamic and/or Private" range as specified by IANA // http://www.iana.org/assignments/port-numbers var nodes = new List <ExpressConsensusNode>(count); for (var i = 0; i < count; i++) { nodes.Add(new ExpressConsensusNode() { TcpPort = GetPortNumber(i, 333), WebSocketPort = GetPortNumber(i, 334), RpcPort = GetPortNumber(i, 332), Wallet = wallets[i].wallet.ToExpressWallet() }); } return(new ExpressChain() { Magic = ExpressChain.GenerateMagicValue(), ConsensusNodes = nodes, }); } finally { foreach (var(wallet, _) in wallets) { wallet.Dispose(); } } }
static Neo.Consensus.Settings GetConsensusSettings(ExpressChain chain) { var settings = new Dictionary <string, string>() { { "PluginConfiguration:Network", $"{chain.Network}" } }; var config = new ConfigurationBuilder().AddInMemoryCollection(settings).Build(); return(new Neo.Consensus.Settings(config.GetSection("PluginConfiguration"))); }
public static void SaveChain(this IFileSystem fileSystem, ExpressChain chain, string path) { var serializer = new JsonSerializer(); using var stream = fileSystem.File.Open(path, System.IO.FileMode.Create, System.IO.FileAccess.Write); using var streamWriter = new System.IO.StreamWriter(stream); using var writer = new JsonTextWriter(streamWriter) { Formatting = Formatting.Indented }; serializer.Serialize(writer, chain); }
public OfflineNode(IExpressStore store, Wallet nodeWallet, ExpressChain chain, bool enableTrace) { this.nodeWallet = nodeWallet; this.chain = chain; this.store = store; applicationEngineProvider = enableTrace ? new ExpressApplicationEngineProvider() : null; storageProvider = new ExpressStorageProvider((IStore)store); _ = new ExpressAppLogsPlugin(store); neoSystem = new NeoSystem(storageProvider.Name); ApplicationEngine.Log += OnLog !; }
public static bool TryReadSetting <T>(this ExpressChain chain, string setting, TryParse <T> tryParse, [MaybeNullWhen(false)] out T value) { if (chain.Settings.TryGetValue(setting, out var stringValue) && tryParse(stringValue, out var result)) { value = result; return(true); } value = default; return(false); }
public static void Save(this ExpressChain chain, string fileName) { var serializer = new JsonSerializer(); using (var stream = File.Open(fileName, FileMode.Create, FileAccess.Write)) using (var writer = new JsonTextWriter(new StreamWriter(stream)) { Formatting = Formatting.Indented }) { serializer.Serialize(writer, chain); } }
public void empty_settings_save_correctly() { var fileSystem = new MockFileSystem(); var fileName = fileSystem.Path.Combine(fileSystem.AllDirectories.First(), FILENAME); var chain = new ExpressChain(); fileSystem.SaveChain(chain, fileName); using var json = JsonDocument.Parse(fileSystem.GetFile(fileName).Contents); var settings = json.RootElement.GetProperty("settings"); settings.EnumerateObject().Should().BeEmpty(); }
public ExpressChainManager(IFileSystem fileSystem, ExpressChain chain, uint?secondsPerBlock = null) { this.fileSystem = fileSystem; this.chain = chain; uint secondsPerBlockResult = secondsPerBlock.HasValue ? secondsPerBlock.Value : chain.TryReadSetting <uint>("chain.SecondsPerBlock", uint.TryParse, out var secondsPerBlockSetting) ? secondsPerBlockSetting : 0; this.ProtocolSettings = chain.GetProtocolSettings(secondsPerBlockResult); }
public static bool TryParseScriptHash(this ExpressChain chain, string name, [MaybeNullWhen(false)] out UInt160 hash) { try { hash = name.ToScriptHash(chain.AddressVersion); return(true); } catch { hash = null; return(false); } }
public OfflineNode(ProtocolSettings settings, RocksDbStorageProvider rocksDbStorageProvider, Wallet nodeWallet, ExpressChain chain, bool enableTrace) { this.nodeWallet = nodeWallet; this.chain = chain; this.rocksDbStorageProvider = rocksDbStorageProvider; applicationEngineProvider = enableTrace ? new ApplicationEngineProvider() : null; consensusNodesKeys = new Lazy <KeyPair[]>(() => chain.GetConsensusNodeKeys()); var storageProviderPlugin = new StorageProviderPlugin(rocksDbStorageProvider); _ = new PersistencePlugin(rocksDbStorageProvider); neoSystem = new NeoSystem(settings, storageProviderPlugin.Name); ApplicationEngine.Log += OnLog !; }
internal static ExpressChain CreateChain(int nodeCount) { if (nodeCount != 1 && nodeCount != 4 && nodeCount != 7) { throw new ArgumentException("invalid blockchain node count", nameof(nodeCount)); } var wallets = new List <(DevWallet wallet, Neo.Wallets.WalletAccount account)>(nodeCount); for (var i = 1; i <= nodeCount; i++) { var wallet = new DevWallet($"node{i}"); var account = wallet.CreateAccount(); account.IsDefault = true; wallets.Add((wallet, account)); } var keys = wallets.Select(t => t.account.GetKey().PublicKey).ToArray(); var contract = Neo.SmartContract.Contract.CreateMultiSigContract((keys.Length * 2 / 3) + 1, keys); foreach (var(wallet, account) in wallets) { var multiSigContractAccount = wallet.CreateAccount(contract, account.GetKey()); multiSigContractAccount.Label = "MultiSigContract"; } // 49152 is the first port in the "Dynamic and/or Private" range as specified by IANA // http://www.iana.org/assignments/port-numbers var nodes = new List <ExpressConsensusNode>(nodeCount); for (var i = 0; i < nodeCount; i++) { nodes.Add(new ExpressConsensusNode() { TcpPort = GetPortNumber(i, 3), WebSocketPort = GetPortNumber(i, 4), RpcPort = GetPortNumber(i, 2), Wallet = wallets[i].wallet.ToExpressWallet() }); } return(new ExpressChain() { Magic = ExpressChain.GenerateMagicValue(), ConsensusNodes = nodes, });
public static bool IsReservedName(this ExpressChain chain, string name) { if ("genesis".Equals(name, StringComparison.InvariantCultureIgnoreCase)) { return(true); } foreach (var node in chain.ConsensusNodes) { if (string.Equals(name, node.Wallet.Name, StringComparison.InvariantCultureIgnoreCase)) { return(true); } } return(false); }
public static bool IsReservedName(this ExpressChain chain, string name) { if (string.Equals(GENESIS, name, StringComparison.OrdinalIgnoreCase)) { return(true); } for (int i = 0; i < chain.ConsensusNodes.Count; i++) { if (string.Equals(chain.ConsensusNodes[i].Wallet.Name, name, StringComparison.OrdinalIgnoreCase)) { return(true); } } return(false); }
internal static ExpressChain CreateChain(int nodeCount, byte?addressVersion) { if (nodeCount != 1 && nodeCount != 4 && nodeCount != 7) { throw new ArgumentException("invalid blockchain node count", nameof(nodeCount)); } var settings = ProtocolSettings.Default with { Network = ExpressChain.GenerateNetworkValue(), AddressVersion = addressVersion ?? ProtocolSettings.Default.AddressVersion }; var wallets = new List <(DevWallet wallet, WalletAccount account)>(nodeCount); for (var i = 1; i <= nodeCount; i++) { var wallet = new DevWallet(settings, $"node{i}"); var account = wallet.CreateAccount(); account.IsDefault = true; wallets.Add((wallet, account)); } var keys = wallets.Select(t => t.account.GetKey().PublicKey).ToArray(); var contract = Contract.CreateMultiSigContract((keys.Length * 2 / 3) + 1, keys); foreach (var(wallet, account) in wallets) { var multiSigContractAccount = wallet.CreateAccount(contract, account.GetKey()); multiSigContractAccount.Label = "Consensus MultiSigContract"; } var nodes = wallets.Select((w, i) => new ExpressConsensusNode { TcpPort = GetPortNumber(i, 3), WebSocketPort = GetPortNumber(i, 4), RpcPort = GetPortNumber(i, 2), Wallet = w.wallet.ToExpressWallet() }); return(new ExpressChain() { Network = settings.Network, AddressVersion = settings.AddressVersion, ConsensusNodes = nodes.ToList(), });
public static bool TryGetAccountHash(this ExpressChain chain, string name, [MaybeNullWhen(false)] out UInt160 accountHash, ProtocolSettings?settings = null) { settings ??= chain.GetProtocolSettings(); if (chain.Wallets != null && chain.Wallets.Count > 0) { for (int i = 0; i < chain.Wallets.Count; i++) { if (string.Equals(name, chain.Wallets[i].Name, StringComparison.OrdinalIgnoreCase)) { var wallet = DevWallet.FromExpressWallet(settings, chain.Wallets[i]); var account = wallet.GetAccounts().Single(a => a.IsDefault); accountHash = account.ScriptHash; return(true); } } } Debug.Assert(chain.ConsensusNodes != null && chain.ConsensusNodes.Count > 0); for (int i = 0; i < chain.ConsensusNodes.Count; i++) { var nodeWallet = chain.ConsensusNodes[i].Wallet; if (string.Equals(name, nodeWallet.Name, StringComparison.OrdinalIgnoreCase)) { var wallet = DevWallet.FromExpressWallet(settings, nodeWallet); var account = wallet.GetAccounts().Single(a => a.IsDefault); accountHash = account.ScriptHash; return(true); } } if (GENESIS.Equals(name, StringComparison.OrdinalIgnoreCase)) { (_, accountHash) = chain.GetGenesisAccount(settings); return(true); } if (TryToScriptHash(name, settings.AddressVersion, out accountHash)) { return(true); } accountHash = default; return(false);
public OfflineNode(ProtocolSettings settings, RocksDbStorageProvider rocksDbStorageProvider, Wallet nodeWallet, ExpressChain chain, bool enableTrace) { this.nodeWallet = nodeWallet; this.chain = chain; this.rocksDbStorageProvider = rocksDbStorageProvider; applicationEngineProvider = enableTrace ? new ExpressApplicationEngineProvider() : null; consensusNodesKeys = new Lazy <KeyPair[]>(() => chain.ConsensusNodes .Select(n => n.Wallet.DefaultAccount ?? throw new Exception()) .Select(a => new KeyPair(a.PrivateKey.HexToBytes())) .ToArray()); var storageProviderPlugin = new StorageProviderPlugin(rocksDbStorageProvider); _ = new ExpressAppLogsPlugin(rocksDbStorageProvider); neoSystem = new NeoSystem(settings, storageProviderPlugin.Name); ApplicationEngine.Log += OnLog !; }
public static bool TryFindChain(this IFileSystem fileSystem, [MaybeNullWhen(false)] out ExpressChain chain, string fileName = Constants.DEFAULT_EXPRESS_FILENAME, string?searchFolder = null) { searchFolder ??= fileSystem.Directory.GetCurrentDirectory(); while (searchFolder != null) { var filePath = fileSystem.Path.Combine(searchFolder, fileName); if (fileSystem.File.Exists(filePath)) { chain = fileSystem.LoadChain(filePath); return(true); } searchFolder = fileSystem.Path.GetDirectoryName(searchFolder); } chain = null; return(false); }
public static bool TryGetAccountHash(this ExpressChain chain, string name, [MaybeNullWhen(false)] out UInt160 accountHash) { if (chain.Wallets != null && chain.Wallets.Count > 0) { for (int i = 0; i < chain.Wallets.Count; i++) { if (string.Equals(name, chain.Wallets[i].Name, StringComparison.OrdinalIgnoreCase)) { accountHash = chain.Wallets[i].DefaultAccount.GetScriptHash(); return(true); } } } Debug.Assert(chain.ConsensusNodes != null && chain.ConsensusNodes.Count > 0); for (int i = 0; i < chain.ConsensusNodes.Count; i++) { var nodeWallet = chain.ConsensusNodes[i].Wallet; if (string.Equals(name, nodeWallet.Name, StringComparison.OrdinalIgnoreCase)) { accountHash = nodeWallet.DefaultAccount.GetScriptHash(); return(true); } } if (GENESIS.Equals(name, StringComparison.OrdinalIgnoreCase)) { var keys = chain.ConsensusNodes .Select(n => n.Wallet.DefaultAccount ?? throw new Exception()) .Select(a => new KeyPair(a.PrivateKey.HexToBytes()).PublicKey) .ToArray(); var contract = Neo.SmartContract.Contract.CreateMultiSigContract((keys.Length * 2 / 3) + 1, keys); accountHash = contract.ScriptHash; return(true); } return(chain.TryParseScriptHash(name, out accountHash)); }
public static TransactionManager AddSignatures(this TransactionManager tm, ExpressChain chain, ExpressWalletAccount account) { if (account.IsMultiSigContract()) { var signers = chain.GetMultiSigAccounts(account); var publicKeys = signers.Select(s => s.GetKey() !.PublicKey).ToArray(); var sigCount = account.Contract.Parameters.Count; foreach (var signer in signers.Take(sigCount)) { var keyPair = signer.GetKey() ?? throw new Exception(); tm = tm.AddMultiSig(keyPair, sigCount, publicKeys); } return(tm); } else { return(tm.AddSignature(account.GetKey() !)); } }
static RpcServerSettings GetRpcServerSettings(ExpressChain chain, ExpressConsensusNode node) { var settings = new Dictionary <string, string>() { { "PluginConfiguration:Network", $"{chain.Network}" }, { "PluginConfiguration:BindAddress", $"{IPAddress.Loopback}" }, { "PluginConfiguration:Port", $"{node.RpcPort}" } }; if (chain.TryReadSetting <long>("rpc.MaxGasInvoke", long.TryParse, out var maxGasInvoke)) { settings.Add("PluginConfiguration:MaxGasInvoke", $"{maxGasInvoke}"); } if (chain.TryReadSetting <long>("rpc.MaxFee", long.TryParse, out var maxFee)) { settings.Add("PluginConfiguration:MaxFee", $"{maxFee}"); } var config = new ConfigurationBuilder().AddInMemoryCollection(settings).Build(); return(RpcServerSettings.Load(config.GetSection("PluginConfiguration"))); }
public static bool InitializeProtocolSettings(this ExpressChain chain, uint secondsPerBlock = 0) { secondsPerBlock = secondsPerBlock == 0 ? 15 : secondsPerBlock; IEnumerable <KeyValuePair <string, string> > settings() { yield return(new KeyValuePair <string, string>( "ProtocolConfiguration:Magic", $"{chain.Magic}")); yield return(new KeyValuePair <string, string>( "ProtocolConfiguration:AddressVersion", $"{ExpressChain.AddressVersion}")); yield return(new KeyValuePair <string, string>( "ProtocolConfiguration:SecondsPerBlock", $"{secondsPerBlock}")); foreach (var(node, index) in chain.ConsensusNodes.Select((n, i) => (n, i))) { var privateKey = node.Wallet.Accounts .Select(a => a.PrivateKey) .Distinct().Single().HexToBytes(); var encodedPublicKey = new KeyPair(privateKey).PublicKey .EncodePoint(true).ToHexString(); yield return(new KeyValuePair <string, string>( $"ProtocolConfiguration:StandbyValidators:{index}", encodedPublicKey)); yield return(new KeyValuePair <string, string>( $"ProtocolConfiguration:SeedList:{index}", $"{IPAddress.Loopback}:{node.TcpPort}")); } } var config = new ConfigurationBuilder() .AddInMemoryCollection(settings()) .Build(); return(ProtocolSettings.Initialize(config)); }
// this method only used in Online/OfflineNode ExecuteAsync public static IReadOnlyList <Wallet> GetMultiSigWallets(this ExpressChain chain, ProtocolSettings settings, UInt160 accountHash) { var builder = ImmutableList.CreateBuilder <Wallet>(); for (int i = 0; i < chain.ConsensusNodes.Count; i++) { var wallet = DevWallet.FromExpressWallet(settings, chain.ConsensusNodes[i].Wallet); if (wallet.GetAccount(accountHash) != null) { builder.Add(wallet); } } for (int i = 0; i < chain.Wallets.Count; i++) { var wallet = DevWallet.FromExpressWallet(settings, chain.Wallets[i]); if (wallet.GetAccount(accountHash) != null) { builder.Add(wallet); } } return(builder.ToImmutable()); }
public static Witness GetWitness(Transaction tx, ExpressChain chain, ExpressWalletAccount account) { var txHashData = Neo.Network.P2P.Helper.GetHashData(tx); var devAccount = DevWalletAccount.FromExpressWalletAccount(account); if (IsMultiSigContract(account)) { // neo-express only uses multi sig contracts for consensus nodes var verification = devAccount.Contract.Script; var signatureCount = devAccount.Contract.ParameterList.Length; var signatures = chain.ConsensusNodes .Select(node => DevWalletAccount.FromExpressWalletAccount(node.Wallet.DefaultAccount)) .OrderBy(account => (account.GetKey() ?? throw new Exception("DevWalletAccount missing key")).PublicKey) .Select(account => Sign(txHashData, account)) .Take(signatureCount) .ToList(); var invocation = new byte[signatures.Aggregate(0, (a, sig) => a + sig.Length + 1)]; var curPos = 0; for (int i = 0; i < signatures.Count; i++) { invocation[curPos] = 0x40; signatures[i].AsSpan().CopyTo(invocation.AsSpan().Slice(curPos + 1, signatures[i].Length)); curPos += 1 + signatures[i].Length; } return(new Witness() { InvocationScript = invocation, VerificationScript = verification, }); } else {