The receipt trie is stored locally by the nodes (such as the 4 tries that are part of the Ethereum architecture) and is referenced in the block header by its root node hash called receiptsRoot
.
To answer the second part of your question, we first need to distinguish between invalid and failed transactions :
- Invalid : When receiving a transaction, the miner first verifies it locally checking among other things the nonce, the signature validity and the well-formed RLP encoding of the transaction. Invalid transactions are not broadcasted by the miners, not executed by the network, and never recorded on the blockchain.
- Failed : Failed transactions on the other hand have passed the basic tests of intrinsic validity and are then valid from the protocol point of view. They will failed during the execution for whatever reason but are still included in the block. Indeed, the miner will receive the gas of the transaction and the sender account nonce will be incremented.
The receipt trie stores the receipt of each transaction of the block, including the failed ones. The receipt results of the transaction execution and is formally defined by the Ethereum yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf) as follow :
The transaction receipt, R, is a tuple of four items comprising: the cumulative gas used in the block containing
the transaction receipt as of immediately after the transaction has happened, Ru, the set of logs created through
execution of the transaction, Rl and the Bloom filter composed from information in those logs, Rb and the status
code of the transaction, Rz: R ≡ (Ru, Rb, Rl, Rz)
The status code Rz
is defined as "a non-negative
integer", which I believe is 1 or 0 for respectively succeeded and failed transactions.