public byte[] ComputeHash(IHashFactory hashFactory) { IByteConverter converter = hashFactory.GetByteConverter(); string rawData = converter.ConvertToString(Nonce) + converter.ConvertToString(Sender) + converter.ConvertToString(Recipient) + Amount; byte[] data = Encoding.ASCII.GetBytes(rawData); return(hashFactory.GetDigest().GetHash(data)); }
public IMiner GetMiner(IEnumerable <Transaction> transactions, Block previousBlock) { double factor = amountFactor(transactions, previousBlock.Transactions); int algorithmId = previousBlock.HashAlgorithmId; int difficulty = previousBlock.Difficulty; algorithmId = adjustAlgorithmIdByFactor(factor, algorithmId, maxId: 3); IHashFactory hashFactory = GetMiningHashFactoryById(algorithmId); int maxDifficulty = hashFactory.GetDigest().HashLength / 2; difficulty = adjustDifficultyByFactor(factor, difficulty, maxDifficulty); return(new BasicMiner(algorithmId, difficulty, hashFactory)); }
public byte[] ComputeHash(IHashFactory hashFactory) { IByteConverter converter = hashFactory.GetByteConverter(); IDigest digest = hashFactory.GetDigest(); string data = converter.ConvertToString(Nonce) + converter.ConvertToString(PreviousHash) + CreationTime.ToString() + converter.ConvertToString(MerkleRoot) + converter.ConvertToString(MinerAddress) + HashAlgorithmId + Difficulty; byte[] rawData = Encoding.ASCII.GetBytes(data); return(digest.GetHash(rawData)); }
public Block(byte[] minerAddress, byte[] previousHash, ICollection <Transaction> transactions, IHashFactory hashFactory) { MinerAddress = minerAddress; Transactions = transactions; CreationTime = DateTime.Now; IDigest digest = hashFactory.GetDigest(); INonceGenerator nonceGenerator = hashFactory.GetNonceGenerator(); PreviousHash = new byte[previousHash.Length]; Array.Copy(previousHash, PreviousHash, previousHash.Length); MerkleRoot = ComputeMerkleRoot(hashFactory); Nonce = nonceGenerator.GetNextNonce(); Hash = ComputeHash(hashFactory); }
public Blockchain(Wallet miningWallet, IWalletManager walletManager, IHashFactory transactionHashFactory, ISignatureFactory signatureFactory, IMiningFactory miningFactory, IBlockStorage storage) { SignatureFactory = signatureFactory; Verifier = signatureFactory.GetSignatureVerifier(); MiningFactory = miningFactory; TransactionHashFactory = transactionHashFactory; Digest = TransactionHashFactory.GetDigest(); MiningWallet = miningWallet; WalletManager = walletManager; pendingTransactions = new LinkedList <Transaction>(); storedChain = storage; foreach (Block block in storedChain) { WalletManager.AcceptTransactions(block.Transactions); } }
static void Main(string[] args) { #region Configs. BlockchainStorageManagerParameters managerParameters = new BlockchainStorageManagerParameters() { BlocksInUnit = 2 }; UnitRepositoryParameters repositoryParameters = new UnitRepositoryParameters() { DirectoryPath = Path.Combine(Directory.GetCurrentDirectory(), "blockchain") }; WalletManagerParameters walletManagerParameters = new WalletManagerParameters() { WalletDirectoryPath = Path.Combine(Directory.GetCurrentDirectory(), "wallets") }; NetworkParameters networkParametersFirst = new NetworkParameters() { AddressBookPath = Path.Combine(Directory.GetCurrentDirectory(), AddressBookPath), PeerHostName = Dns.GetHostName(), PeerPort = 8900, ClientTimeout = new TimeSpan(0, 0, 4), ServerTimeout = new TimeSpan(0, 0, 4) }; BasicMiningFactoryParameters parameters = new BasicMiningFactoryParameters() { HashLeastUpFactor = 1.25, HashMostUpFactor = 1.75, HashLeastDownFactor = 0.25, HashMostDownFactor = 0.75, DifficultyLeastUpFactor = 1.25, DifficultyMostUpFactor = 1.75, DifficultyLeastDownFactor = 0.25, DifficultyMostDownFactor = 0.75, MinDifficulty = Difficulty }; JsonSerializer jsonSerializer = new JsonSerializer() { Formatting = Formatting.Indented }; using (Stream jsonFile = File.Open(MiningConfigPath, FileMode.Create, FileAccess.Write, FileShare.None)) using (StreamWriter writer = new StreamWriter(jsonFile)) using (JsonWriter jsonWriter = new JsonTextWriter(writer)) jsonSerializer.Serialize(jsonWriter, parameters); using (Stream jsonFile = File.Open(ManagerConfigPath, FileMode.Create, FileAccess.Write, FileShare.None)) using (StreamWriter writer = new StreamWriter(jsonFile)) using (JsonWriter jsonWriter = new JsonTextWriter(writer)) jsonSerializer.Serialize(jsonWriter, managerParameters); using (Stream jsonFile = File.Open(RepositoryConfigPath, FileMode.Create, FileAccess.Write, FileShare.None)) using (StreamWriter writer = new StreamWriter(jsonFile)) using (JsonWriter jsonWriter = new JsonTextWriter(writer)) jsonSerializer.Serialize(jsonWriter, repositoryParameters); using (Stream jsonFile = File.Open(WalletManagerConfigPath, FileMode.Create, FileAccess.Write, FileShare.None)) using (StreamWriter writer = new StreamWriter(jsonFile)) using (JsonWriter jsonWriter = new JsonTextWriter(writer)) jsonSerializer.Serialize(jsonWriter, walletManagerParameters); using (Stream jsonFile = File.Open(NetworkConfigPath, FileMode.Create, FileAccess.Write, FileShare.None)) using (StreamWriter writer = new StreamWriter(jsonFile)) using (JsonWriter jsonWriter = new JsonTextWriter(writer)) jsonSerializer.Serialize(jsonWriter, networkParametersFirst); JsonBasicMiningFactoryConfig miningConfig = new JsonBasicMiningFactoryConfig(MiningConfigPath); JsonBlockchainStorageManagerConfig managerConfig = new JsonBlockchainStorageManagerConfig(ManagerConfigPath); JsonUnitRepositoryConfig repositoryConfig = new JsonUnitRepositoryConfig(RepositoryConfigPath); JsonWalletManagerConfig walletManagerConfig = new JsonWalletManagerConfig(WalletManagerConfigPath); JsonNetworkConfig networkConfig = new JsonNetworkConfig(NetworkConfigPath); #endregion #region Directory remove. removeDirectory(repositoryConfig.DirectoryPath); removeDirectory(SecondPeerRepositoryPath); //removeDirectory(walletManagerConfig.WalletDirectoryPath); #endregion #region Directory creation. Directory.CreateDirectory(repositoryParameters.DirectoryPath); Directory.CreateDirectory(Path.Combine(repositoryConfig.DirectoryPath, "addressers")); Directory.CreateDirectory(Path.Combine(repositoryConfig.DirectoryPath, "units")); //Directory.CreateDirectory(walletManagerConfig.WalletDirectoryPath); #endregion #region Initial units. // Addressers. string addresser0Path = Path.Combine(repositoryParameters.DirectoryPath, "addressers", "addresser0.addr"); File.Create(addresser0Path).Close(); // Units. string unit0Path = Path.Combine(repositoryParameters.DirectoryPath, "units", "unit0.unit"); File.Create(unit0Path).Close(); BlockchainUnit unit0 = new BlockchainUnit(unit0Path, new AddressStorage(addresser0Path)); #endregion IMiningFactory miningFactory = new BasicMiningFactory(miningConfig); IHashFactory hashFactory = miningFactory.GetMiningHashFactoryById(3); IHashFactory transactionHashFactory = new TransactionHashFactory(); ISignatureFactory signatureFactory = new ECDSAFactory(); IWalletManager walletManager = new WalletManager(walletManagerConfig, signatureFactory, transactionHashFactory); Wallet wallet = walletManager.GetWallets().First(); #region Initial transactions. LinkedList <Transaction> firstInitialList = new LinkedList <Transaction>(); LinkedList <Transaction> secondInitialList = new LinkedList <Transaction>(); Transaction firstInitialTransaction = new Transaction(wallet.PublicKey, wallet.PublicKey, 32, transactionHashFactory); Transaction secondInitialTransaction = new Transaction(wallet.PublicKey, wallet.PublicKey, 19, transactionHashFactory); Transaction thirdInitialTransaction = new Transaction(wallet.PublicKey, wallet.PublicKey, 32, transactionHashFactory); Transaction fourthInitialTransaction = new Transaction(wallet.PublicKey, wallet.PublicKey, 19, transactionHashFactory); firstInitialTransaction.SignTransaction(wallet.Signer); secondInitialTransaction.SignTransaction(wallet.Signer); thirdInitialTransaction.SignTransaction(wallet.Signer); fourthInitialTransaction.SignTransaction(wallet.Signer); firstInitialList.AddLast(firstInitialTransaction); firstInitialList.AddLast(secondInitialTransaction); secondInitialList.AddLast(thirdInitialTransaction); secondInitialList.AddLast(fourthInitialTransaction); #endregion #region Initial blocks. IHashFactory miningHashFactory = miningFactory.GetMiningHashFactoryById(3); IMiner miner = new BasicMiner(3, Difficulty, miningHashFactory); Block firstInitialBlock = new Block(wallet.PublicKey, new byte[hashFactory.GetDigest().HashLength], firstInitialList, hashFactory); miner.MineBlock(firstInitialBlock); firstInitialBlock.SignBlock(wallet.Signer); Block secondInitialBlock = new Block(wallet.PublicKey, firstInitialBlock.Hash, secondInitialList, hashFactory); miner.MineBlock(secondInitialBlock); secondInitialBlock.SignBlock(wallet.Signer); unit0.AddBlock(firstInitialBlock); unit0.AddBlock(secondInitialBlock); unit0.Dispose(); #endregion Copy(repositoryConfig.DirectoryPath, SecondPeerRepositoryPath); UnitRepository repository = new UnitRepository(repositoryConfig); BlockchainStorageManager storageManager = new BlockchainStorageManager(managerConfig, repository); Blockchain blockchain = new Blockchain(wallet, walletManager, transactionHashFactory, signatureFactory, miningFactory, storageManager); wallet.Blockchain = blockchain; #region Network. AddressBook addressBook = new AddressBook(AddressBookPath); IBroadcaster network = new Network(blockchain, addressBook, networkConfig, wallet.Signer); blockchain.NetworkBroadcaster = network; network.Start(); #endregion Console.WriteLine("First"); Console.WriteLine("0 - send transaction;\n1 - mine new block;\n2 - validate blockchain;\n3 - count your balance;\n4 - quit"); string input; do { input = Console.ReadLine(); switch (input) { case "0": wallet.SendTokens(8, addressBook.First().Key.PublicKey); break; case "1": blockchain.ProcessPendingTransactions(); break; case "2": Console.WriteLine(blockchain.IsValid ? "Blockchain is valid" : "Blockchain is invalid"); break; case "3": Console.WriteLine($"Current balance for testing wallet: " + blockchain.CountBalanceForWallet(wallet.PublicKey)); break; case "4": Console.WriteLine("Bye!"); break; default: Console.WriteLine("Wrong input!"); break; } }while (input != "4"); network.Stop(); }
static void Main(string[] args) { #region Configs creation. BlockchainStorageManagerParameters managerParameters = new BlockchainStorageManagerParameters() { BlocksInUnit = 2 }; UnitRepositoryParameters firstRepositoryParameters = new UnitRepositoryParameters() { DirectoryPath = Path.Combine(Directory.GetCurrentDirectory(), "blockchain0") }; UnitRepositoryParameters secondRepositoryParameters = new UnitRepositoryParameters() { DirectoryPath = Path.Combine(Directory.GetCurrentDirectory(), "blockchain1") }; WalletManagerParameters walletManagerParameters = new WalletManagerParameters() { WalletDirectoryPath = Path.Combine(Directory.GetCurrentDirectory(), "wallets") }; NetworkParameters networkParametersFirst = new NetworkParameters() { AddressBookPath = Path.Combine(Directory.GetCurrentDirectory(), FirstBookPath), PeerHostName = Dns.GetHostName(), PeerPort = 8900, ClientTimeout = new TimeSpan(0, 0, 4), ServerTimeout = new TimeSpan(0, 0, 4) }; NetworkParameters networkParametersSecond = new NetworkParameters() { AddressBookPath = Path.Combine(Directory.GetCurrentDirectory(), SecondBookPath), PeerHostName = Dns.GetHostName(), PeerPort = 8910, ClientTimeout = new TimeSpan(0, 0, 4), ServerTimeout = new TimeSpan(0, 0, 4) }; BasicMiningFactoryParameters parameters = new BasicMiningFactoryParameters() { HashLeastUpFactor = 1.25, HashMostUpFactor = 1.75, HashLeastDownFactor = 0.25, HashMostDownFactor = 0.75, DifficultyLeastUpFactor = 1.25, DifficultyMostUpFactor = 1.75, DifficultyLeastDownFactor = 0.25, DifficultyMostDownFactor = 0.75, MinDifficulty = Difficulty }; JsonSerializer jsonSerializer = new JsonSerializer() { Formatting = Formatting.Indented }; using (Stream jsonFile = File.Open(MiningConfigPath, FileMode.Create, FileAccess.Write, FileShare.None)) using (StreamWriter writer = new StreamWriter(jsonFile)) using (JsonWriter jsonWriter = new JsonTextWriter(writer)) jsonSerializer.Serialize(jsonWriter, parameters); using (Stream jsonFile = File.Open(ManagerConfigPath, FileMode.Create, FileAccess.Write, FileShare.None)) using (StreamWriter writer = new StreamWriter(jsonFile)) using (JsonWriter jsonWriter = new JsonTextWriter(writer)) jsonSerializer.Serialize(jsonWriter, managerParameters); using (Stream jsonFile = File.Open(FirstRepositoryConfigPath, FileMode.Create, FileAccess.Write, FileShare.None)) using (StreamWriter writer = new StreamWriter(jsonFile)) using (JsonWriter jsonWriter = new JsonTextWriter(writer)) jsonSerializer.Serialize(jsonWriter, firstRepositoryParameters); using (Stream jsonFile = File.Open(SecondRepositoryConfigPath, FileMode.Create, FileAccess.Write, FileShare.None)) using (StreamWriter writer = new StreamWriter(jsonFile)) using (JsonWriter jsonWriter = new JsonTextWriter(writer)) jsonSerializer.Serialize(jsonWriter, secondRepositoryParameters); using (Stream jsonFile = File.Open(WalletManagerConfigPath, FileMode.Create, FileAccess.Write, FileShare.None)) using (StreamWriter writer = new StreamWriter(jsonFile)) using (JsonWriter jsonWriter = new JsonTextWriter(writer)) jsonSerializer.Serialize(jsonWriter, walletManagerParameters); using (Stream jsonFile = File.Open(FirstNetworkConfigPath, FileMode.Create, FileAccess.Write, FileShare.None)) using (StreamWriter writer = new StreamWriter(jsonFile)) using (JsonWriter jsonWriter = new JsonTextWriter(writer)) jsonSerializer.Serialize(jsonWriter, networkParametersFirst); using (Stream jsonFile = File.Open(SecondNetworkConfigPath, FileMode.Create, FileAccess.Write, FileShare.None)) using (StreamWriter writer = new StreamWriter(jsonFile)) using (JsonWriter jsonWriter = new JsonTextWriter(writer)) jsonSerializer.Serialize(jsonWriter, networkParametersSecond); JsonBasicMiningFactoryConfig miningConfig = new JsonBasicMiningFactoryConfig(MiningConfigPath); JsonBlockchainStorageManagerConfig managerConfig = new JsonBlockchainStorageManagerConfig(ManagerConfigPath); JsonUnitRepositoryConfig firstRepositoryConfig = new JsonUnitRepositoryConfig(FirstRepositoryConfigPath); JsonUnitRepositoryConfig secondRepositoryConfig = new JsonUnitRepositoryConfig(SecondRepositoryConfigPath); JsonWalletManagerConfig walletManagerConfig = new JsonWalletManagerConfig(WalletManagerConfigPath); JsonNetworkConfig firstNetworkConfig = new JsonNetworkConfig(FirstNetworkConfigPath); JsonNetworkConfig secondNetworkConfig = new JsonNetworkConfig(SecondNetworkConfigPath); #endregion #region Directory remove. removeDirectory(firstRepositoryConfig.DirectoryPath); removeDirectory(secondRepositoryConfig.DirectoryPath); removeDirectory(walletManagerConfig.WalletDirectoryPath); #endregion #region Directory creation. Directory.CreateDirectory(firstRepositoryParameters.DirectoryPath); Directory.CreateDirectory(secondRepositoryParameters.DirectoryPath); Directory.CreateDirectory(Path.Combine(firstRepositoryConfig.DirectoryPath, "addressers")); Directory.CreateDirectory(Path.Combine(firstRepositoryConfig.DirectoryPath, "units")); Directory.CreateDirectory(Path.Combine(secondRepositoryConfig.DirectoryPath, "addressers")); Directory.CreateDirectory(Path.Combine(secondRepositoryConfig.DirectoryPath, "units")); Directory.CreateDirectory(walletManagerConfig.WalletDirectoryPath); #endregion #region Initial units. // Addressers. string firstAddresser0Path = Path.Combine(firstRepositoryParameters.DirectoryPath, "addressers", "addresser0.addr"); string secondAddresser0Path = Path.Combine(secondRepositoryParameters.DirectoryPath, "addressers", "addresser0.addr"); #if (CREATE_EXTRA_FILE) string firstAddresser1Path = Path.Combine(firstRepositoryParameters.DirectoryPath, "addressers", "addresser1.addr"); string secondAddresser1Path = Path.Combine(secondRepositoryParameters.DirectoryPath, "addressers", "addresser1.addr"); #endif File.Create(firstAddresser0Path).Close(); File.Create(secondAddresser0Path).Close(); #if (CREATE_EXTRA_FILE) File.Create(firstAddresser1Path).Close(); File.Create(secondAddresser1Path).Close(); #endif // Units. string firstUnit0Path = Path.Combine(firstRepositoryParameters.DirectoryPath, "units", "unit0.unit"); string secondUnit0Path = Path.Combine(secondRepositoryParameters.DirectoryPath, "units", "unit0.unit"); #if (CREATE_EXTRA_FILE) string firstUnit1Path = Path.Combine(firstRepositoryParameters.DirectoryPath, "units", "unit1.unit"); string secondUnit1Path = Path.Combine(secondRepositoryParameters.DirectoryPath, "units", "unit1.unit"); #endif File.Create(firstUnit0Path).Close(); File.Create(secondUnit0Path).Close(); #if (CREATE_EXTRA_FILE) File.Create(firstUnit1Path).Close(); File.Create(secondUnit1Path).Close(); #endif BlockchainUnit firstUnit0 = new BlockchainUnit(firstUnit0Path, new AddressStorage(firstAddresser0Path)); BlockchainUnit secondUnit0 = new BlockchainUnit(secondUnit0Path, new AddressStorage(secondAddresser0Path)); #endregion IMiningFactory miningFactory = new BasicMiningFactory(miningConfig); IHashFactory hashFactory = miningFactory.GetMiningHashFactoryById(3); IHashFactory transactionHashFactory = new TransactionHashFactory(); ISignatureFactory signatureFactory = new ECDSAFactory(); IWalletManager walletManager = new WalletManager(walletManagerConfig, signatureFactory, transactionHashFactory); Wallet firstWallet = walletManager.AddNewWallet(); #region Initial transactions. LinkedList <Transaction> firstInitialList = new LinkedList <Transaction>(); LinkedList <Transaction> secondInitialList = new LinkedList <Transaction>(); Transaction firstInitialTransaction = new Transaction(firstWallet.PublicKey, firstWallet.PublicKey, 32, transactionHashFactory); Transaction secondInitialTransaction = new Transaction(firstWallet.PublicKey, firstWallet.PublicKey, 19, transactionHashFactory); Transaction thirdInitialTransaction = new Transaction(firstWallet.PublicKey, firstWallet.PublicKey, 32, transactionHashFactory); Transaction fourthInitialTransaction = new Transaction(firstWallet.PublicKey, firstWallet.PublicKey, 19, transactionHashFactory); firstInitialTransaction.SignTransaction(firstWallet.Signer); secondInitialTransaction.SignTransaction(firstWallet.Signer); thirdInitialTransaction.SignTransaction(firstWallet.Signer); fourthInitialTransaction.SignTransaction(firstWallet.Signer); firstInitialList.AddLast(firstInitialTransaction); firstInitialList.AddLast(secondInitialTransaction); secondInitialList.AddLast(thirdInitialTransaction); secondInitialList.AddLast(fourthInitialTransaction); #endregion #region Initial blocks. IHashFactory miningHashFactory = miningFactory.GetMiningHashFactoryById(3); IMiner miner = new BasicMiner(3, Difficulty, miningHashFactory); Block firstInitialBlock = new Block(firstWallet.PublicKey, new byte[hashFactory.GetDigest().HashLength], firstInitialList, hashFactory); miner.MineBlock(firstInitialBlock); firstInitialBlock.SignBlock(firstWallet.Signer); Block secondInitialBlock = new Block(firstWallet.PublicKey, firstInitialBlock.Hash, secondInitialList, hashFactory); miner.MineBlock(secondInitialBlock); secondInitialBlock.SignBlock(firstWallet.Signer); firstUnit0.AddBlock(firstInitialBlock); firstUnit0.AddBlock(secondInitialBlock); secondUnit0.AddBlock(firstInitialBlock); secondUnit0.AddBlock(secondInitialBlock); firstUnit0.Dispose(); secondUnit0.Dispose(); #endregion UnitRepository firstRepository = new UnitRepository(firstRepositoryConfig); UnitRepository secondRepository = new UnitRepository(secondRepositoryConfig); BlockchainStorageManager firstManager = new BlockchainStorageManager(managerConfig, firstRepository); BlockchainStorageManager secondManager = new BlockchainStorageManager(managerConfig, secondRepository); Blockchain firstBlockchain = new Blockchain(firstWallet, walletManager, transactionHashFactory, signatureFactory, miningFactory, firstManager); Wallet secondWallet = walletManager.AddNewWallet(); Blockchain secondBlockchain = new Blockchain(secondWallet, walletManager, transactionHashFactory, signatureFactory, miningFactory, secondManager); #region Address books creation. File.Create(FirstBookPath).Close(); File.Create(SecondBookPath).Close(); PeerAddress firstPeer = new PeerAddress(firstWallet.PublicKey); PeerAddress secondPeer = new PeerAddress(secondWallet.PublicKey); AddressBook firstPeerAddressBook = new AddressBook(FirstBookPath); AddressBook secondPeerAddressBook = new AddressBook(SecondBookPath); firstPeerAddressBook.Add(secondPeer, "ws://" + secondNetworkConfig.PeerHostName + $":{secondNetworkConfig.PeerPort}/simplecoin"); secondPeerAddressBook.Add(firstPeer, "ws://" + firstNetworkConfig.PeerHostName + $":{firstNetworkConfig.PeerPort}/simplecoin"); firstPeerAddressBook.SaveOnDrive(); secondPeerAddressBook.SaveOnDrive(); #endregion #region Network. IBroadcaster firstNetwork = new Network(firstBlockchain, firstPeerAddressBook, firstNetworkConfig, firstWallet.Signer); IBroadcaster secondNetwork = new Network(secondBlockchain, secondPeerAddressBook, secondNetworkConfig, secondWallet.Signer); firstBlockchain.NetworkBroadcaster = firstNetwork; secondBlockchain.NetworkBroadcaster = secondNetwork; firstNetwork.Start(); secondNetwork.Start(); #endregion firstWallet.Blockchain = firstBlockchain; secondWallet.Blockchain = secondBlockchain; firstWallet.SendTokens(16, secondWallet.PublicKey); firstWallet.SendTokens(8, secondWallet.PublicKey); secondBlockchain.ProcessPendingTransactions(); secondWallet.SendTokens(8, firstWallet.PublicKey); firstBlockchain.ProcessPendingTransactions(); Console.WriteLine("First blockchain:\n"); foreach (Block block in firstBlockchain) { Console.WriteLine(JsonConvert.SerializeObject(block, Formatting.Indented)); Console.WriteLine(); } Console.WriteLine("--------------------------------------------------------------------------------------"); Console.WriteLine("Second blockchain:\n"); foreach (Block block in secondBlockchain) { Console.WriteLine(JsonConvert.SerializeObject(block, Formatting.Indented)); Console.WriteLine(); } Console.WriteLine("}\n"); Console.WriteLine(firstBlockchain.IsValid ? "First blockchain is valid" : "First blockchain is invalid"); Console.WriteLine(firstBlockchain.IsValid ? "Second blockchain is valid" : "Second blockchain is invalid"); Console.WriteLine($"Cryptocurrency ownership for first wallet: {firstBlockchain.CountBalanceForWallet(firstWallet.PublicKey)}"); Console.WriteLine($"Cryptocurrency ownership for second wallet: {firstBlockchain.CountBalanceForWallet(secondWallet.PublicKey)}"); }