public void TestRpcImportPubkeyIsSuccessful() { using (NodeBuilder builder = NodeBuilder.Create(this)) { CoreNode node = builder.CreateStratisPowNode(new BitcoinRegTest()).AlwaysFlushBlocks().WithWallet().Start(); CoreNode node2 = builder.CreateStratisPowNode(new BitcoinRegTest()).WithReadyBlockchainData(ReadyBlockchain.BitcoinRegTest10Miner).Start(); TestHelper.ConnectAndSync(node, node2); UnspentOutputReference tx = node2.FullNode.WalletManager().GetUnspentTransactionsInWallet("mywallet", 0, Features.Wallet.Wallet.NormalAccounts).First(); RPCClient rpc = node.CreateRPCClient(); PubKey pubKey = PayToPubkeyTemplate.Instance.ExtractScriptPubKeyParameters(tx.Address.Pubkey); PubKey pubKey2 = new Key().PubKey; uint256 blockHash = rpc.GenerateToAddress(1, pubKey2.GetAddress(rpc.Network)).First(); Block block = rpc.GetBlock(blockHash); uint256 tx2 = block.Transactions.First().GetHash(); Assert.Throws <RPCException>(() => rpc.SendCommand(RPCOperations.gettransaction, tx.Transaction.Id.ToString(), true)); Assert.Throws <RPCException>(() => rpc.SendCommand(RPCOperations.gettransaction, tx2.ToString(), true));; // Test that adding the same pubkey twice doesn't throw. rpc.ImportPubKey(pubKey.ToHex()); rpc.ImportPubKey(pubKey.ToHex()); // Add a second pubkey and ensure it doesn't throw. rpc.ImportPubKey(pubKey2.ToHex()); // Add an arbitrary pubkey and ensure it doesn't throw. rpc.ImportPubKey(new Key().PubKey.ToHex()); TestBase.WaitLoop(() => node.FullNode.WalletManager().WalletTipHeight == node2.FullNode.WalletManager().WalletTipHeight); TestBase.WaitLoop(() => { try { // Check if gettransaction can now find the transactions in the watch only account. RPCResponse walletTx = rpc.SendCommand(RPCOperations.gettransaction, tx.Transaction.Id.ToString(), true); RPCResponse walletTx2 = rpc.SendCommand(RPCOperations.gettransaction, tx2.ToString(), true); return(walletTx != null && walletTx2 != null); } catch (RPCException) { return(false); } }); // Check that when include_watchonly is not set, the watched addresses' transactions cannot be located in the normal wallet accounts. Assert.Throws <RPCException>(() => rpc.SendCommand(RPCOperations.gettransaction, tx.Transaction.Id.ToString(), false)); Assert.Throws <RPCException>(() => rpc.SendCommand(RPCOperations.gettransaction, tx2.ToString(), false)); } }
public async Task <IActionResult> JoinFederationAsync([FromBody] JoinFederationRequestModel request, CancellationToken cancellationToken = default) { Guard.NotNull(request, nameof(request)); // Checks that the request is valid. if (!this.ModelState.IsValid) { this.logger.LogTrace("(-)[MODEL_STATE_INVALID]"); return(ModelStateErrors.BuildErrorResponse(this.ModelState)); } if (!(this.network.Consensus.Options as PoAConsensusOptions).AutoKickIdleMembers) { return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, "Error", "This feature is currently disabled.")); } try { PubKey minerPubKey = await this.joinFederationRequestService.JoinFederationAsync(request, cancellationToken); var model = new JoinFederationResponseModel { MinerPublicKey = minerPubKey.ToHex() }; this.logger.LogTrace("(-):'{0}'", model); return(this.Json(model)); } catch (Exception e) { this.logger.LogError("Exception occurred: {0}", e.ToString()); this.logger.LogTrace("(-)[ERROR]"); return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString())); } }
public void GetBalance() { ssbalance = 0; string privateKeyStr = PrivateKey; //string privateKeyStr = "cUvazeu9ucqD4trygt8xMEQKZfR3SZ5BdiAWb3eEwbQ48iPwYKSB"; BitcoinSecret privateKey = new BitcoinSecret(privateKeyStr); Network network = privateKey.Network; PubKey pubKey = privateKey.PubKey; string pubKeyStr = pubKey.ToHex(); KeyId pkhash = pubKey.Hash; string pkhashStr = pkhash.ToString(); BitcoinAddress addres = pkhash.GetAddress(network); string address = addres.ToString(); string networkStr = bsvConfiguration_class.testNetwork; string uri = bsvConfiguration_class.RestApiUri; //读取未消费的交易输出utxo Task <RestApiUtxo_class[]> utxo = Task <RestApiUtxo_class[]> .Run(() => { RestApiUtxo_class[] untxo = RestApi_class.getUtxosByAnAddress(uri, networkStr, address); return(untxo); }); utxo.Wait(); int n = utxo.Result.Length; for (int i = 0; i < n; i++) { ssbalance += utxo.Result[i].Value; } //Console.WriteLine(ssbalance); label3.Text = "账户余额:"; label3.Text += ssbalance; label3.Text += " sat"; }
private void AddFederationMember(byte[] pubKeyBytes) { var key = new PubKey(pubKeyBytes); this.logger.LogInformation("Adding new fed member: '{0}'.", key.ToHex()); this.federationManager.AddFederationMember(key); }
private void RemoveFederationMember(byte[] pubKeyBytes) { var key = new PubKey(pubKeyBytes); this.logger.LogInformation("Kicking fed member: '{0}'.", key.ToHex()); this.federationManager.RemoveFederationMember(key); }
public async Task <IActionResult> JoinFederationAsync([FromBody] JoinFederationRequestModel request, CancellationToken cancellationToken = default) { Guard.NotNull(request, nameof(request)); // Checks that the request is valid. if (!this.ModelState.IsValid) { this.logger.LogTrace("(-)[MODEL_STATE_INVALID]"); return(ModelStateErrors.BuildErrorResponse(this.ModelState)); } try { PubKey minerPubKey = await(this.federationManager as CollateralFederationManager).JoinFederationAsync(request, cancellationToken); var model = new JoinFederationResponseModel { MinerPublicKey = minerPubKey.ToHex() }; this.logger.LogTrace("(-):'{0}'", model); return(this.Json(model)); } catch (Exception e) { this.logger.LogError("Exception occurred: {0}", e.ToString()); this.logger.LogTrace("(-)[ERROR]"); return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString())); } }
private void label4_Click(object sender, EventArgs e)//查询地址 { if (flagadr == 0) { string privateKeyStr = PrivateKey; BitcoinSecret privateKey = new BitcoinSecret(privateKeyStr); Network network = privateKey.Network; PubKey pubKey = privateKey.PubKey; string pubKeyStr = pubKey.ToHex(); KeyId pkhash = pubKey.Hash; string pkhashStr = pkhash.ToString(); BitcoinAddress addres = pkhash.GetAddress(network); string address = addres.ToString(); textBox1.Visible = true; textBox1.Text = address; flagadr = 2; } else if (flagadr == 1) { textBox1.Visible = true; flagadr = 2; } else { textBox1.Visible = false; flagadr = 1; } }
/// <summary> /// Describe public key /// </summary> /// <param name="publicKey"></param> /// <returns></returns> public static ScanTxoutPubkey PublicKey(PubKey publicKey) { if (publicKey == null) { throw new ArgumentNullException(nameof(publicKey)); } return(new ScanTxoutPubkey($"{publicKey.ToHex()}")); }
public void TestRpcListUnspentForWatchOnlyIsSuccessful() { using (NodeBuilder builder = NodeBuilder.Create(this)) { CoreNode node = builder.CreateStratisPowNode(new BitcoinRegTest()).AlwaysFlushBlocks().WithWallet().Start(); CoreNode node2 = builder.CreateStratisPowNode(new BitcoinRegTest()).WithReadyBlockchainData(ReadyBlockchain.BitcoinRegTest10Miner).Start(); TestHelper.ConnectAndSync(node, node2); UnspentOutputReference tx = node2.FullNode.WalletManager().GetUnspentTransactionsInWallet("mywallet", 0, Features.Wallet.Wallet.NormalAccounts).First(); RPCClient rpc = node.CreateRPCClient(); PubKey pubKey = PayToPubkeyTemplate.Instance.ExtractScriptPubKeyParameters(tx.Address.Pubkey); Assert.Throws <RPCException>(() => rpc.SendCommand(RPCOperations.gettransaction, tx.Transaction.Id.ToString(), true)); rpc.ImportPubKey(pubKey.ToHex()); // ListUnspent will not regard the outputs as spendable if they are not sufficiently mature. rpc.Generate((int)node.FullNode.Network.Consensus.CoinbaseMaturity); TestBase.WaitLoop(() => node.FullNode.WalletManager().WalletTipHeight == node2.FullNode.WalletManager().WalletTipHeight); TestBase.WaitLoop(() => { try { // Wait until gettransaction can find the transaction in the watch only account. RPCResponse walletTx = rpc.SendCommand(RPCOperations.gettransaction, tx.Transaction.Id.ToString(), true); return(walletTx != null); } catch (RPCException) { return(false); } }); UnspentCoin[] unspent = rpc.ListUnspent(1, 9999999); bool found = false; foreach (UnspentCoin coin in unspent) { if (coin.OutPoint == tx.ToOutPoint()) { found = true; } Assert.Equal(coin.Account, Features.Wallet.Wallet.WatchOnlyAccountName); } Assert.True(found); } }
public void RemoveFederationMember(PubKey pubKey) { lock (this.locker) { this.federationMembers.Remove(pubKey); this.SaveFederationKeys(this.federationMembers); this.logger.LogInformation("Federation member '{0}' was removed!", pubKey.ToHex()); } }
public WhatIsPublicKey(PubKey pubkey, Network network) { Hex = pubkey.ToHex(); Address = new WhatIsAddress(pubkey.GetAddress(network)); P2SHAddress = new WhatIsAddress(pubkey.ScriptPubKey.GetScriptAddress(network)) { RedeemScript = new WhatIsScript(pubkey.ScriptPubKey, network) }; ScriptPubKey = new WhatIsScript(pubkey.ScriptPubKey, network); IsCompressed = pubkey.IsCompressed; }
public void ListSecrets(NetworkCredential credentials, string host, Network net) { RPCClient client = new RPCClient("usuaraw:SupEr3421aw", new Uri("127.0.0.1"), net); IEnumerable <BitcoinSecret> secrets = client.ListSecrets(); foreach (BitcoinSecret secret in secrets) { PubKey pub = secret.PubKey; Console.WriteLine("key: " + secret.PrivateKey.GetWif(net)); Console.WriteLine("secret: " + secret); Console.WriteLine("pub: " + pub.ToHex()); Console.WriteLine("addr: " + pub.GetAddress(net)); } }
public void RemoveFederationMember(PubKey pubKey) { lock (this.locker) { this.federationMembers.Remove(pubKey); this.SaveFederationKeys(this.federationMembers); this.SetIsFederationMember(); this.logger.LogInformation("Federation member '{0}' was removed!", pubKey.ToHex()); } this.signals.Publish(new FedMemberKicked(pubKey)); }
private int FindPubKey(ExtPubKey xpubKey, PubKey pubKey) { var receive = xpubKey.Derive(0); for (int i = 0; i < 100; i++) { if (receive.Derive((uint)i).PubKey.ToHex() == pubKey.ToHex()) { return(i); } } throw new ArgumentException("Pubkey not found"); }
public void AddFederationMember(PubKey pubKey) { lock (this.locker) { if (this.federationMembers.Contains(pubKey)) { this.logger.LogTrace("(-)[ALREADY_EXISTS]"); return; } this.federationMembers.Add(pubKey); this.SaveFederationKeys(this.federationMembers); this.logger.LogInformation("Federation member '{0}' was added!", pubKey.ToHex()); } }
/// <summary> /// Verify the Neblio message signed by some Neblio Private Key. /// This function uses the Public Key instead of the Address for the verification /// </summary> /// <param name="message">input original message</param> /// <param name="signature">signature made by some Neblio address</param> /// <param name="address">Neblio address</param> /// <returns></returns> public static async Task <(bool, string)> VerifyMessage(string message, string signature, PubKey pubkey, bool messageIsAlreadyHash = false) { //if (string.IsNullOrEmpty(message) || string.IsNullOrEmpty(signature) || string.IsNullOrEmpty(address)) if (string.IsNullOrEmpty(message) || string.IsNullOrEmpty(signature) || pubkey == null) { return(false, "Input parameters cannot be empty or null."); } try { //var add = BitcoinAddress.Create(address, NeblioTransactionHelpers.Network); //var vadd = (add as IPubkeyHashUsable); var split = signature.Split('@'); if (split.Length > 1) { var sg = Convert.FromBase64String(split[1]); var recoveryId = Convert.ToInt32(split[0]); var sgs = new CompactSignature(recoveryId, sg); Console.WriteLine("Signature loaded."); PubKey recoveredPubKey = null; if (!messageIsAlreadyHash) { uint256 hash = NBitcoin.Crypto.Hashes.DoubleSHA256(Encoding.UTF8.GetBytes(message)); recoveredPubKey = PubKey.RecoverCompact(hash, sgs); } else { recoveredPubKey = PubKey.RecoverCompact(uint256.Parse(message), sgs); } if (recoveredPubKey.ToHex() == pubkey.ToHex()) { return(true, "Verified."); } } } catch (Exception ex) { Console.WriteLine("Exception in verification. " + ex.Message); return(false, "Wrong input. Cannot verify the message signature." + ex.Message); } return(false, "Not verified."); }
private static void Main(string[] args) { string wif = "KxyJg5ZCk6SFsorqiR7Bb6heDDmss4PX76VtyM5qTEyn83Bgt1tD"; Key privateKey = Key.Parse(wif); Console.WriteLine("0. private_key:\r\n" + privateKey.ToBytes().ToHex()); PubKey publicKey = privateKey.PubKey; Console.WriteLine("00. public_key_uncompressed:\r\n" + publicKey.ToHex()); string output = HashUtils.CreateBitcoinAddressVerbose(publicKey.Compress(true).ToBytes()); Console.WriteLine(output); string check = HashUtils.WifToBitcoinAddress(wif); Console.WriteLine("Check:\r\n" + check); }
private async Task VoteAndMineBlockAsync(PubKey key, bool add, CoreNode node) { var model = new HexPubKeyModel() { PubKeyHex = key.ToHex() }; if (add) { node.FullNode.NodeService <FederationVotingController>().VoteAddFedMember(model); } else { node.FullNode.NodeService <FederationVotingController>().VoteKickFedMember(model); } await node.MineBlocksAsync(1); CoreNodePoAExtensions.WaitTillSynced(this.node1, this.node2, this.node3); }
/// <summary>Checks that whomever mined this block is participating in any pending polls to vote-in new federation members.</summary> public override Task RunAsync(RuleContext context) { // "AddFederationMember" polls, that were started at or before this height, that are still pending, which this node has voted in favor of. List <Poll> pendingPolls = this.ruleEngine.VotingManager.GetPendingPolls() .Where(p => p.VotingData.Key == VoteKey.AddFederationMember && p.PollStartBlockData != null && p.PollStartBlockData.Height <= context.ValidationContext.ChainedHeaderToValidate.Height && p.PubKeysHexVotedInFavor.Any(pk => pk == this.federationManager.CurrentFederationKey.PubKey.ToHex())).ToList(); // Exit if there aren't any. if (!pendingPolls.Any()) { return(Task.CompletedTask); } // Ignore any polls that the miner has already voted on. PubKey blockMiner = this.federationHistory.GetFederationMemberForBlock(context.ValidationContext.ChainedHeaderToValidate).PubKey; pendingPolls = pendingPolls.Where(p => !p.PubKeysHexVotedInFavor.Any(pk => pk == blockMiner.ToHex())).ToList(); // Exit if there is nothing remaining. if (!pendingPolls.Any()) { return(Task.CompletedTask); } // Verify that the miner is including all the missing votes now. Transaction coinbase = context.ValidationContext.BlockToValidate.Transactions[0]; byte[] votingDataBytes = this.votingDataEncoder.ExtractRawVotingData(coinbase); if (votingDataBytes == null) { PoAConsensusErrors.BlockMissingVotes.Throw(); } // If any remaining polls are not found in the voting data list then throw a consenus error. List <VotingData> votingDataList = this.votingDataEncoder.Decode(votingDataBytes); if (pendingPolls.Any(p => !votingDataList.Any(data => data == p.VotingData))) { PoAConsensusErrors.BlockMissingVotes.Throw(); } return(Task.CompletedTask); }
public void key_test1() { BitcoinSecret bsecret1 = this.networkMain.CreateBitcoinSecret(strSecret1); BitcoinSecret bsecret2 = this.networkMain.CreateBitcoinSecret(strSecret2); BitcoinSecret bsecret1C = this.networkMain.CreateBitcoinSecret(strSecret1C); BitcoinSecret bsecret2C = this.networkMain.CreateBitcoinSecret(strSecret2C); Assert.Throws <FormatException>(() => this.networkMain.CreateBitcoinSecret(strAddressBad)); Key key1 = bsecret1.PrivateKey; Assert.True(key1.IsCompressed == false); Assert.True(bsecret1.Copy(true).PrivateKey.IsCompressed == true); Assert.True(bsecret1.Copy(true).Copy(false).IsCompressed == false); Assert.True(bsecret1.Copy(true).Copy(false).ToString() == bsecret1.ToString()); Key key2 = bsecret2.PrivateKey; Assert.True(key2.IsCompressed == false); Key key1C = bsecret1C.PrivateKey; Assert.True(key1C.IsCompressed == true); Key key2C = bsecret2C.PrivateKey; Assert.True(key1C.IsCompressed == true); PubKey pubkey1 = key1.PubKey; PubKey pubkey2 = key2.PubKey; PubKey pubkey1C = key1C.PubKey; PubKey pubkey2C = key2C.PubKey; Assert.True(this.addr1.Hash == pubkey1.Hash); Assert.True(this.addr2.Hash == pubkey2.Hash); Assert.True(this.addr1C.Hash == pubkey1C.Hash); Assert.True(this.addr2C.Hash == pubkey2C.Hash); for (int n = 0; n < 16; n++) { string strMsg = String.Format("Very secret message {0}: 11", n); if (n == 10) { //Test one long message strMsg = String.Join(",", Enumerable.Range(0, 2000).Select(i => i.ToString()).ToArray()); } uint256 hashMsg = Hashes.Hash256(TestUtils.ToBytes(strMsg)); // normal signatures ECDSASignature sign1 = null, sign2 = null, sign1C = null, sign2C = null; var tasks = new List <Task>(); tasks.Add(Task.Run(() => sign1 = key1.Sign(hashMsg))); tasks.Add(Task.Run(() => sign2 = key2.Sign(hashMsg))); tasks.Add(Task.Run(() => sign1C = key1C.Sign(hashMsg))); tasks.Add(Task.Run(() => sign2C = key2C.Sign(hashMsg))); Task.WaitAll(tasks.ToArray()); tasks.Clear(); tasks.Add(Task.Run(() => Assert.True(pubkey1.Verify(hashMsg, sign1)))); tasks.Add(Task.Run(() => Assert.True(pubkey2.Verify(hashMsg, sign2)))); tasks.Add(Task.Run(() => Assert.True(pubkey1C.Verify(hashMsg, sign1C)))); tasks.Add(Task.Run(() => Assert.True(pubkey2C.Verify(hashMsg, sign2C)))); Task.WaitAll(tasks.ToArray()); tasks.Clear(); tasks.Add(Task.Run(() => Assert.True(pubkey1.Verify(hashMsg, sign1)))); tasks.Add(Task.Run(() => Assert.True(!pubkey1.Verify(hashMsg, sign2)))); tasks.Add(Task.Run(() => Assert.True(pubkey1.Verify(hashMsg, sign1C)))); tasks.Add(Task.Run(() => Assert.True(!pubkey1.Verify(hashMsg, sign2C)))); tasks.Add(Task.Run(() => Assert.True(!pubkey2.Verify(hashMsg, sign1)))); tasks.Add(Task.Run(() => Assert.True(pubkey2.Verify(hashMsg, sign2)))); tasks.Add(Task.Run(() => Assert.True(!pubkey2.Verify(hashMsg, sign1C)))); tasks.Add(Task.Run(() => Assert.True(pubkey2.Verify(hashMsg, sign2C)))); tasks.Add(Task.Run(() => Assert.True(pubkey1C.Verify(hashMsg, sign1)))); tasks.Add(Task.Run(() => Assert.True(!pubkey1C.Verify(hashMsg, sign2)))); tasks.Add(Task.Run(() => Assert.True(pubkey1C.Verify(hashMsg, sign1C)))); tasks.Add(Task.Run(() => Assert.True(!pubkey1C.Verify(hashMsg, sign2C)))); tasks.Add(Task.Run(() => Assert.True(!pubkey2C.Verify(hashMsg, sign1)))); tasks.Add(Task.Run(() => Assert.True(pubkey2C.Verify(hashMsg, sign2)))); tasks.Add(Task.Run(() => Assert.True(!pubkey2C.Verify(hashMsg, sign1C)))); tasks.Add(Task.Run(() => Assert.True(pubkey2C.Verify(hashMsg, sign2C)))); Task.WaitAll(tasks.ToArray()); tasks.Clear(); // compact signatures (with key recovery) byte[] csign1 = null, csign2 = null, csign1C = null, csign2C = null; tasks.Add(Task.Run(() => csign1 = key1.SignCompact(hashMsg))); tasks.Add(Task.Run(() => csign2 = key2.SignCompact(hashMsg))); tasks.Add(Task.Run(() => csign1C = key1C.SignCompact(hashMsg))); tasks.Add(Task.Run(() => csign2C = key2C.SignCompact(hashMsg))); Task.WaitAll(tasks.ToArray()); tasks.Clear(); PubKey rkey1 = null, rkey2 = null, rkey1C = null, rkey2C = null; tasks.Add(Task.Run(() => rkey1 = PubKey.RecoverCompact(hashMsg, csign1))); tasks.Add(Task.Run(() => rkey2 = PubKey.RecoverCompact(hashMsg, csign2))); tasks.Add(Task.Run(() => rkey1C = PubKey.RecoverCompact(hashMsg, csign1C))); tasks.Add(Task.Run(() => rkey2C = PubKey.RecoverCompact(hashMsg, csign2C))); Task.WaitAll(tasks.ToArray()); tasks.Clear(); Assert.True(rkey1.ToHex() == pubkey1.ToHex()); Assert.True(rkey2.ToHex() == pubkey2.ToHex()); Assert.True(rkey1C.ToHex() == pubkey1C.ToHex()); Assert.True(rkey2C.ToHex() == pubkey2C.ToHex()); } }
public override async Task RunAsync(RuleContext context) { ChainedHeader chainedHeader = context.ValidationContext.ChainedHeaderToValidate; var header = chainedHeader.Header as PoABlockHeader; List <IFederationMember> federation = this.federationHistory.GetFederationForBlock(chainedHeader); PubKey pubKey = this.federationHistory.GetFederationMemberForBlock(context.ValidationContext.ChainedHeaderToValidate, federation)?.PubKey; if (pubKey == null || !this.validator.VerifySignature(pubKey, header)) { ChainedHeader currentHeader = context.ValidationContext.ChainedHeaderToValidate; // If we're evaluating a batch of received headers it's possible that we're so far beyond the current tip // that we have not yet processed all the votes that may determine the federation make-up. bool mightBeInsufficient = currentHeader.Height - this.chainState.ConsensusTip.Height > this.maxReorg; if (mightBeInsufficient) { // Mark header as insufficient to avoid banning the peer that presented it. // When we advance consensus we will be able to validate it. context.ValidationContext.InsufficientHeaderInformation = true; } this.Logger.LogDebug("(-)[INVALID_SIGNATURE]"); PoAConsensusErrors.InvalidHeaderSignature.Throw(); } // Look at the last round of blocks to find the previous time that the miner mined. uint roundTime = this.slotsManager.GetRoundLengthSeconds(federation.Count); int blockCounter = 0; for (ChainedHeader prevHeader = chainedHeader.Previous; prevHeader.Previous != null; prevHeader = prevHeader.Previous) { blockCounter += 1; if ((header.Time - prevHeader.Header.Time) >= roundTime) { break; } // If the miner is found again within the same round then throw a consensus error. if (this.federationHistory.GetFederationMemberForBlock(prevHeader)?.PubKey != pubKey) { continue; } // Mining slots shift when the federation changes. // Only raise an error if the federation did not change. uint newRoundTime = this.slotsManager.GetRoundLengthSeconds(this.federationHistory.GetFederationForBlock(prevHeader).Count); if (newRoundTime != roundTime) { break; } // Exempt this one block that somehow got included in the chain where the miner managed to mine too early. if (prevHeader.HashBlock == uint256.Parse("7d67ea42010f03971edd6ba5e1b644d09c9fd0191ca8d312255c12d23f7cd147")) { break; } this.Logger.LogWarning($"Block {prevHeader.HashBlock} was mined by the same miner '{pubKey.ToHex()}' as {blockCounter} blocks ({header.Time - prevHeader.Header.Time})s ago and there was no federation change."); } }
/// <inheritdoc/> public void AddVote(string requestId, BigInteger transactionId, PubKey pubKey) { lock (this.lockObject) { if (!this.receivedVotes.TryGetValue(requestId, out HashSet <PubKey> voted)) { voted = new HashSet <PubKey>(); } // Ignore the vote if the pubkey has already submitted a vote. if (voted.Contains(pubKey)) { return; } this.logger.Info("Pubkey {0} adding vote for request {1}, transactionId {2}.", pubKey.ToHex(), requestId, transactionId); voted.Add(pubKey); if (!this.activeVotes.TryGetValue(requestId, out Dictionary <BigInteger, int> transactionIdVotes)) { transactionIdVotes = new Dictionary <BigInteger, int>(); } if (!transactionIdVotes.ContainsKey(transactionId)) { transactionIdVotes[transactionId] = 1; } else { transactionIdVotes[transactionId]++; } this.activeVotes[requestId] = transactionIdVotes; this.receivedVotes[requestId] = voted; } }
public List <Teammate> GetWithPubKey(PubKey pubKey) { string pubKeyHex = pubKey.ToHex(); return(_context.Teammate.Where(x => x.PublicKey == pubKeyHex).ToList()); }
public override void Run(RuleContext context) { var header = context.ValidationContext.ChainedHeaderToValidate.Header as PoABlockHeader; PubKey pubKey = this.slotsManager.GetFederationMemberForBlock(context.ValidationContext.ChainedHeaderToValidate, this.votingManager).PubKey; if (!this.validator.VerifySignature(pubKey, header)) { if (this.votingEnabled) { ChainedHeader currentHeader = context.ValidationContext.ChainedHeaderToValidate; // If we're evaluating a batch of received headers it's possible that we're so far beyond the current tip // that we have not yet processed all the votes that may determine the federation make-up. bool mightBeInsufficient = currentHeader.Height - this.chainState.ConsensusTip.Height > this.maxReorg; if (mightBeInsufficient) { // Mark header as insufficient to avoid banning the peer that presented it. // When we advance consensus we will be able to validate it. context.ValidationContext.InsufficientHeaderInformation = true; } } try { // Gather all past and present mining public keys. IEnumerable <PubKey> genesisFederation = ((PoAConsensusOptions)this.network.Consensus.Options).GenesisFederationMembers.Select(m => m.PubKey); var knownKeys = new HashSet <PubKey>(genesisFederation); foreach (Poll poll in this.votingManager.GetFinishedPolls().Where(x => ((x.VotingData.Key == VoteKey.AddFederationMember) || (x.VotingData.Key == VoteKey.KickFederationMember)))) { IFederationMember federationMember = ((PoAConsensusFactory)(this.network.Consensus.ConsensusFactory)).DeserializeFederationMember(poll.VotingData.Data); knownKeys.Add(federationMember.PubKey); } // Try to provide the public key that signed the block. var signature = ECDSASignature.FromDER(header.BlockSignature.Signature); for (int recId = 0; recId < 4; recId++) { PubKey pubKeyForSig = PubKey.RecoverFromSignature(recId, signature, header.GetHash(), true); if (pubKeyForSig == null) { this.Logger.LogDebug($"Could not match candidate keys to any known key."); break; } this.Logger.LogDebug($"Attempting to match candidate key '{ pubKeyForSig.ToHex() }' to known keys."); if (!knownKeys.Any(pk => pk == pubKeyForSig)) { continue; } IEnumerable <PubKey> modifiedFederation = this.votingManager?.GetModifiedFederation(context.ValidationContext.ChainedHeaderToValidate).Select(m => m.PubKey) ?? genesisFederation; this.Logger.LogDebug($"Block is signed by '{0}' but expected '{1}' from: {2}.", pubKeyForSig.ToHex(), pubKey, string.Join(" ", modifiedFederation.Select(pk => pk.ToHex()))); break; } ; } catch (Exception) { } this.Logger.LogTrace("(-)[INVALID_SIGNATURE]"); PoAConsensusErrors.InvalidHeaderSignature.Throw(); } }
public override async Task RunAsync(RuleContext context) { // Only start validating at the last checkpoint block. if (context.ValidationContext.ChainedHeaderToValidate.Height < (this.lastCheckPoint?.Height ?? 0)) { return; } ChainedHeader chainedHeader = context.ValidationContext.ChainedHeaderToValidate; // If we're evaluating a batch of received headers it's possible that we're so far beyond the current tip // that we have not yet processed all the votes that may determine the federation make-up. if (!this.federationHistory.CanGetFederationForBlock(chainedHeader)) { // Mark header as insufficient to avoid banning the peer that presented it. // When we advance consensus we will be able to validate it. context.ValidationContext.InsufficientHeaderInformation = true; this.Logger.LogWarning("The polls repository is too far behind to reliably determine the federation members."); this.Logger.LogDebug("(-)[INVALID_SIGNATURE]"); PoAConsensusErrors.InvalidHeaderSignature.Throw(); } var header = chainedHeader.Header as PoABlockHeader; PubKey pubKey = this.federationHistory.GetFederationMemberForBlock(chainedHeader)?.PubKey; if (pubKey == null || !this.validator.VerifySignature(pubKey, header)) { this.Logger.LogWarning("The block signature could not be matched with a current federation member."); this.Logger.LogDebug("(-)[INVALID_SIGNATURE]"); PoAConsensusErrors.InvalidHeaderSignature.Throw(); } // Look at the last round of blocks to find the previous time that the miner mined. var roundTime = this.slotsManager.GetRoundLength(this.federationHistory.GetFederationForBlock(chainedHeader).Count); int blockCounter = 0; for (ChainedHeader prevHeader = chainedHeader.Previous; prevHeader.Previous != null; prevHeader = prevHeader.Previous) { blockCounter += 1; if ((header.BlockTime - prevHeader.Header.BlockTime) >= roundTime) { break; } // If the miner is found again within the same round then throw a consensus error. if (this.federationHistory.GetFederationMemberForBlock(prevHeader)?.PubKey != pubKey) { continue; } // Mining slots shift when the federation changes. // Only raise an error if the federation did not change. if (this.slotsManager.GetRoundLength(this.federationHistory.GetFederationForBlock(prevHeader).Count) != roundTime) { break; } if (this.slotsManager.GetRoundLength(this.federationHistory.GetFederationForBlock(prevHeader.Previous).Count) != roundTime) { break; } this.Logger.LogDebug("Block {0} was mined by the same miner '{1}' as {2} blocks ({3})s ago and there was no federation change.", prevHeader.HashBlock, pubKey.ToHex(), blockCounter, header.Time - prevHeader.Header.Time); this.Logger.LogTrace("(-)[TIME_TOO_EARLY]"); ConsensusErrors.BlockTimestampTooEarly.Throw(); } }
/// <summary>Checks that whomever mined this block is participating in any pending polls to vote-in new federation members.</summary> public override Task RunAsync(RuleContext context) { // Determine the members that this node is currently in favor of adding. List <Poll> pendingPolls = this.ruleEngine.VotingManager.GetPendingPolls(); var encoder = new JoinFederationRequestEncoder(this.loggerFactory); IEnumerable <PubKey> newMembers = pendingPolls .Where(p => p.VotingData.Key == VoteKey.AddFederationMember && (p.PollStartBlockData == null || p.PollStartBlockData.Height <= context.ValidationContext.ChainedHeaderToValidate.Height) && p.PubKeysHexVotedInFavor.Any(pk => pk == this.federationManager.CurrentFederationKey.PubKey.ToHex())) .Select(p => ((CollateralFederationMember)this.consensusFactory.DeserializeFederationMember(p.VotingData.Data)).PubKey); if (!newMembers.Any()) { return(Task.CompletedTask); } // Determine who mined the block. PubKey blockMiner = this.slotsManager.GetFederationMemberForBlock(context.ValidationContext.ChainedHeaderToValidate, this.votingManager).PubKey; // Check that the miner is in favor of adding the same member(s). Dictionary <string, bool> checkList = newMembers.ToDictionary(x => x.ToHex(), x => false); foreach (CollateralFederationMember member in pendingPolls .Where(p => p.VotingData.Key == VoteKey.AddFederationMember && p.PubKeysHexVotedInFavor.Any(pk => pk == blockMiner.ToHex())) .Select(p => (CollateralFederationMember)this.consensusFactory.DeserializeFederationMember(p.VotingData.Data))) { checkList[member.PubKey.ToHex()] = true; } if (!checkList.Any(c => !c.Value)) { return(Task.CompletedTask); } // Otherwise check that the miner is including those votes now. Transaction coinbase = context.ValidationContext.BlockToValidate.Transactions[0]; byte[] votingDataBytes = this.votingDataEncoder.ExtractRawVotingData(coinbase); if (votingDataBytes != null) { List <VotingData> votingDataList = this.votingDataEncoder.Decode(votingDataBytes); foreach (VotingData votingData in votingDataList) { var member = (CollateralFederationMember)this.consensusFactory.DeserializeFederationMember(votingData.Data); var expectedCollateralAmount = CollateralFederationMember.GetCollateralAmountForPubKey((PoANetwork)this.network, member.PubKey); // Check collateral amount. if (member.CollateralAmount.ToDecimal(MoneyUnit.BTC) != expectedCollateralAmount) { this.logger.LogTrace("(-)[INVALID_COLLATERAL_REQUIREMENT]"); PoAConsensusErrors.InvalidCollateralRequirement.Throw(); } // Can't be a multisig member. if (member.IsMultisigMember) { this.logger.LogTrace("(-)[INVALID_MULTISIG_VOTING]"); PoAConsensusErrors.VotingRequestInvalidMultisig.Throw(); } checkList[member.PubKey.ToHex()] = true; } } // If any outstanding votes have not been included throw a consensus error. if (checkList.Any(c => !c.Value)) { PoAConsensusErrors.BlockMissingVotes.Throw(); } return(Task.CompletedTask); }
/// <inheritdoc/> public void AddVote(string requestId, BigInteger transactionId, PubKey pubKey) { lock (this.lockObject) { // If the request has not yet been voted on, create a voting list. if (!this.receivedVotes.TryGetValue(requestId, out HashSet <PubKey> voted)) { voted = new HashSet <PubKey>(); } // Check if the pubkey node has voted for this request. if (!voted.Contains(pubKey)) { this.logger.LogDebug("Adding vote for request '{0}' (transactionId '{1}') from pubkey {2}.", requestId, transactionId, pubKey.ToHex()); voted.Add(pubKey); // If the set of active votes does not contain the request, create a new list. if (!this.transactionIdVotes.TryGetValue(requestId, out Dictionary <BigInteger, int> transactionIdVotesForRequestId)) { transactionIdVotesForRequestId = new Dictionary <BigInteger, int>(); } if (!transactionIdVotesForRequestId.ContainsKey(transactionId)) { transactionIdVotesForRequestId[transactionId] = 1; } else { transactionIdVotesForRequestId[transactionId]++; } this.transactionIdVotes[requestId] = transactionIdVotesForRequestId; this.receivedVotes[requestId] = voted; } } }
private void GatherMiningStatistics() { var log = new StringBuilder(); log.AppendLine(">> Miner"); log.AppendLine(); if (!this.federationManager.IsFederationMember) { log.AppendLine("Mining information is not available for non federation members."); log.AppendLine("It is possible that your node was kicked from the federation due to inactivity."); log.AppendLine(); this.miningStatisticsLog = log.ToString(); return; } if (this.ibdState.IsInitialBlockDownload()) { log.AppendLine("Mining information is not available whilst the node is syncing."); log.AppendLine("The node will mine once it reaches the network's height."); log.AppendLine(); this.miningStatisticsLog = log.ToString(); return; } ChainedHeader tip = this.consensusManager.Tip; ChainedHeader currentHeader = tip; int pubKeyTakeCharacters = 5; int hitCount = 0; List <IFederationMember> modifiedFederation = this.federationHistory.GetFederationForBlock(currentHeader); int maxDepth = modifiedFederation.Count; // TODO: Make this a command line option. bool includeHeight = false; log.AppendLine($"Mining information for the last { maxDepth } blocks."); if (includeHeight) { log.AppendLine("Note 'MISS' indicates a slot where a miner didn't produce a block."); } else { log.AppendLine("Note that '<' and '>' surrounds a slot where a miner didn't produce a block."); } uint currentSlotTime = (uint)this.dateTimeProvider.GetAdjustedTimeAsUnixTimestamp(); currentSlotTime -= currentSlotTime % this.network.ConsensusOptions.TargetSpacingSeconds; // Determine the public key of the current slot. string pubKeyRepresentation; // Iterate mining slots. for (int i = 0; i < maxDepth; i++, currentSlotTime -= this.network.ConsensusOptions.TargetSpacingSeconds) { // Find the chained header for this slot. while (currentHeader.Header.Time > currentSlotTime) { currentHeader = currentHeader.Previous; } // Mined in this slot? bool minedInThisSlot = currentHeader.Header.Time == currentSlotTime; PubKey pubKey = (minedInThisSlot ? this.federationHistory.GetFederationMemberForBlock(currentHeader) : DetermineExpectedMinerForTimestamp(currentSlotTime)).PubKey; if (pubKey == this.federationManager.CurrentFederationKey?.PubKey) { pubKeyRepresentation = "█████"; this.miningStatistics.ProducedBlockInLastRound = minedInThisSlot; } else { pubKeyRepresentation = pubKey.ToHex().Substring(0, pubKeyTakeCharacters); } if (includeHeight) { string strHeight = minedInThisSlot ? currentHeader.Height.ToString().PadLeft(7) : "---MISS"; log.Append($"{strHeight}:{ pubKeyRepresentation } "); } else { log.Append(minedInThisSlot ? $"[{pubKeyRepresentation}] " : $"<{pubKeyRepresentation}> "); } if (minedInThisSlot) { hitCount++; } if (((i + 1) % (includeHeight ? 10 : 20)) == 0) { log.AppendLine(); } } this.miningStatistics.MinerHits = hitCount; log.Append("..."); log.AppendLine(); log.AppendLine($"Miner hits".PadRight(LoggingConfiguration.ColumnLength) + $": {hitCount} of {maxDepth}({(((float)hitCount / (float)maxDepth)).ToString("P2")})"); log.AppendLine($"Miner idle time".PadRight(LoggingConfiguration.ColumnLength) + $": { TimeSpan.FromSeconds(this.network.ConsensusOptions.TargetSpacingSeconds * (maxDepth - hitCount)).ToString(@"hh\:mm\:ss")}"); log.AppendLine(); this.miningStatisticsLog = log.ToString(); }
private void ProcessBlock(DBreeze.Transactions.Transaction transaction, ChainedHeaderBlock chBlock) { long flagFall = DateTime.Now.Ticks; try { lock (this.locker) { bool pollsRepositoryModified = false; foreach (Poll poll in this.GetPendingPolls().Where(p => PollsRepository.IsPollExpiredAt(p, chBlock.ChainedHeader, this.network as PoANetwork)).ToList()) { this.logger.LogDebug("Expiring poll '{0}'.", poll); // Flag the poll as expired. The "PollVotedInFavorBlockData" will always be null at this point due to the "GetPendingPolls" filter above. // The value of the hash is not significant but we set it to a non-zero value to prevent the field from being de-serialized as null. poll.IsExpired = true; this.polls.OnPendingStatusChanged(poll); this.PollsRepository.UpdatePoll(transaction, poll); pollsRepositoryModified = true; } foreach (Poll poll in this.GetApprovedPolls()) { if (poll.IsExpired || chBlock.ChainedHeader.Height != (poll.PollVotedInFavorBlockData.Height + this.network.Consensus.MaxReorgLength)) { continue; } this.logger.LogDebug("Applying poll '{0}'.", poll); this.pollResultExecutor.ApplyChange(poll.VotingData); poll.PollExecutedBlockData = new HashHeightPair(chBlock.ChainedHeader); this.PollsRepository.UpdatePoll(transaction, poll); pollsRepositoryModified = true; } if (this.federationManager.GetMultisigMinersApplicabilityHeight() == chBlock.ChainedHeader.Height) { this.federationManager.UpdateMultisigMiners(true); } byte[] rawVotingData = this.votingDataEncoder.ExtractRawVotingData(chBlock.Block.Transactions[0]); if (rawVotingData == null) { this.PollsRepository.SaveCurrentTip(pollsRepositoryModified ? transaction : null, chBlock.ChainedHeader); return; } IFederationMember member = this.federationHistory.GetFederationMemberForBlock(chBlock.ChainedHeader); if (member == null) { this.logger.LogError("The block was mined by a non-federation-member!"); this.logger.LogTrace("(-)[ALIEN_BLOCK]"); this.PollsRepository.SaveCurrentTip(pollsRepositoryModified ? transaction : null, chBlock.ChainedHeader); return; } PubKey fedMemberKey = member.PubKey; string fedMemberKeyHex = fedMemberKey.ToHex(); List <VotingData> votingDataList = this.votingDataEncoder.Decode(rawVotingData); this.logger.LogDebug("Applying {0} voting data items included in a block by '{1}'.", votingDataList.Count, fedMemberKeyHex); lock (this.locker) { foreach (VotingData data in votingDataList) { if (this.federationManager.CurrentFederationKey?.PubKey.ToHex() == fedMemberKeyHex) { // Any votes found in the block is no longer scheduled. // This avoids clinging to votes scheduled during IBD. if (this.scheduledVotingData.Any(v => v == data)) { this.scheduledVotingData.Remove(data); } } if (this.IsVotingOnMultisigMember(data)) { continue; } Poll poll = this.polls.GetPendingPollByVotingData(data); if (poll == null) { // Ensures that highestPollId can't be changed before the poll is committed. this.PollsRepository.Synchronous(() => { poll = new Poll() { Id = this.PollsRepository.GetHighestPollId() + 1, PollVotedInFavorBlockData = null, PollExecutedBlockData = null, PollStartBlockData = new HashHeightPair(chBlock.ChainedHeader), VotingData = data, PubKeysHexVotedInFavor = new List <Vote>() { new Vote() { PubKey = fedMemberKeyHex, Height = chBlock.ChainedHeader.Height } } }; this.polls.Add(poll); this.PollsRepository.AddPolls(transaction, poll); pollsRepositoryModified = true; this.logger.LogDebug("New poll was created: '{0}'.", poll); }); } else if (!poll.PubKeysHexVotedInFavor.Any(v => v.PubKey == fedMemberKeyHex)) { poll.PubKeysHexVotedInFavor.Add(new Vote() { PubKey = fedMemberKeyHex, Height = chBlock.ChainedHeader.Height }); this.PollsRepository.UpdatePoll(transaction, poll); pollsRepositoryModified = true; this.logger.LogDebug("Voted on existing poll: '{0}'.", poll); } else { this.logger.LogDebug("Fed member '{0}' already voted for this poll. Ignoring his vote. Poll: '{1}'.", fedMemberKeyHex, poll); } List <IFederationMember> modifiedFederation = this.federationManager.GetFederationMembers(); var fedMembersHex = new ConcurrentHashSet <string>(modifiedFederation.Select(x => x.PubKey.ToHex())); // Member that were about to be kicked when voting started don't participate. if (this.idleFederationMembersKicker != null) { ChainedHeader chainedHeader = chBlock.ChainedHeader.GetAncestor(poll.PollStartBlockData.Height); if (chainedHeader?.Header == null) { chainedHeader = this.chainIndexer.GetHeader(poll.PollStartBlockData.Hash); if (chainedHeader == null) { this.logger.LogWarning("Couldn't retrieve header for block at height-hash: {0}-{1}.", poll.PollStartBlockData.Height, poll.PollStartBlockData.Hash?.ToString()); Guard.NotNull(chainedHeader, nameof(chainedHeader)); Guard.NotNull(chainedHeader.Header, nameof(chainedHeader.Header)); } } foreach (IFederationMember miner in modifiedFederation) { if (this.idleFederationMembersKicker.ShouldMemberBeKicked(miner, chainedHeader, chBlock.ChainedHeader, out _)) { fedMembersHex.TryRemove(miner.PubKey.ToHex()); } } } // It is possible that there is a vote from a federation member that was deleted from the federation. // Do not count votes from entities that are not active fed members. int validVotesCount = poll.PubKeysHexVotedInFavor.Count(x => fedMembersHex.Contains(x.PubKey)); int requiredVotesCount = (fedMembersHex.Count / 2) + 1; this.logger.LogDebug("Fed members count: {0}, valid votes count: {1}, required votes count: {2}.", fedMembersHex.Count, validVotesCount, requiredVotesCount); if (validVotesCount < requiredVotesCount) { continue; } poll.PollVotedInFavorBlockData = new HashHeightPair(chBlock.ChainedHeader); this.polls.OnPendingStatusChanged(poll); this.PollsRepository.UpdatePoll(transaction, poll); pollsRepositoryModified = true; } } this.PollsRepository.SaveCurrentTip(pollsRepositoryModified ? transaction : null, chBlock.ChainedHeader); } } catch (Exception ex) { this.logger.LogError(ex, ex.ToString()); throw; } finally { long timeConsumed = DateTime.Now.Ticks - flagFall; this.blocksProcessed++; this.blocksProcessingTime += timeConsumed; } }
public void key_test1() { BitcoinSecret bsecret1 = Network.Main.CreateBitcoinSecret(strSecret1); BitcoinSecret bsecret2 = Network.Main.CreateBitcoinSecret(strSecret2); BitcoinSecret bsecret1C = Network.Main.CreateBitcoinSecret(strSecret1C); BitcoinSecret bsecret2C = Network.Main.CreateBitcoinSecret(strSecret2C); Assert.Throws <FormatException>(() => Network.Main.CreateBitcoinSecret(strAddressBad)); Key key1 = bsecret1.PrivateKey; Assert.True(key1.IsCompressed == false); Assert.True(bsecret1.Copy(true).PrivateKey.IsCompressed == true); Assert.True(bsecret1.Copy(true).Copy(false).IsCompressed == false); Assert.True(bsecret1.Copy(true).Copy(false).ToString() == bsecret1.ToString()); Key key2 = bsecret2.PrivateKey; Assert.True(key2.IsCompressed == false); Key key1C = bsecret1C.PrivateKey; Assert.True(key1C.IsCompressed == true); Key key2C = bsecret2C.PrivateKey; Assert.True(key1C.IsCompressed == true); PubKey pubkey1 = key1.PubKey; PubKey pubkey2 = key2.PubKey; PubKey pubkey1C = key1C.PubKey; PubKey pubkey2C = key2C.PubKey; Assert.True(addr1.Hash == pubkey1.Hash); Assert.True(addr2.Hash == pubkey2.Hash); Assert.True(addr1C.Hash == pubkey1C.Hash); Assert.True(addr2C.Hash == pubkey2C.Hash); for (int n = 0; n < 16; n++) { string strMsg = String.Format("Very secret message {0}: 11", n); if (n == 10) { //Test one long message strMsg = String.Join(",", Enumerable.Range(0, 2000).Select(i => i.ToString()).ToArray()); } uint256 hashMsg = Hashes.DoubleSHA256(TestUtils.ToBytes(strMsg)); // normal signatures ECDSASignature sign1 = null, sign2 = null, sign1C = null, sign2C = null; List <Task> tasks = new List <Task>(); sign1 = key1.Sign(hashMsg); sign2 = key2.Sign(hashMsg); sign1C = key1C.Sign(hashMsg); sign2C = key2C.Sign(hashMsg); for (int i = 0; i < 30; i++) { Assert.True(pubkey1.Verify(hashMsg, sign1)); Assert.True(pubkey2.Verify(hashMsg, sign2)); Assert.True(pubkey1C.Verify(hashMsg, sign1C)); Assert.True(pubkey2C.Verify(hashMsg, sign2C)); Assert.True(pubkey1.Verify(hashMsg, sign1)); Assert.True(!pubkey1.Verify(hashMsg, sign2)); Assert.True(pubkey1.Verify(hashMsg, sign1C)); Assert.True(!pubkey1.Verify(hashMsg, sign2C)); Assert.True(!pubkey2.Verify(hashMsg, sign1)); Assert.True(pubkey2.Verify(hashMsg, sign2)); Assert.True(!pubkey2.Verify(hashMsg, sign1C)); Assert.True(pubkey2.Verify(hashMsg, sign2C)); Assert.True(pubkey1C.Verify(hashMsg, sign1)); Assert.True(!pubkey1C.Verify(hashMsg, sign2)); Assert.True(pubkey1C.Verify(hashMsg, sign1C)); Assert.True(!pubkey1C.Verify(hashMsg, sign2C)); Assert.True(!pubkey2C.Verify(hashMsg, sign1)); Assert.True(pubkey2C.Verify(hashMsg, sign2)); Assert.True(!pubkey2C.Verify(hashMsg, sign1C)); Assert.True(pubkey2C.Verify(hashMsg, sign2C)); } // compact signatures (with key recovery) byte[] csign1 = null, csign2 = null, csign1C = null, csign2C = null; csign1 = key1.SignCompact(hashMsg); csign2 = key2.SignCompact(hashMsg); csign1C = key1C.SignCompact(hashMsg); csign2C = key2C.SignCompact(hashMsg); PubKey rkey1 = null, rkey2 = null, rkey1C = null, rkey2C = null; rkey1 = PubKey.RecoverCompact(hashMsg, csign1); rkey2 = PubKey.RecoverCompact(hashMsg, csign2); rkey1C = PubKey.RecoverCompact(hashMsg, csign1C); rkey2C = PubKey.RecoverCompact(hashMsg, csign2C); Assert.True(rkey1.ToHex() == pubkey1.ToHex()); Assert.True(rkey2.ToHex() == pubkey2.ToHex()); Assert.True(rkey1C.ToHex() == pubkey1C.ToHex()); Assert.True(rkey2C.ToHex() == pubkey2C.ToHex()); Assert.True(sign1.IsLowR && sign1.ToDER().Length <= 70); Assert.True(sign2.IsLowR && sign2.ToDER().Length <= 70); Assert.True(sign1C.IsLowR && sign1C.ToDER().Length <= 70); Assert.True(sign2C.IsLowR && sign2C.ToDER().Length <= 70); } }