@@ -5,6 +5,7 @@ use anyhow::{anyhow, Result};
55use byteorder:: { BigEndian , ByteOrder , ReadBytesExt } ;
66use cid:: Cid ;
77use fvm_ipld_blockstore:: { Blockstore , Buffered } ;
8+ use fvm_shared:: commcid:: { FIL_COMMITMENT_SEALED , FIL_COMMITMENT_UNSEALED } ;
89
910// TODO: figure out where to put this.
1011const DAG_CBOR : u64 = 0x71 ;
@@ -171,31 +172,59 @@ fn copy_rec<'a>(
171172 root : Cid ,
172173 buffer : & mut Vec < ( Cid , & ' a [ u8 ] ) > ,
173174) -> Result < ( ) > {
174- // TODO: Make this non-recursive.
175- // Skip identity and Filecoin commitment Cids
176- if root. codec ( ) != DAG_CBOR {
177- return Ok ( ( ) ) ;
178- }
179-
180- let block = & * cache
181- . get ( & root)
182- . ok_or_else ( || anyhow ! ( "Invalid link ({}) in flushing buffered store" , root) ) ?;
183-
184- scan_for_links ( & mut Cursor :: new ( block) , |link| {
185- if link. codec ( ) != DAG_CBOR {
186- return Ok ( ( ) ) ;
175+ const DAG_RAW : u64 = 0x55 ;
176+ const BLAKE2B_256 : u64 = 0xb220 ;
177+ const BLAKE2B_LEN : u8 = 32 ;
178+ const IDENTITY : u64 = 0x0 ;
179+
180+ // Differences from lotus (vm.Copy):
181+ // 1. We assume that if we don't have a block in our buffer, it must already be in the client.
182+ // don't check. This should only happen if the lotus node is missing state.
183+ // 2. We always write-back new blocks, even if lotus already has them. We haven't noticed a perf
184+ // impact.
185+
186+ match ( root. codec ( ) , root. hash ( ) . code ( ) , root. hash ( ) . size ( ) ) {
187+ // Allow non-truncated blake2b-256 raw/cbor (code/state)
188+ ( DAG_RAW | DAG_CBOR , BLAKE2B_256 , BLAKE2B_LEN ) => ( ) ,
189+ // Ignore raw identity cids (fake code cids)
190+ ( DAG_RAW , IDENTITY , _) => return Ok ( ( ) ) ,
191+ // Copy links from cbor identity cids.
192+ // We shouldn't be creating these at the moment, but lotus' vm.Copy supports them.
193+ ( DAG_CBOR , IDENTITY , _) => {
194+ return scan_for_links ( & mut Cursor :: new ( root. hash ( ) . digest ( ) ) , |link| {
195+ copy_rec ( cache, link, buffer)
196+ } )
187197 }
188-
189- // DB reads are expensive. So we check if it exists in the cache.
190- // If it doesnt exist in the DB, which is likely, we proceed with using the cache.
191- if !cache. contains_key ( & link) {
192- return Ok ( ( ) ) ;
198+ // Ignore commitments (not even going to check the hash function.
199+ ( FIL_COMMITMENT_UNSEALED | FIL_COMMITMENT_SEALED , _, _) => return Ok ( ( ) ) ,
200+ // Fail on anything else. We usually want to continue on error, but there's really no going
201+ // back from here.
202+ ( codec, hash, length) => {
203+ return Err ( anyhow ! (
204+ "cid {root} has unexpected codec ({codec}), hash ({hash}), or length ({length})"
205+ ) )
193206 }
207+ }
194208
195- // Recursively find more links under the links we're iterating over.
196- copy_rec ( cache, link, buffer)
197- } ) ?;
209+ // If we don't have the block, we assume it's already in the datastore.
210+ //
211+ // The alternative would be to check if it's in the datastore, but that's likely even more
212+ // expensive. And there wouldn't be much we could do at that point but abort the block.
213+ let block = match cache. get ( & root) {
214+ Some ( blk) => blk,
215+ None => return Ok ( ( ) ) ,
216+ } ;
217+
218+ // At the moment, we only expect dag-cbor and raw.
219+ // In M2, we'll need to copy explicitly.
220+ if root. codec ( ) == DAG_CBOR {
221+ // TODO: Make this non-recursive.
222+ scan_for_links ( & mut Cursor :: new ( block) , |link| {
223+ copy_rec ( cache, link, buffer)
224+ } ) ?;
225+ }
198226
227+ // Finally, push the block. We do this _last_ so that we always include write before parents.
199228 buffer. push ( ( root, block) ) ;
200229
201230 Ok ( ( ) )
0 commit comments