From c117500761e2bb01a289705dd85a998d4504e3e6 Mon Sep 17 00:00:00 2001 From: Ferran Borreguero Date: Tue, 26 Mar 2024 12:47:57 +0000 Subject: [PATCH] Add example of a suapp storing and using a private key --- examples/private-suapp-key/README.md | 17 ++++++ examples/private-suapp-key/main.go | 33 ++++++++++++ .../private-suapp-key/private-suapp-key.sol | 53 +++++++++++++++++++ 3 files changed, 103 insertions(+) create mode 100644 examples/private-suapp-key/README.md create mode 100644 examples/private-suapp-key/main.go create mode 100644 examples/private-suapp-key/private-suapp-key.sol diff --git a/examples/private-suapp-key/README.md b/examples/private-suapp-key/README.md new file mode 100644 index 0000000..ff470c7 --- /dev/null +++ b/examples/private-suapp-key/README.md @@ -0,0 +1,17 @@ +# Example Suapp with a stored private key + +This example shows how Suapps can store private keys in the confidential storage to be used in multiple confidential requests. + +## How to use + +Run `Suave` in development mode: + +``` +$ suave --suave.dev +``` + +Execute the deployment script: + +``` +$ go run main.go +``` diff --git a/examples/private-suapp-key/main.go b/examples/private-suapp-key/main.go new file mode 100644 index 0000000..e7f9e62 --- /dev/null +++ b/examples/private-suapp-key/main.go @@ -0,0 +1,33 @@ +package main + +import ( + "encoding/hex" + "log" + + "github.com/flashbots/suapp-examples/framework" +) + +func main() { + fr := framework.New() + + priv := "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291" + + contract := fr.Suave.DeployContract("private-suapp-key.sol/PublicSuapp.json") + contract.SendConfidentialRequest("registerPrivateKey", nil, []byte(priv)) + + receipt := contract.SendConfidentialRequest("example", nil, nil) + + // validate the signature + txnSignatureEvent, err := contract.Abi.Events["TxnSignature"].ParseLog(receipt.Logs[0]) + if err != nil { + log.Fatal(err) + } + var r, s = txnSignatureEvent["r"].([32]byte), txnSignatureEvent["s"].([32]byte) + + if hex.EncodeToString(r[:]) != "eebcfac0def6db5649d0ae6b52ed3b8ba1f5c6c428588df125461113ba8c6749" { + log.Fatal("wrong r signature") + } + if hex.EncodeToString(s[:]) != "5d5e1aafa0c964b43c251b6a525d49572968f2cebc5868c58bcc9281b9a07505" { + log.Fatal("wrong s signature") + } +} diff --git a/examples/private-suapp-key/private-suapp-key.sol b/examples/private-suapp-key/private-suapp-key.sol new file mode 100644 index 0000000..7ae1a10 --- /dev/null +++ b/examples/private-suapp-key/private-suapp-key.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: Unlicensed +pragma solidity ^0.8.8; + +import "suave-std/suavelib/Suave.sol"; +import "suave-std/Context.sol"; +import "suave-std/Suapp.sol"; +import "suave-std/Transactions.sol"; + +contract PublicSuapp is Suapp { + Suave.DataId signingKeyBid; + string public KEY_PRIVATE_KEY = "KEY"; + + // onchain-offchain pattern to register the new private key in the Confidential storage + function updateKeyCallback(Suave.DataId _signingKeyBid) public { + signingKeyBid = _signingKeyBid; + } + + function registerPrivateKey() public returns (bytes memory) { + bytes memory keyData = Context.confidentialInputs(); + + address[] memory peekers = new address[](1); + peekers[0] = address(this); + + Suave.DataRecord memory bid = Suave.newDataRecord(10, peekers, peekers, "private_key"); + Suave.confidentialStore(bid.id, KEY_PRIVATE_KEY, keyData); + + return abi.encodeWithSelector(this.updateKeyCallback.selector, bid.id); + } + + // offchain-onchain pattern to sign a transaction using the private key stored in the Suapp + event TxnSignature(bytes32 r, bytes32 s); + + function exampleCallback() public emitOffchainLogs {} + + function example() public returns (bytes memory) { + bytes memory signingKey = Suave.confidentialRetrieve(signingKeyBid, KEY_PRIVATE_KEY); + + Transactions.EIP155Request memory txnWithToAddress = Transactions.EIP155Request({ + to: address(0x00000000000000000000000000000000DeaDBeef), + gas: 1000000, + gasPrice: 500, + value: 1, + nonce: 1, + data: bytes(""), + chainId: 1337 + }); + + Transactions.EIP155 memory txn = Transactions.signTxn(txnWithToAddress, string(signingKey)); + emit TxnSignature(txn.r, txn.s); + + return abi.encodeWithSelector(this.exampleCallback.selector); + } +}