Continuing from Part 1 of the series where we introduced Sapphire and explored confidential transactions.
This section is about impersonation and view calls. Let’s go through this step by step.
- When executing a transaction, msg.sender contains the address of the caller — theone who signed the transaction or the contract address of the caller function.
- For view calls, Ethereum supports impersonation. This means you can set the value of msg.sender field to arbitrary value (e.g. some third-party address) and perform read-only queries on behalf of someone else.
- This is perfectly fine in Ethereum, because the contract state is public anyway and anyone can simulate any execution path.
- Sapphire contract storage is confidential, it forbids impersonation: For anonymous view call:msg.sender equals address(0x0) For signed view call (aka. signed query):msg.sender equals address(signer)
- Here you should mitigate memory access pattern attacks by requiring specificmsg.sender.
- Make the message field in the MessageBox contract private and only allow the author of the message to retrieve it.
- Try using the unwrapped signer to check whether the msg.sender is really zeroed, if view call is unsigned.
- The frontend folder contains a simple Vue-based app that imports the MessageBox contract from backend.
- Let’s start with building the backend.
cd frontend; pnpm dev
- In the demo starter we have been working with, we use Vite to reflect any changes on-the-fly and for deployment. You can preview the frontend with:
cd frontend; pnpm dev
- To sign in the frontend with this demo starter, we store the MetaMask instance into local store so that it’s not reloaded each time you open a subpage.
- useEthereumStore() returns {signer, unwrappedSigner, provider, unwrappedProvider} to access all flavors of the signer or the provider respectively.
- Contract address, expected chain ID etc. is read from frontend/.env* files.
- In contracts.ts the wrappers for the contracts are prepared for you:
const messageBox = useMessageBox();
const uwMessageBox = useUnwrappedMessageBox();
- Calling uwMessageBox.message() will execute Anonymous Unencrypted View Call in the background.
- Calling messageBox.message() will activate MetaMask for Signed and Encrypted View Call.
- Signing the same query is cached. (This helps to reduce annoying popups.)
Based on what you’ve learned so far, try setting the message in the browser with MetaMask.
For EVMs, the coding language in use is Solidity. So, precompiles are built-in functions that extend the EVM.
Be careful here as these are complex operations that are difficult to implement efficiently.
Examples of Ethereum precompiles:
- SHA256
- Elliptic Curve & Pairing (ECADD, ECMUL, ECPAIRING) for ZK / BN128 curve
- EcDSA Signature Verification (ecrecover)
Sapphire precompiles:
pnpm install -D @oasisprotocol/sapphire-contracts
import "@oasisprotocol/sapphire-contracts/contracts/Sapphire.sol";
The list here is not an exhaustive one:
- Gas padding
- Random Number Generation (VRF / CSPRNG)
- Encryption & decryption (X25519), key generation (Ed25519, SEC P256 k1), signing & verification (EdDSA, EcDSA)
Check our docs here.
Sapphire-Localnet
- It goes without saying that Sapphire precompiles run on Sapphire only.
- We can use Testnet, but for running the (automated CI) tests each time, this is not viable (slow, gas costs).
- Inspired by npx hardhat node and geth — dev, the Oasis team provides the sapphire-localnet docker image which spins up an isolated Oasis network locally:
docker run --rm -it -p8545:8545 -p8546:8546 hcr.io/oasisprotocol/sapphire-localnet
Random Number Generator (RNG)
- Cryptographically secure source of entropy:
bytes memory entropy = Sapphire.randomBytes(32, "");
- Much simpler and faster than relying on an external VRF, and, it’s also instant!
- The second parameter seeds the RNG with your own “extra entropy”.
Based on what you’ve learned so far, try implementing on-chain CAPTCHA (e.g. add two numbers) to prevent spam attacks.
Curious what comes next? We’ll take it up in the next part of the series.
In the meantime, you should check these out:
Sapphire docs
Sapphire repository
Have a question or need help? Join our Discord and head over to the #dev-central channel.