예제 #1
0
        // 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);
            }
        }
예제 #2
0
        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");
            }
        }
예제 #3
0
        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()}");
                }
            }
        }
예제 #4
0
        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));
                }
            }
        }