1
+ import rlp
2
+ from ethereum import blocks
3
+ from ethereum .blocks import Account , BlockHeader , Block , CachedBlock
4
+ from ethereum .utils import is_numeric , is_string , encode_hex , decode_hex , zpad , scan_bin , big_endian_to_int
5
+ from ethereum .securetrie import SecureTrie
6
+ from ethereum .trie import Trie , BLANK_NODE , BLANK_ROOT
7
+
8
+
9
+ class FakeHeader (object ):
10
+ def __init__ (self , number , hash , state_root , gas_limit , timestamp ):
11
+ self .number = number
12
+ self .hash = hash
13
+ self .state_root = state_root
14
+ self .gas_limit = gas_limit
15
+ self .timestamp = timestamp
16
+
17
+
18
+ class FakeBlock (object ):
19
+ def __init__ (self , env , header , chain_diff ):
20
+ self .env = env
21
+ self .config = env .config
22
+ self .header = header
23
+ self .uncles = []
24
+ self .number = header .number
25
+ self .hash = header .hash
26
+ self .gas_limit = header .gas_limit
27
+ self .difficulty = header .difficulty
28
+ self .timestamp = header .timestamp
29
+ self ._chain_diff = chain_diff
30
+
31
+ def chain_difficulty (self ):
32
+ return self ._chain_diff
33
+
34
+ def has_parent (self ):
35
+ return False
36
+
37
+ def get_ancestor_list (self , n ):
38
+ if n == 0 or self .header .number == 0 :
39
+ return []
40
+ p = FakeBlock (self .env , self .header , 0 )
41
+ return [p ] + p .get_ancestor_list (n - 1 )
42
+
43
+
44
+ def create_snapshot (chain , recent = 1024 ):
45
+ env = chain .env
46
+ head_block = chain .head
47
+ base_block_hash = chain .index .get_block_by_number (max (head_block .number - recent , 0 ))
48
+ base_block = chain .get (base_block_hash )
49
+
50
+ snapshot = create_env_snapshot (base_block )
51
+ snapshot ['base' ] = create_base_snapshot (base_block )
52
+ snapshot ['blocks' ] = create_blocks_snapshot (base_block , head_block )
53
+ snapshot ['alloc' ] = create_state_snapshot (env , base_block .state )
54
+
55
+ return snapshot
56
+
57
+
58
+ def create_env_snapshot (base ):
59
+ return {
60
+ 'chainDifficulty' : snapshot_form (base .chain_difficulty ())
61
+ }
62
+
63
+
64
+ def create_base_snapshot (base ):
65
+ return snapshot_form (rlp .encode (base .header ))
66
+
67
+
68
+ def create_state_snapshot (env , state_trie ):
69
+ alloc = dict ()
70
+ count = 0
71
+ for addr , account_rlp in state_trie .to_dict ().items ():
72
+ alloc [encode_hex (addr )] = create_account_snapshot (env , account_rlp )
73
+ count += 1
74
+ print "[%d] created account snapshot %s" % encode_hex (addr )
75
+ return alloc
76
+
77
+
78
+ def create_account_snapshot (env , rlpdata ):
79
+ account = get_account (env , rlpdata )
80
+ storage_trie = SecureTrie (Trie (env .db , account .storage ))
81
+ storage = dict ()
82
+ for k , v in storage_trie .to_dict ().items ():
83
+ storage [encode_hex (k .lstrip ('\x00 ' ) or '\x00 ' )] = encode_hex (v )
84
+ return {
85
+ 'nonce' : snapshot_form (account .nonce ),
86
+ 'balance' : snapshot_form (account .balance ),
87
+ 'code' : encode_hex (account .code ),
88
+ 'storage' : storage
89
+ }
90
+
91
+
92
+ def create_blocks_snapshot (base , head ):
93
+ recent_blocks = list ()
94
+ block = head
95
+ while True :
96
+ recent_blocks .append (snapshot_form (rlp .encode (block )))
97
+ if block .prevhash != base .hash :
98
+ block = block .get_parent ()
99
+ else :
100
+ break
101
+ recent_blocks .reverse ()
102
+ return recent_blocks
103
+
104
+
105
+ def load_snapshot (chain , snapshot ):
106
+ base_header = rlp .decode (scan_bin (snapshot ['base' ]), BlockHeader )
107
+
108
+ limit = len (snapshot ['blocks' ])
109
+ # first block is child of base block
110
+ first_block_rlp = scan_bin (snapshot ['blocks' ][0 ])
111
+ first_header_data = rlp .decode (first_block_rlp )[0 ]
112
+ head_block_rlp = scan_bin (snapshot ['blocks' ][limit - 1 ])
113
+ head_header_data = rlp .decode (head_block_rlp )[0 ]
114
+
115
+ state = load_state (chain .env , snapshot ['alloc' ])
116
+ assert state .root_hash == base_header .state_root
117
+
118
+ _get_block_header = blocks .get_block_header
119
+ def get_block_header (db , blockhash ):
120
+ if blockhash == first_header_data [0 ]: # first block's prevhash
121
+ return base_header
122
+ return _get_block_header (db , blockhash )
123
+ blocks .get_block_header = get_block_header
124
+
125
+ _get_block = blocks .get_block
126
+ def get_block (env , blockhash ):
127
+ if blockhash == first_header_data [0 ]:
128
+ return FakeBlock (env , get_block_header (env .db , blockhash ), int (snapshot ['chainDifficulty' ]))
129
+ return _get_block (env , blockhash )
130
+ blocks .get_block = get_block
131
+
132
+ def validate_uncles ():
133
+ return True
134
+
135
+ first_block = rlp .decode (first_block_rlp , Block , env = chain .env )
136
+ chain .index .add_block (first_block )
137
+ chain ._store_block (first_block )
138
+ chain .blockchain .put ('HEAD' , first_block .hash )
139
+ chain .blockchain .put (chain .index ._block_by_number_key (first_block .number ), first_block .hash )
140
+ chain .blockchain .commit ()
141
+ chain ._update_head_candidate ()
142
+
143
+ count = 0
144
+ for block_rlp in snapshot ['blocks' ][1 :]:
145
+ block_rlp = scan_bin (block_rlp )
146
+ block = rlp .decode (block_rlp , Block , env = chain .env )
147
+ if count < chain .env .config ['MAX_UNCLE_DEPTH' ]+ 2 :
148
+ block .__setattr__ ('validate_uncles' , validate_uncles )
149
+ if not chain .add_block (block ):
150
+ print "Failed to load block #%d (%s), abort." % (block .number , encode_hex (block .hash )[:8 ])
151
+ else :
152
+ count += 1
153
+ print "[%d] block #%d (%s) added" % (count , block .number , encode_hex (block .hash )[:8 ])
154
+ print "Snapshot loaded."
155
+
156
+
157
+ def load_state (env , alloc ):
158
+ db = env .db
159
+ state = SecureTrie (Trie (db , BLANK_ROOT ))
160
+ for addr , account in alloc .items ():
161
+ acct = Account .blank_account (db , env .config ['ACCOUNT_INITIAL_NONCE' ])
162
+ if len (account ['storage' ]) > 0 :
163
+ t = SecureTrie (Trie (db , BLANK_ROOT ))
164
+ for k , v in account ['storage' ].items ():
165
+ enckey = zpad (decode_hex (k ), 32 )
166
+ t .update (enckey , decode_hex (v ))
167
+ acct .storage = t .root_hash
168
+ if account ['nonce' ]:
169
+ acct .nonce = int (account ['nonce' ])
170
+ if account ['balance' ]:
171
+ acct .balance = int (account ['balance' ])
172
+ if account ['code' ]:
173
+ acct .code = decode_hex (account ['code' ])
174
+ state .update (decode_hex (addr ), rlp .encode (acct ))
175
+ db .commit ()
176
+ return state
177
+
178
+
179
+ def get_account (env , rlpdata ):
180
+ if rlpdata != BLANK_NODE :
181
+ return rlp .decode (rlpdata , Account , db = env .db )
182
+ else :
183
+ return Account .blank_account (env .db , env .config ['ACCOUNT_INITIAL_NONCE' ])
184
+
185
+
186
+ def snapshot_form (val ):
187
+ if is_numeric (val ):
188
+ return str (val )
189
+ elif is_string (val ):
190
+ return b'0x' + encode_hex (val )
0 commit comments