1616
1717use std:: sync:: Arc ;
1818use futures:: { IntoFuture , Future } ;
19+ use primitives:: Hash ;
1920use primitives:: block:: Id as BlockId ;
2021use state_machine:: { self , OverlayedChanges , Backend as StateBackend , CodeExecutor } ;
21- use state_machine:: backend:: InMemory as InMemoryStateBackend ;
22- use triehash:: trie_root;
2322
2423use backend;
2524use blockchain:: Backend as ChainBackend ;
@@ -49,6 +48,11 @@ pub trait CallExecutor {
4948 ///
5049 /// No changes are made.
5150 fn call_at_state < S : state_machine:: Backend > ( & self , state : & S , overlay : & mut OverlayedChanges , method : & str , call_data : & [ u8 ] ) -> Result < ( Vec < u8 > , S :: Transaction ) , error:: Error > ;
51+
52+ /// Execute a call to a contract on top of given state, gathering execution proof.
53+ ///
54+ /// No changes are made.
55+ fn prove_at_state < S : state_machine:: Backend > ( & self , state : S , overlay : & mut OverlayedChanges , method : & str , call_data : & [ u8 ] ) -> Result < ( Vec < u8 > , Vec < Vec < u8 > > ) , error:: Error > ;
5256}
5357
5458/// Call executor that executes methods locally, querying all required
@@ -104,6 +108,18 @@ impl<B, E> CallExecutor for LocalCallExecutor<B, E>
104108 call_data,
105109 ) . map_err ( Into :: into)
106110 }
111+
112+ fn prove_at_state < S : state_machine:: Backend > ( & self , state : S , changes : & mut OverlayedChanges , method : & str , call_data : & [ u8 ] ) -> Result < ( Vec < u8 > , Vec < Vec < u8 > > ) , error:: Error > {
113+ state_machine:: prove (
114+ state,
115+ changes,
116+ & self . executor ,
117+ method,
118+ call_data,
119+ )
120+ . map ( |( result, proof, _) | ( result, proof) )
121+ . map_err ( Into :: into)
122+ }
107123}
108124
109125impl < B , F > RemoteCallExecutor < B , F > {
@@ -138,36 +154,40 @@ impl<B, F> CallExecutor for RemoteCallExecutor<B, F>
138154 fn call_at_state < S : state_machine:: Backend > ( & self , _state : & S , _changes : & mut OverlayedChanges , _method : & str , _call_data : & [ u8 ] ) -> error:: Result < ( Vec < u8 > , S :: Transaction ) > {
139155 Err ( error:: ErrorKind :: NotAvailableOnLightClient . into ( ) )
140156 }
157+
158+ fn prove_at_state < S : state_machine:: Backend > ( & self , _state : S , _changes : & mut OverlayedChanges , _method : & str , _call_data : & [ u8 ] ) -> Result < ( Vec < u8 > , Vec < Vec < u8 > > ) , error:: Error > {
159+ Err ( error:: ErrorKind :: NotAvailableOnLightClient . into ( ) )
160+ }
141161}
142162
143- /// Check remote execution proof.
163+ /// Check remote execution proof using given backend .
144164pub fn check_execution_proof < B , E > ( backend : & B , executor : & E , request : & RemoteCallRequest , remote_proof : ( Vec < u8 > , Vec < Vec < u8 > > ) ) -> Result < CallResult , error:: Error >
145165 where
146166 B : backend:: RemoteBackend ,
147167 E : CodeExecutor ,
148168 error:: Error : From < <<B as backend:: Backend >:: State as StateBackend >:: Error > ,
149169{
150- let ( remote_result, remote_proof) = remote_proof;
151-
152- let remote_state = state_from_execution_proof ( remote_proof) ;
153- let remote_state_root = trie_root ( remote_state. pairs ( ) . into_iter ( ) ) . 0 ;
154-
155170 let local_header = backend. blockchain ( ) . header ( BlockId :: Hash ( request. block ) ) ?;
156171 let local_header = local_header. ok_or_else ( || error:: ErrorKind :: UnknownBlock ( BlockId :: Hash ( request. block ) ) ) ?;
157172 let local_state_root = local_header. state_root ;
173+ do_check_execution_proof ( local_state_root, executor, request, remote_proof)
174+ }
158175
159- if remote_state_root != * local_state_root {
160- return Err ( error:: ErrorKind :: InvalidExecutionProof . into ( ) ) ;
161- }
176+ /// Check remote execution proof using given state root.
177+ fn do_check_execution_proof < E > ( local_state_root : Hash , executor : & E , request : & RemoteCallRequest , remote_proof : ( Vec < u8 > , Vec < Vec < u8 > > ) ) -> Result < CallResult , error:: Error >
178+ where
179+ E : CodeExecutor ,
180+ {
181+ let ( remote_result, remote_proof) = remote_proof;
162182
163183 let mut changes = OverlayedChanges :: default ( ) ;
164- let ( local_result, _) = state_machine:: execute (
165- & remote_state,
184+ let ( local_result, _) = state_machine:: proof_check (
185+ local_state_root. into ( ) ,
186+ remote_proof,
166187 & mut changes,
167188 executor,
168189 & request. method ,
169- & request. call_data ,
170- ) ?;
190+ & request. call_data ) ?;
171191
172192 if local_result != remote_result {
173193 return Err ( error:: ErrorKind :: InvalidExecutionProof . into ( ) ) ;
@@ -176,28 +196,31 @@ pub fn check_execution_proof<B, E>(backend: &B, executor: &E, request: &RemoteCa
176196 Ok ( CallResult { return_data : local_result, changes } )
177197}
178198
179- /// Convert state to execution proof. Proof is simple the whole state (temporary).
180- // TODO [light]: this method must be removed after trie-based proofs are landed.
181- pub fn state_to_execution_proof < B : state_machine:: Backend > ( state : & B ) -> Vec < Vec < u8 > > {
182- state. pairs ( ) . into_iter ( )
183- . flat_map ( |( k, v) | :: std:: iter:: once ( k) . chain ( :: std:: iter:: once ( v) ) )
184- . collect ( )
185- }
186-
187- /// Convert execution proof to in-memory state for check. Reverse function for state_to_execution_proof.
188- // TODO [light]: this method must be removed after trie-based proofs are landed.
189- fn state_from_execution_proof ( proof : Vec < Vec < u8 > > ) -> InMemoryStateBackend {
190- let mut changes = Vec :: new ( ) ;
191- let mut proof_iter = proof. into_iter ( ) ;
192- loop {
193- let key = proof_iter. next ( ) ;
194- let value = proof_iter. next ( ) ;
195- if let ( Some ( key) , Some ( value) ) = ( key, value) {
196- changes. push ( ( key, Some ( value) ) ) ;
197- } else {
198- break ;
199- }
199+ #[ cfg( test) ]
200+ mod tests {
201+ use primitives:: block:: Id as BlockId ;
202+ use state_machine:: Backend ;
203+ use test_client;
204+ use light:: RemoteCallRequest ;
205+ use super :: do_check_execution_proof;
206+
207+ #[ test]
208+ fn execution_proof_is_generated_and_checked ( ) {
209+ // prepare remote client
210+ let remote_client = test_client:: new ( ) ;
211+ let remote_block_id = BlockId :: Number ( 0 ) ;
212+ let remote_block_storage_root = remote_client. state_at ( & remote_block_id)
213+ . unwrap ( ) . storage_root ( :: std:: iter:: empty ( ) ) . 0 ;
214+
215+ // 'fetch' execution proof from remote node
216+ let remote_execution_proof = remote_client. execution_proof ( & remote_block_id, "authorities" , & [ ] ) . unwrap ( ) ;
217+
218+ // check remote execution proof locally
219+ let local_executor = test_client:: NativeExecutor :: new ( ) ;
220+ do_check_execution_proof ( remote_block_storage_root. into ( ) , & local_executor, & RemoteCallRequest {
221+ block : Default :: default ( ) ,
222+ method : "authorities" . into ( ) ,
223+ call_data : vec ! [ ] ,
224+ } , remote_execution_proof) . unwrap ( ) ;
200225 }
201-
202- InMemoryStateBackend :: default ( ) . update ( changes)
203226}
0 commit comments