import { BinaryReader , BinaryWriter , common , createSerializeWire , UInt256 } from '@neo-one/client-common' ; import { BN } from 'bn.js' ; import _ from 'lodash' ; import { ConsensusContext } from '../../consensus/ConsensusContext' ; import { DeserializeWireBaseOptions , DeserializeWireOptions } from '../../Serializable' ; import { ProtocolSettings } from '../../Settings' ; import { ChangeViewPayloadCompact } from '../ChangeViewPayloadCompact' ; import { CommitPayloadCompact } from '../CommitPayloadCompact' ; import { ExtensiblePayload } from '../ExtensiblePayload' ; import { PreparationPayloadCompact } from '../PreparationPayloadCompact' ; import { ChangeViewConsensusMessage } from './ChangeViewConsensusMessage' ; import { ChangeViewReason } from './ChangeViewReason' ; import { CommitConsensusMessage } from './CommitConsensusMessage' ; import { ConsensusMessageBase , ConsensusMessageBaseAdd } from './ConsensusMessageBase' ; import { ConsensusMessageType } from './ConsensusMessageType' ; import { PrepareRequestConsensusMessage } from './PrepareRequestConsensusMessage' ; import { PrepareResponseConsensusMessage } from './PrepareResponseConsensusMessage' ; export interface RecoveryConsensusMessageAdd extends ConsensusMessageBaseAdd { readonly prepareRequestMessage ? : PrepareRequestConsensusMessage ; readonly preparationHash ? : UInt256 ; readonly changeViewMessages : _ . NumericDictionary < ChangeViewPayloadCompact > ; readonly preparationMessages : _ . NumericDictionary < PreparationPayloadCompact > ; readonly commitMessages : _ . NumericDictionary < CommitPayloadCompact > ; } export class RecoveryConsensusMessage extends ConsensusMessageBase { public static deserializeWireBase ( options ) { const { reader } = options ; const { viewNumber , blockIndex , validatorIndex } = super . deserializeConsensusMessageBaseWireBase ( options ) ; const changeViewMessagesIn = reader . readArray ( ( ) => ChangeViewPayloadCompact . deserializeWireBase ( options ) , common . BYTE_MAX_VALUE ) ; let prepareRequestMessage ; let preparationHash ; if ( reader . readBoolean ( ) ) { prepareRequestMessage = PrepareRequestConsensusMessage . deserializeWireBase ( options ) ; } else if ( reader . readVarUIntLE ( new BN ( common . UINT256_BUFFER_BYTES ) ) . toNumber ( ) === common . UINT256_BUFFER_BYTES ) { preparationHash = reader . readUInt256 ( ) ; } const preparationMessagesIn = reader . readArray ( ( ) => PreparationPayloadCompact . deserializeWireBase ( options ) , common . BYTE_MAX_VALUE ) ; const commitMessagesIn = reader . readArray ( ( ) => CommitPayloadCompact . deserializeWireBase ( options ) , common . BYTE_MAX_VALUE ) ; const changeViewMessages = _ . fromPairs ( changeViewMessagesIn . map ( ( changeView ) => [ changeView . validatorIndex , changeView ] ) ) ; const preparationMessages = _ . fromPairs ( preparationMessagesIn . map ( ( preparation ) => [ preparation . validatorIndex , preparation ] ) ) ; const commitMessages = _ . fromPairs ( commitMessagesIn . map ( ( commit ) => [ commit . validatorIndex , commit ] ) ) ; return new RecoveryConsensusMessage ( { viewNumber , blockIndex , validatorIndex , prepareRequestMessage , preparationHash , changeViewMessages , preparationMessages , commitMessages } ) ; } public static deserializeWire ( options ) { return this . deserializeWireBase ( { context : options . context , reader : new BinaryReader ( options . buffer ) , validatorsCount : options . validatorsCount } ) ; } public readonly prepareRequestMessage ? : PrepareRequestConsensusMessage ; public readonly preparationHash ? : UInt256 ; public readonly changeViewMessages : _ . NumericDictionary < ChangeViewPayloadCompact > ; public readonly preparationMessages : _ . NumericDictionary < PreparationPayloadCompact > ; public readonly commitMessages : _ . NumericDictionary < CommitPayloadCompact > ; public readonly serializeWire = createSerializeWire ( this . serializeWireBase . bind ( this ) ) ; public constructor ( { viewNumber , blockIndex , validatorIndex , prepareRequestMessage , preparationHash , changeViewMessages , preparationMessages , commitMessages , } ) { super ( { type : ConsensusMessageType . RecoveryMessage , viewNumber , blockIndex , validatorIndex } ) ; this . prepareRequestMessage = prepareRequestMessage ; this . preparationHash = preparationHash ; this . changeViewMessages = changeViewMessages ; this . preparationMessages = preparationMessages ; this . commitMessages = commitMessages ; } public serializeWireBase ( writer ) { super . serializeWireBase ( writer ) ; writer . writeArray ( Object . values ( this . changeViewMessages ) , ( message ) => message . serializeWireBase ( writer ) ) ; writer . writeBoolean ( this . prepareRequestMessage !== undefined ) ; if ( this . prepareRequestMessage !== undefined ) { this . prepareRequestMessage . serializeWireBase ( writer ) ; } else if ( this . preparationHash === undefined ) { writer . writeVarUIntLE ( 0 ) ; } else { writer . writeVarBytesLE ( this . preparationHash ) ; } writer . writeArray ( Object . values ( this . preparationMessages ) , ( message ) => message . serializeWireBase ( writer ) ) ; writer . writeArray ( Object . values ( this . commitMessages ) , ( message ) => message . serializeWireBase ( writer ) ) ; } public getChangeViewPayloads ( context , network ) { return Object . values ( this . changeViewMessages ) . map ( ( item ) => context . createPayload ( new ChangeViewConsensusMessage ( { blockIndex : this . blockIndex , validatorIndex : item . validatorIndex , viewNumber : item . originalViewNumber , timestamp : item . timestamp , reason : ChangeViewReason . Timeout } ) , network , item . invocationScript ) ) ; } public getCommitPayloadsFromRecoveryMessage ( context , network ) { return Object . values ( this . commitMessages ) . map ( ( item ) => context . createPayload ( new CommitConsensusMessage ( { blockIndex : this . blockIndex , validatorIndex : item . validatorIndex , viewNumber : item . viewNumber , signature : item . signature } ) , network , item . invocationScript ) ) ; } public getPrepareRequestPayload ( context , network ) { if ( this . prepareRequestMessage === undefined ) { return undefined ; } const index = context . blockBuilder . primaryIndex ; const maybePreparationMessage = this . preparationMessages [ index ] ; if ( maybePreparationMessage === undefined ) { return undefined ; } return context . createPayload ( this . prepareRequestMessage , network , maybePreparationMessage . invocationScript ) ; } public getPrepareResponsePayloads ( context , network ) { const index = context . blockBuilder . primaryIndex ; const preparationHash = this . preparationHash ?? context . preparationPayloads [ index ] ?. hash ; if ( preparationHash === undefined ) { return [ ] ; } return Object . values ( this . preparationMessages ) . filter ( ( item ) => item . validatorIndex !== index ) . map ( ( item ) => context . createPayload ( new PrepareResponseConsensusMessage ( { blockIndex : this . blockIndex , validatorIndex : item . validatorIndex , viewNumber : this . viewNumber , preparationHash } ) , network , item . invocationScript ) ) ; } public verify ( protocolSettings ) { if ( ! super . verify ( protocolSettings ) ) { return false ; } return ( ( this . prepareRequestMessage === undefined || this . prepareRequestMessage . verify ( protocolSettings ) ) && Object . values ( this . changeViewMessages ) . every ( ( p ) => p . validatorIndex < protocolSettings . validatorsCount ) && Object . values ( this . preparationMessages ) . every ( ( p ) => p . validatorIndex < protocolSettings . validatorsCount ) && Object . values ( this . commitMessages ) . every ( ( p ) => p . validatorIndex < protocolSettings . validatorsCount ) ) ; } }