12
12
# See the License for the specific language governing permissions and
13
13
# limitations under the License.
14
14
15
+ import hashlib
15
16
from abc import ABC , abstractmethod
16
17
from enum import Enum , unique
17
18
from typing import Annotated , Any , Literal , TypeAlias
18
19
19
- from pydantic import Field , NonNegativeInt , validator
20
+ from pydantic import Field , NonNegativeInt , PrivateAttr , validator
20
21
from typing_extensions import override
21
22
22
23
from hathor .transaction import TxVersion
24
+ from hathor .util import json_dumpb
23
25
from hathor .utils .pydantic import BaseModel
24
26
25
27
@@ -31,6 +33,7 @@ class ConsensusType(str, Enum):
31
33
32
34
class _BaseConsensusSettings (ABC , BaseModel ):
33
35
type : ConsensusType
36
+ _peer_hello_hash : str | None = PrivateAttr (default = None )
34
37
35
38
def is_pow (self ) -> bool :
36
39
"""Return whether this is a Proof-of-Work consensus."""
@@ -49,6 +52,16 @@ def is_vertex_version_valid(self, version: TxVersion, include_genesis: bool = Fa
49
52
"""Return whether a `TxVersion` is valid for this consensus type."""
50
53
return version in self ._get_valid_vertex_versions (include_genesis )
51
54
55
+ def get_peer_hello_hash (self ) -> str | None :
56
+ """Return a hash of consensus settings to be used in peer hello validation."""
57
+ if self ._peer_hello_hash is None :
58
+ self ._peer_hello_hash = self ._calculate_peer_hello_hash ()
59
+ return self ._peer_hello_hash
60
+
61
+ def _calculate_peer_hello_hash (self ) -> str | None :
62
+ """Calculate a hash of consensus settings to be used in peer hello validation."""
63
+ return None
64
+
52
65
53
66
class PowSettings (_BaseConsensusSettings ):
54
67
type : Literal [ConsensusType .PROOF_OF_WORK ] = ConsensusType .PROOF_OF_WORK
@@ -62,6 +75,10 @@ def _get_valid_vertex_versions(self, include_genesis: bool) -> set[TxVersion]:
62
75
TxVersion .MERGE_MINED_BLOCK
63
76
}
64
77
78
+ @override
79
+ def get_peer_hello_hash (self ) -> str | None :
80
+ return None
81
+
65
82
66
83
class PoaSignerSettings (BaseModel ):
67
84
public_key : bytes
@@ -86,6 +103,13 @@ def _validate_end_height(cls, end_height: int | None, values: dict[str, Any]) ->
86
103
87
104
return end_height
88
105
106
+ def to_json_dict (self ) -> dict [str , Any ]:
107
+ """Return this signer settings instance as a json dict."""
108
+ json_dict = self .dict ()
109
+ # TODO: We can use a custom serializer to convert bytes to hex when we update to Pydantic V2.
110
+ json_dict ['public_key' ] = self .public_key .hex ()
111
+ return json_dict
112
+
89
113
90
114
class PoaSettings (_BaseConsensusSettings ):
91
115
type : Literal [ConsensusType .PROOF_OF_AUTHORITY ] = ConsensusType .PROOF_OF_AUTHORITY
@@ -114,5 +138,12 @@ def _get_valid_vertex_versions(self, include_genesis: bool) -> set[TxVersion]:
114
138
115
139
return versions
116
140
141
+ @override
142
+ def _calculate_peer_hello_hash (self ) -> str | None :
143
+ data = b''
144
+ for signer in self .signers :
145
+ data += json_dumpb (signer .to_json_dict ())
146
+ return hashlib .sha256 (data ).digest ().hex ()
147
+
117
148
118
149
ConsensusSettings : TypeAlias = Annotated [PowSettings | PoaSettings , Field (discriminator = 'type' )]
0 commit comments