diff --git a/Makefile b/Makefile index 122e379..edbeb45 100644 --- a/Makefile +++ b/Makefile @@ -56,4 +56,5 @@ run-integration: go run examples/private-library/main.go go run examples/private-library-confidential-store/main.go go run examples/private-suapp-key/main.go + go run examples/private-suapp-key-gen/main.go go run examples/std-transaction-signing/main.go diff --git a/examples/private-suapp-key-gen/README.md b/examples/private-suapp-key-gen/README.md new file mode 100644 index 0000000..e30a37b --- /dev/null +++ b/examples/private-suapp-key-gen/README.md @@ -0,0 +1,17 @@ +# Example Suapp with a stored private key generated by the Suapp + +This is a variation of the [private-suapp-key](../private-suapp-key) example. The difference is that the private key is generated by the Suapp itself during the `initialize` function. + +## 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-gen/main.go b/examples/private-suapp-key-gen/main.go new file mode 100644 index 0000000..d1a095f --- /dev/null +++ b/examples/private-suapp-key-gen/main.go @@ -0,0 +1,22 @@ +package main + +import ( + "log" + + "github.com/flashbots/suapp-examples/framework" +) + +func main() { + fr := framework.New() + + contract := fr.Suave.DeployContract("private-suapp-key-gen.sol/PublicSuapp.json") + + contract.SendConfidentialRequest("initialize", nil, nil) + receipt := contract.SendConfidentialRequest("example", nil, nil) + + // validate the signature (TODO: return the address from the Suapp and validate the signature) + _, err := contract.Abi.Events["TxnSignature"].ParseLog(receipt.Logs[0]) + if err != nil { + log.Fatal(err) + } +} diff --git a/examples/private-suapp-key-gen/private-suapp-key-gen.sol b/examples/private-suapp-key-gen/private-suapp-key-gen.sol new file mode 100644 index 0000000..5aec9ed --- /dev/null +++ b/examples/private-suapp-key-gen/private-suapp-key-gen.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 initialize() public returns (bytes memory) { + string memory keyData = Suave.privateKeyGen(Suave.CryptoSignature.SECP256); + + 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, abi.encodePacked(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); + } +}