I was trying to solve this exact problem and wrote a proof of concept that can do this:
Basically, you need two components:
1: A way to read and confirm the validity of a block header
2: A way to check the bloom filter for the presence of a log entry
In my proof of concept, in order to ingest a log entry and confirm it's validity a client calls a method and passes in both the RLP encoded block header that the log is from and the contents of the log itself.
The RLP encoded header is decoded in the contract. Once it's decoded, the correct block hash for that block can be retrieved via block.blockhash. You then just compare the keccak256 hash of the rlp encoded header bytes and the real hash, if they match the header is valid.
Once you have a valid header, you can read the logs bloom from it. The logs bloom is a 256 byte number which can be checked to confirm the existence of a log as long as you know the address of the contract that wrote the log, the signature of the log event, and any topics that the log included.
The trick to validating a specific blob of log data from this is to include the logged data blob as the log value AND as an indexed topic. When you include it as an indexed topic the keccak256 hash of the blob will be present in the logs bloom.
Checking the presence of a specific log in the logs bloom is relatively simple, but requires some funky bit math which is a bit tricky to do efficiently in solidity. My example was forced to fall back on assembly for this part of it.
The process is as follows:
- Get the three hashes that should be in the logs bloom: the hash of
the contract address, the hash of the hash of the event signature
[keccak256(keccak256("DataStored(bytes,bytes)"], and the hash of the
hash of the logged data.
- For each of the hashes extract the first three pairs of bytes. Lets call these [B1,B2,B3]
- For each byte-pair B, check the presence of the marker bit
(m) in the logs bloom, where m = 1<<(B%2048).
- If the logs bloom contains all 9 of the bits that you check, the log is (most likely) valid.
Using this method you can ingest historical logs into a smart contract in order to validate data or to act as a lower gas cost storage space. The tradeoff is that the gas cost of the data retrieval/validation is significantly higher.