The eth_getProof API is available in geth and parity to query smart contract storage and returns a proof from which you can verify inclusion and non inclusion in a state root. You probably have to run a full node locally as Infura might not support the API.
See EIP 1186 eth_getProof for details about the EIP.
At the time of writing this, web3js and web3py do not implement the API so you should access it in a different way. One way to do it is this fork of web3py which let's you query eth_getProof : https://github.com/paouvrard/web3.py/tree/EIP-1186-eth_getProof
Example solidity contract:
contract Token {
string public greeting;
mapping (address => uint) public balances;
constructor() public {
greeting = 'Hello';
balances[msg.sender] = 33333;
}
}
Verifying storage proofs for 'greeting' and 'balances': this is how I do it on a private network.
from web3 import Web3, IPCProvider
from web3.middleware import geth_poa_middleware
from web3._utils.proof import verify_eth_getProof, storage_position
w3 = Web3(IPCProvider(...))
w3.middleware_stack.inject(geth_poa_middleware, layer=0)
w3.eth.defaultAccount = Web3.toChecksumAddress('0x...')
# get the latest block state root
block = w3.eth.getBlock('latest')
# calculate storate position == patricia trie key
trie_key_greeting = "0x0"
trie_key_balance = storage_position(w3.eth.defaultAccount, "0x1")
# query eth_getProof
proof = w3.eth.getProof(contract_addr, [trie_key_greeting, trie_key_balance], block.number)
# verify the value is included in the state root
is_valid_proof = verify_eth_getProof(proof, block.stateRoot)
To query a contract variable, you need to know the key used to store it in the trie. This key is based on the order in which the variable was defined in the contract. 'greeting' was defined first so its "0x0". For the 'balances' mapping it's a bit more complex as it's a combination of the position and the map's key you are accessing : keccack(LeftPad32(key, 0), LeftPad32(map position, 0)). For more details, see eth_getStorageAt here in the Ethereum wiki.
The proof object returned by w3.eth.getProof contains the account proof and an array of storage proofs that were queried ([trie_key_greeting, trie_key_balance]).