Continuing with Sapphire tutorial, we learnt in part 1 about Oasis architecture and confidential transaction, and in part 2 about signed view calls, frontend, and precompiles.
#5: What to do next?
This section will touch upon encryption & decryption, signing & verification, autonomous contracts, Oasis Privacy Layer (OPL), running nodes, etc.
Encryption & Decryption
We know now that end-to-end encryption is possible between user and smart contract where storage is on the public ledger in encrypted form. But, it is also possible to use X25519 key pairs inside a smart contract and derive shared secret on-chain.
ECDH states that two people with their own key pairs, when they have each others public keys they can both derive the point, and nobody else can, unless one of the secrets gets leaked.
Use cases
- Encrypted emitted events, stateless view calls
- Encrypted coupons, proxies, authentication, compliance
Encryption (TypeScript)
import * as sapphire from '@oasisprotocol/sapphire-paratime'
const e2ePubKey = await e2e.getPublicKey();
const box = sapphire.cipher.X25519DeoxysII.ephemeral(e2ePubKey);
let {nonce, cipherText} = await box.encrypt(plaintextBytes);
const nonceBytes32Hex = ethers.utils.hexlify(nonce) +
"0000000000000000000000000000000000";
Decryption (Solidity)
import "@oasisprotocol/sapphire-contracts/contracts/Sapphire.sol";
contract E2EProxy {
Sapphire.Curve25519PublicKey internal immutable publicKey;
Sapphire.Curve25519SecretKey internal immutable privateKey;
constructor (bytes memory extraEntropy) {
(publicKey, privateKey) = Sapphire.generateCurve25519KeyPair(extraEntropy);
}
function getPublicKey() external view returns (bytes32) {
return Sapphire.Curve25519PublicKey.unwrap(publicKey);
}
}
import "@oasisprotocol/sapphire-contracts/contracts/Sapphire.sol";
contract E2EProxy {
function decrypt(bytes32 peerPublicKey, bytes32 nonce, bytes memory data)
external
{
bytes32 sk = Sapphire.deriveSymmetricKey(
Sapphire.Curve25519PublicKey.wrap(peerPublicKey),
privateKey);
bytes memory plaintext = Sapphire.decrypt(sk, nonce, data, "");
}
}
Signing & Verification
- Contracts can generate key-pairs (Ed25519, X25519, Bitcoin & NIST)
- Signing with EcDSA and EdDSA
There are various utilities for this.
- You can generate Ethereum addresses and sign transactions on-chain.
- You can verify WebAuthN / TouchID, using Ed25519 & EdDSA
- You can broadcast cross-chain without bridge dependency
Signing (Solidity)
import "@oasisprotocol/sapphire-contracts/contracts/Sapphire.sol";
contract SigningExample {
function sign(bytes32 hashed_message)
external returns (bytes)
{
Sapphire.SigningAlg alg =
Sapphire.SigningAlg.Secp256k1PrehashedKeccak256;
bytes memory pk;
bytes memory sk;
bytes memory digest = abi.encodePacked(hashed_message);
Bytes memory entropy = Sapphire.randomBytes(32, "");
(pk, sk) = Sapphire.generateSigningKeyPair(alg, entropy);
return Sapphire.sign(alg, sk, digest, "");
}
}
Verification (Solidity)
import "@oasisprotocol/sapphire-contracts/contracts/Sapphire.sol";
contract VerifyExample {
function verify(bytes32 hashed_message, bytes memory signature)
external returns (bool)
{
Sapphire.SigningAlg alg =
Sapphire.SigningAlg.Secp256k1PrehashedKeccak256;
bytes memory digest = abi.encodePacked(hashed_message);
return Sapphire.verify(alg, pk, digest, "", signature);
}
}
On-chain Single Sign-On
- Viewing Keys - submitting an access token to view your data.
- Permits - providing access to specific functions (read/write).
- Daily Login with EIP-712: a. it is similar to permits but is application-specific b. it lets you sign a transaction once on a daily or weekly basis
Explore this more with Oasis docs on Sapphire.
Autonomous Contracts
Using the key generation precompiles, Sapphire can generate Ethereum compatible accounts to pay their own gas — either on Sapphire or on other EVM compatible chains.
Ethereum compatible key-generation & signing is included in
the @oasisprotocol/sapphire-contracts package:
- EthereumUtils.sol - a, s = EthereumUtils.generateKeypair()
- EIP155Signer.sol - struct EIP155Signer.EthTx - EIP155Signer.sign(address,secret,tx)
Ethereum Keypair Generation and Signing (Solidity)
import '@oasisprotocol/sapphire-contracts/contracts/EthereumUtils.sol';
contract KeypairExample {
address pubkey;
bytes32 secret;
constructor () {
(pubkey, secret) = EthereumUtils.generateKeypair();
}
function sign(bytes memory data)
external view returns (SignatureRSV memory rsv)
{
bytes32 digest = keccak256(data);
rsv = EthereumUtils.sign(pubkey, secret, digest);
}
}
Transaction Signing (Solidity)
function signTx(uint64 nonce, uint256 gasPrice, uint256 value)
external view returns (bytes memory txdata)
{
return EIP155Signer.sign(pubkey, secret, EIP155Signer.EthTx({
nonce: nonce,
gasPrice: gasPrice,
gasLimit: 250000,
to: msg.sender,
value: value,
data: "",
chainId: block.chainid
}));
}
Explore this more with Oasis docs on Sapphire
Oasis Privacy Layer (OPL)
This workflow demonstrates connecting Sapphire with Ethereum, BNB chain and other EVM networks to be able to add encrypted transactions and
confidential state without moving the whole build away from the home network.
Explore this more with Oasis docs on OPL
Running a node
- As a decentralized network, Oasis encourages developers to help with decentralization by becoming a validator and/or running a ParaTime compute node (it can be Sapphire as well as Cipher or Emerald).
- Alternatively, devs can simply run consensus and/or ParaTime client node along with the web3 gateway to avoid public endpoints or possible malicious interactions.
Explore this more with Oasis docs on running a node
Key resources used for this tutorial which is must-check:
Sapphire docs
Sapphire repository
Oasis playground for demo dApps
Sapphire workshop in Oasis Academy from the online 101 workshop hosted by Matevz Jekovec, Oasis Software Engineer
The online video tutorial
Have a question or need help? Join our Discord and head over to the #dev-central channel.
Top comments (3)
Sapphire deep! 💎
This part of the Sapphire 101 workshop really shows why it’s more than just an EVM runtime, it’s a privacy-first developer toolkit. End-to-end encryption, on-chain signing/verification, autonomous contracts, and OPL all make it possible to build apps that are both Ethereum-compatible and confidentiality-native. Feels like a new baseline for Web3 devs who want security without sacrificing usability.
This part of the workshop is 🔥. Seeing how Sapphire handles encryption/decryption and signing inside the contract itself makes the privacy tools feel way more tangible for EVM devs.