1
1
from cytoolz import (
2
+ assoc ,
3
+ complement ,
2
4
compose ,
3
5
curry ,
4
6
dissoc ,
5
7
)
6
8
from eth_utils .curried import (
7
- apply_formatter_at_index ,
9
+ apply_formatter_if ,
8
10
apply_formatters_to_dict ,
11
+ is_null ,
12
+ )
13
+ from eth_utils .decorators import (
14
+ return_arg_type ,
15
+ )
16
+ from eth_utils .functional import (
17
+ to_dict ,
18
+ )
19
+ from hexbytes import (
20
+ HexBytes ,
9
21
)
10
22
11
23
from web3 .exceptions import (
12
24
ValidationError ,
13
25
)
14
26
27
+ is_not_null = complement (is_null )
28
+
15
29
30
+ # TODO: move to utils
31
+ def construct_validation_middleware (request_validators = None , result_validators = None ):
32
+ if request_validators is None :
33
+ request_validators = {}
34
+ if result_validators is None :
35
+ result_validators = {}
36
+
37
+ def formatter_middleware (make_request , web3 ):
38
+ def middleware (method , params ):
39
+ if method in request_validators :
40
+ formatter = request_validators [method ]
41
+ formatted_params = formatter (web3 , params )
42
+ response = make_request (method , formatted_params )
43
+ else :
44
+ response = make_request (method , params )
45
+ if 'result' in response and method in result_validators :
46
+ formatter = result_validators [method ]
47
+ formatted_response = assoc (
48
+ response ,
49
+ 'result' ,
50
+ formatter (response ['result' ]),
51
+ )
52
+ return formatted_response
53
+ else :
54
+ return response
55
+ return middleware
56
+ return formatter_middleware
57
+
58
+
59
+ # TODO: move to utils
16
60
@curry
61
+ @return_arg_type (3 )
62
+ def apply_validator_at_index (validator , at_index , web3 , value ):
63
+ if at_index + 1 > len (value ):
64
+ raise IndexError (
65
+ "Not enough values in iterable to apply formatter. Got: {0}. "
66
+ "Need: {1}" .format (len (value ), at_index + 1 )
67
+ )
68
+ for index , item in enumerate (value ):
69
+ if index == at_index :
70
+ yield validator (web3 , item )
71
+ else :
72
+ yield item
73
+
74
+
75
+ # TODO: move to utils
76
+ @curry
77
+ @to_dict
78
+ def apply_validator_to_dict (validators , web3 , value ):
79
+ for key , item in value .items ():
80
+ if key in validators :
81
+ try :
82
+ yield key , validators [key ](web3 , item )
83
+ except (TypeError , ValueError ) as exc :
84
+ raise type (exc )("Could not format value %r as field %r" % (item , key )) from exc
85
+ else :
86
+ yield key , item
87
+
88
+
17
89
def validate_chain_id (web3 , chain_id ):
18
90
if chain_id == web3 .version .network :
19
91
return None
@@ -27,21 +99,55 @@ def validate_chain_id(web3, chain_id):
27
99
)
28
100
29
101
102
+ @curry
103
+ def check_extradata_length (val , length ):
104
+ if not isinstance (val , (str , int , bytes )):
105
+ return val
106
+ result = HexBytes (val )
107
+ if len (result ) > length :
108
+ raise ValidationError (
109
+ "The field extraData is %d bytes, but should be %d. "
110
+ "It is quite likely that you are connected to a POA chain. "
111
+ "Refer "
112
+ "http://web3py.readthedocs.io/en/latest/middleware.html#geth-style-proof-of-authority "
113
+ "for more details. The full extraData is: %r" % (
114
+ len (result ), length , result
115
+ )
116
+ )
117
+ return val
118
+
119
+
30
120
def transaction_normalizer (transaction ):
31
121
return dissoc (transaction , 'chainId' )
32
122
33
123
34
- def validation_middleware (make_request , web3 ):
35
- transaction_validator = apply_formatters_to_dict ({
36
- 'chainId' : validate_chain_id (web3 ),
37
- })
124
+ BLOCK_VALIDATORS = {
125
+ 'extraData' : check_extradata_length (length = 32 ),
126
+ }
38
127
39
- transaction_sanitizer = compose (transaction_normalizer , transaction_validator )
40
128
41
- def middleware (method , params ):
42
- if method in {'eth_sendTransaction' , 'eth_estimateGas' , 'eth_call' }:
43
- post_validated_params = apply_formatter_at_index (transaction_sanitizer , 0 , params )
44
- return make_request (method , post_validated_params )
45
- else :
46
- return make_request (method , params )
47
- return middleware
129
+ block_validator = apply_formatters_to_dict (BLOCK_VALIDATORS )
130
+
131
+
132
+ TRANSACTION_PARAMS_VALIDATORS = apply_validator_to_dict ({
133
+ 'chainId' : validate_chain_id ,
134
+ })
135
+
136
+
137
+ transaction_params_validator = apply_validator_at_index (
138
+ compose (transaction_normalizer , TRANSACTION_PARAMS_VALIDATORS ),
139
+ 0 ,
140
+ )
141
+
142
+
143
+ validation_middleware = construct_validation_middleware (
144
+ request_validators = {
145
+ 'eth_sendTransaction' : transaction_params_validator ,
146
+ 'eth_estimateGas' : transaction_params_validator ,
147
+ 'eth_call' : transaction_params_validator ,
148
+ },
149
+ result_validators = {
150
+ 'eth_getBlockByHash' : apply_formatter_if (is_not_null , block_validator ),
151
+ 'eth_getBlockByNumber' : apply_formatter_if (is_not_null , block_validator ),
152
+ },
153
+ )
0 commit comments