// We need to handle this message carefully like how about decoding a random message with random length // and the value of 'share.ShareId' needs to be checked. If it is out of range, it can throw exception private void HandleDecryptedMessage(TPKEPartiallyDecryptedShareMessage msg, int senderId) { PartiallyDecryptedShare?share = null; try { // Converting any random bytes to G1 is not possible share = Wallet.TpkePublicKey.Decode(msg); _decryptedShares[share.ShareId].Add(share); } catch (Exception exception) { var pubKey = Broadcaster.GetPublicKeyById(senderId) !.ToHex(); Logger.LogWarning($"Exception occured handling Decrypted message: {msg} from {senderId} ({pubKey})"); } if (!(share is null)) { CheckDecryptedShares(share.ShareId); } }
private void HandleValMessage(ValMessage val, int validator) { var validatorPubKey = Wallet.EcdsaPublicKeySet[validator].ToHex(); if (_sentValMessage[validator]) { Logger.LogWarning($"{Id}: validator {validator} ({validatorPubKey}) tried to send VAL message twice"); return; } Logger.LogTrace( $"Protocol {Id} got VAL message from {validator} ({validatorPubKey}), sending ECHO" ); _sentValMessage[validator] = true; // Before sending echo, we can check if validator == val.SenderId, means if the val message is from the correct validator // Because one validator cannot produce val message of another validator, it can only send echo. // If we don't check this condition, there could be potential issue, for example, a malicious validator (id = x) sends a // val message that has random shards but correct MerkleProof and uses val.SenderId = y (another validator), and sends to // validator with id = z. It will be constructed as echo message and sent to everyone by validator, id = z, this echo will // pass the CheckEchoMessage(). Now every honest validator will think that val message of validator of id = y is confirmed // by echo message from validator of id = z. When the correct val message of id = y will come to id = z, he will send echo // again but others will not accept it, because id = z already sent echo for id = y, (but it was from malicious id = x), // because the correct echo for each pair is received only once. if (validator == val.SenderId) { Broadcaster.Broadcast(CreateEchoMessage(val)); } else { var pubKey = Broadcaster.GetPublicKeyById(validator) !.ToHex(); Logger.LogWarning( $"Faulty behaviour: val message with sender id: {val.SenderId} came from validator: " + $"{validator} ({pubKey}), which should not happen. Val message for {val.SenderId} should come " + $"from {val.SenderId}. Not sending echo message for this val message"); } }
public override void ProcessMessage(MessageEnvelope envelope) { if (envelope.External) { var message = envelope.ExternalMessage; if (message is null) { _lastMessage = "Failed to decode external message"; throw new ArgumentNullException(); } // These checks are somewhat redundant, but whatever if (message.PayloadCase != ConsensusMessage.PayloadOneofCase.Coin) { _lastMessage = $"consensus message of type {message.PayloadCase} routed to CommonCoin protocol"; throw new ArgumentException( $"consensus message of type {message.PayloadCase} routed to CommonCoin protocol"); } if (message.Validator.Era != _coinId.Era || message.Coin.Agreement != _coinId.Agreement || message.Coin.Epoch != _coinId.Epoch) { _lastMessage = $"era, agreement or epoch of message mismatched: message({message.Validator.Era}, " + $"{message.Coin.Agreement}, {message.Coin.Epoch}), coin ({_coinId.Era}, " + $"{_coinId.Agreement}, {_coinId.Epoch})"; throw new ArgumentException("era, agreement or epoch of message mismatched"); } // To create signature from the message, some requirements need to be fulfilled, otherwise it can // throw exception (for example maybe a fixed length of the input bytes or maybe valid array of bytes) try { Logger.LogTrace($"Received share from {envelope.ValidatorIndex}"); _lastMessage = $"Received share from {envelope.ValidatorIndex}"; var signatureShare = Signature.FromBytes(message.Coin.SignatureShare.ToByteArray()); if (!_thresholdSigner.AddShare(envelope.ValidatorIndex, signatureShare, out var signature)) { _lastMessage = $"Faulty behaviour from player {envelope.ValidatorIndex}, {message.PrettyTypeString()}, {message.Coin.SignatureShare.ToByteArray().ToHex()}: bad signature share"; Logger.LogWarning( $"Faulty behaviour from player {envelope.ValidatorIndex}, {message.PrettyTypeString()}, {message.Coin.SignatureShare.ToByteArray().ToHex()}: bad signature share"); return; // potential fault evidence } if (signature == null) { _lastMessage = "signature == null"; return; } _result = new CoinResult(signature.RawSignature.ToBytes()); } catch (Exception exception) { var pubKey = Broadcaster.GetPublicKeyById(envelope.ValidatorIndex) !.ToHex(); Logger.LogWarning( $"Exception occured while handling message from validator {envelope.ValidatorIndex} " + $"({pubKey}). Exception: {exception}"); } CheckResult(); } else { var message = envelope.InternalMessage; if (message is null) { _lastMessage = "Failed to decode internal message"; throw new ArgumentNullException(); } switch (message) { case ProtocolRequest <CoinId, object?> _: _lastMessage = "ProtocolRequest"; var signatureShare = _thresholdSigner.Sign(); _requested = ResultStatus.Requested; CheckResult(); var msg = CreateCoinMessage(signatureShare); Broadcaster.Broadcast(msg); break; case ProtocolResult <CoinId, CoinResult> _: _lastMessage = "ProtocolResult"; Terminate(); break; default: _lastMessage = $"Binary broadcast protocol handles messages of type {message.GetType()}"; throw new InvalidOperationException( $"Binary broadcast protocol handles messages of type {message.GetType()}"); } } }
public override void ProcessMessage(MessageEnvelope envelope) { if (envelope.External) { Logger.LogTrace("External envelope"); var message = envelope.ExternalMessage; if (message is null) { _lastMessage = "Failed to decode external message"; throw new Exception("impossible"); } if (message.PayloadCase != ConsensusMessage.PayloadOneofCase.SignedHeaderMessage) { _lastMessage = $"RootProtocol does not accept messages of type {message.PayloadCase}"; throw new InvalidOperationException( $"RootProtocol does not accept messages of type {message.PayloadCase}" ); } _lastMessage = "SignedHeaderMessage"; var signedHeaderMessage = message.SignedHeaderMessage; var idx = envelope.ValidatorIndex; Logger.LogTrace( $"Received signature of header {signedHeaderMessage.Header.Keccak().ToHex()} " + $"from validator {idx}: " + $"pubKey {Wallet.EcdsaPublicKeySet[idx].EncodeCompressed().ToHex()}" ); if (!(_header is null) && !_header.Equals(signedHeaderMessage.Header)) { Logger.LogWarning($"Received incorrect block header from validator {idx}"); Logger.LogWarning($"Header we have {_header}"); Logger.LogWarning($"Header we received {signedHeaderMessage.Header}"); } // Random message can raise exception like recover id in signature verification can be out of range or // public key cannot be serialized var verified = false; try { verified = _crypto.VerifySignatureHashed( signedHeaderMessage.Header.Keccak().ToBytes(), signedHeaderMessage.Signature.Encode(), Wallet.EcdsaPublicKeySet[idx].EncodeCompressed(), _useNewChainId ); } catch (Exception exception) { var pubKey = Broadcaster.GetPublicKeyById(idx) !.ToHex(); Logger.LogWarning($"Faulty behaviour: exception occured trying to verify SignedHeaderMessage " + $"from {idx} ({pubKey}): {exception}"); } if (!verified) { _lastMessage = $"Incorrect signature of header {signedHeaderMessage.Header.Keccak().ToHex()} from validator {idx}"; Logger.LogWarning( $"Incorrect signature of header {signedHeaderMessage.Header.Keccak().ToHex()} from validator {idx}" ); } else { Logger.LogTrace("Add signatures"); _lastMessage = "Add signatures"; _signatures.Add(new Tuple <BlockHeader, MultiSig.Types.SignatureByValidator>( signedHeaderMessage.Header, new MultiSig.Types.SignatureByValidator { Key = Wallet.EcdsaPublicKeySet[idx], Value = signedHeaderMessage.Signature, } ) ); var validatorAttendance = GetOrCreateValidatorAttendance(message.SignedHeaderMessage.Header.Index); validatorAttendance !.IncrementAttendanceForCycle(Wallet.EcdsaPublicKeySet[idx].EncodeCompressed(), message.SignedHeaderMessage.Header.Index / _cycleDuration); _validatorAttendanceRepository.SaveState(validatorAttendance.ToBytes()); } CheckSignatures(); } else { Logger.LogTrace("Internal envelop"); var message = envelope.InternalMessage; switch (message) { case ProtocolRequest <RootProtocolId, IBlockProducer> request: Logger.LogTrace("request"); _lastMessage = "ProtocolRequest"; _blockProducer = request.Input; using (var stream = new MemoryStream()) { var data = _blockProducer.GetTransactionsToPropose(Id.Era).ToByteArray(); Broadcaster.InternalRequest(new ProtocolRequest <HoneyBadgerId, IRawShare>( Id, new HoneyBadgerId(Id.Era), new RawShare(data, GetMyId())) ); } Broadcaster.InternalRequest(new ProtocolRequest <CoinId, object?>( Id, new CoinId(Id.Era, -1, 0), null )); TrySignHeader(); CheckSignatures(); break; case ProtocolResult <CoinId, CoinResult> coinResult: _nonce = GetNonceFromCoin(coinResult.Result); Logger.LogTrace($"Received coin for block nonce: {_nonce}"); _lastMessage = $"Received coin for block nonce: {_nonce}"; TrySignHeader(); CheckSignatures(); break; case ProtocolResult <HoneyBadgerId, ISet <IRawShare> > result: Logger.LogTrace($"Received shares {result.Result.Count} from HoneyBadger"); _lastMessage = $"Received shares {result.Result.Count} from HoneyBadger"; var rawShares = result.Result.ToArray(); var receipts = new List <TransactionReceipt>(); foreach (var rawShare in rawShares) { // this rawShare may be malicious // we may need to discard this if any issue arises during decoding try { var contributions = rawShare.ToBytes().ToMessageArray <TransactionReceipt>(); foreach (var receipt in contributions) { receipts.Add(receipt); } } catch (Exception e) { Logger.LogError($"Skipped a rawShare due to exception: {e.Message}"); Logger.LogError($"One of the validators might be malicious!!!"); } } _receipts = receipts.Distinct().ToArray(); Logger.LogTrace($"Collected {_receipts.Length} transactions in total"); TrySignHeader(); CheckSignatures(); break; case ProtocolResult <RootProtocolId, object?> _: Logger.LogTrace("Terminate in switch"); _lastMessage = "Terminate in switch"; Terminate(); break; default: _lastMessage = "Invalid message"; Logger.LogError("Invalid message"); throw new ArgumentOutOfRangeException(nameof(message)); } } }