Skip to content

Entropy (η)

Bases: StateComponent

Source code in pyjamaz/state/components.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
class Entropy(StateComponent):
    component_id = 6

    @log_execution_time
    def state_transition(
            self,
            header: Header,
            pre_state_timeslot: TimeslotState,
            pre_state_entropy: EntropyState
    ) -> EntropyOutput:
        """
        GP-0.7.2-eq:6.22,6.23 (η') | State transition function for the state's entropy.

        Parameters
        ----------
        header: Header
            GP-0.7.2-eq:4.8 (bold_H)
        pre_state_timeslot: TimeslotState
            GP-0.7.2-eq:4.8 (τ)
        pre_state_entropy: EntropyState
            GP-0.7.2-eq:4.8 (η)

        Returns
        -------
        EntropyOutput
            Output containing: posterior state of EntropyState (η')
        """

        post_state_entropy = deepcopy(pre_state_entropy)

        # GP-0.7.2-eq:6.22 (η'[0]) | State transition for first index of the entropy.
        eta_0 = blake2b_256_hash(pre_state_entropy.entropy[0] + self.entropy_output(header))

        # GP-0.7.2-eq:6.23 (η'[1-3]) | State transition for last three indices of the entropy.
        # State transition happen on epoch change.
        if self.is_epoch_change(pre_state_timeslot.number, header.timeslot):
            # GP-0.7.2-eq:6.23 (`e > e'`) | When epoch changes
            post_state_entropy.entropy = [eta_0] + pre_state_entropy.entropy[:3]
        else:
            post_state_entropy.entropy = [eta_0] + pre_state_entropy.entropy[1:]

        return EntropyOutput(
            post_state=post_state_entropy
        )

    def retrieve_state(self) -> EntropyState:
        value = self.retrieve()
        return EntropyState.from_jam_bytes(JamBytes(value))

    @log_execution_time
    def entropy_output(self, header: Header) -> bytes:
        """
        GP-0.7.2-eq:G.5
        TODO check if output is indeed the first 32 bytes or a hash of the first 32 bytes
        TODO refactor to vrf_output of entropy signature
        Parameters
        ----------
        header: Header

        Returns
        -------
        bytes
        """

        if len(header.entropy_source) == 32:
            return header.entropy_source

        if header.author_bandersnatch_key is None or self.block_context.seal_vrf_output == bytes(96):
            return bytes(32)

        DEBUG and logging.debug(f"Verifying entropy source signature: bs_key={format_hash(bytes(header.author_bandersnatch_key))} vrf_output={format_hash(self.block_context.seal_vrf_output)}")
        try:
            return ietf_vrf_verify(
                bytes(header.author_bandersnatch_key),
                b"jam_entropy" + self.block_context.seal_vrf_output,
                b'',
                bytes(header.entropy_source)
            )
        except ValueError:
            raise BlockValidationError("Invalid entropy source signature")

state_transition(header: Header, pre_state_timeslot: TimeslotState, pre_state_entropy: EntropyState) -> EntropyOutput

GP-0.7.2-eq:6.22,6.23 (η') | State transition function for the state's entropy.

Parameters:

Name Type Description Default
header Header

GP-0.7.2-eq:4.8 (bold_H)

required
pre_state_timeslot TimeslotState

GP-0.7.2-eq:4.8 (τ)

required
pre_state_entropy EntropyState

GP-0.7.2-eq:4.8 (η)

required

Returns:

Type Description
EntropyOutput

Output containing: posterior state of EntropyState (η')

Source code in pyjamaz/state/components.py
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
@log_execution_time
def state_transition(
        self,
        header: Header,
        pre_state_timeslot: TimeslotState,
        pre_state_entropy: EntropyState
) -> EntropyOutput:
    """
    GP-0.7.2-eq:6.22,6.23 (η') | State transition function for the state's entropy.

    Parameters
    ----------
    header: Header
        GP-0.7.2-eq:4.8 (bold_H)
    pre_state_timeslot: TimeslotState
        GP-0.7.2-eq:4.8 (τ)
    pre_state_entropy: EntropyState
        GP-0.7.2-eq:4.8 (η)

    Returns
    -------
    EntropyOutput
        Output containing: posterior state of EntropyState (η')
    """

    post_state_entropy = deepcopy(pre_state_entropy)

    # GP-0.7.2-eq:6.22 (η'[0]) | State transition for first index of the entropy.
    eta_0 = blake2b_256_hash(pre_state_entropy.entropy[0] + self.entropy_output(header))

    # GP-0.7.2-eq:6.23 (η'[1-3]) | State transition for last three indices of the entropy.
    # State transition happen on epoch change.
    if self.is_epoch_change(pre_state_timeslot.number, header.timeslot):
        # GP-0.7.2-eq:6.23 (`e > e'`) | When epoch changes
        post_state_entropy.entropy = [eta_0] + pre_state_entropy.entropy[:3]
    else:
        post_state_entropy.entropy = [eta_0] + pre_state_entropy.entropy[1:]

    return EntropyOutput(
        post_state=post_state_entropy
    )

entropy_output(header: Header) -> bytes

GP-0.7.2-eq:G.5 TODO check if output is indeed the first 32 bytes or a hash of the first 32 bytes TODO refactor to vrf_output of entropy signature

Parameters:

Name Type Description Default
header Header
required

Returns:

Type Description
bytes
Source code in pyjamaz/state/components.py
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
@log_execution_time
def entropy_output(self, header: Header) -> bytes:
    """
    GP-0.7.2-eq:G.5
    TODO check if output is indeed the first 32 bytes or a hash of the first 32 bytes
    TODO refactor to vrf_output of entropy signature
    Parameters
    ----------
    header: Header

    Returns
    -------
    bytes
    """

    if len(header.entropy_source) == 32:
        return header.entropy_source

    if header.author_bandersnatch_key is None or self.block_context.seal_vrf_output == bytes(96):
        return bytes(32)

    DEBUG and logging.debug(f"Verifying entropy source signature: bs_key={format_hash(bytes(header.author_bandersnatch_key))} vrf_output={format_hash(self.block_context.seal_vrf_output)}")
    try:
        return ietf_vrf_verify(
            bytes(header.author_bandersnatch_key),
            b"jam_entropy" + self.block_context.seal_vrf_output,
            b'',
            bytes(header.entropy_source)
        )
    except ValueError:
        raise BlockValidationError("Invalid entropy source signature")