sapphire

package module
v0.11.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 18, 2024 License: Apache-2.0 Imports: 22 Imported by: 1

README

Sapphire ParaTime Compat Lib

@oasisprotocol/sapphire-paratime makes it easy to port your dapp to the Sapphire ParaTime. You can port over a Go Ethereum application by using a sapphire.WrappedBackend or by packing native Ethereum transactions Sapphire style.

Building

Sapphire compatibility library works with Go version 1.22 or later and the latest compatible go-ethereum version.

To build and test locally:

go test

Usage

Import
import (
    "context"

    "github.com/ethereum/go-ethereum/accounts/abi/bind"
    "github.com/ethereum/go-ethereum/ethclient"

    sapphire "github.com/oasisprotocol/sapphire-paratime/clients/go"
)
Go-Ethereum ABI

After generating the Go bindings for a particular Solidity contract, you can instantiate an Ethereum client with the Sapphire gateway URL and instantiate a sapphire.WrappedBackend as a drop in replacement:

// key := private key
client, _ := ethclient.Dial(sapphire.Networks[SapphireChainID.Uint64()].DefaultGateway)
backend, _ := sapphire.WrapClient(client, func(digest [32]byte)([]byte, error) {
  // Pass in a custom signing function to interact with the signer
  return crypto.Sign(digest[:], key)
})

Contracts using go-ethereum's abigen can now be used by passing in backend instead of the usual ethclient.Client instance:

nft, _ := NewNft(addr, backend)

Confidential transactions using Go-Ethereum ABI wrapper can be submitted by passing the Sapphire-specific bind.TransactOpts as the first parameter:

txOpts := backend.Transactor(senderAddr)
tx, _ := nft.Transfer(txOpts, tokenId, recipient)
receipt, _ := bind.WaitMined(context.Background(), client, tx)

WARNING: If you forget to pass txOpts as described above, your transaction will be sent in plain-text!

Confidential queries signed with your account's key are also supported on the Sapphire-wrapped contract above, if you pass the bind.CallOpts defining your From address:

balance := nft.BalanceOf(&bind.CallOpts{From: "0xYOUR_ADDRESS"}, common.HexToAddress("0xDce075E1C39b1ae0b75D554558b6451A226ffe00"))
Bring Your Own Signer

You can also package an existing Ethereum transaction for Sapphire by:

sapphireTestnetChainId := 0x5aff // Sapphire Testnet.
packedTx := sapphire.PackTx(tx, sapphire.NewCipher(sapphireTestnetChainId))
signedTx := sign(packedTx) // Using your usual signer.

and sending it with a normal, not-wrapped ethclient.Client instance:

_ = c.SendTransaction(ctx, signedTx)

See Also

Documentation

Index

Constants

View Source
const (
	DefaultGasPrice   = 100_000_000_000
	DefaultGasLimit   = 30_000_000
	DefaultBlockRange = 15
)

Variables

View Source
var (
	ErrCallFailed       = errors.New("call failed in module")
	ErrCallResultDecode = errors.New("could not decode call result")
)
View Source
var Networks = map[uint64]NetworkParams{
	0x5aff: {
		Name:           "testnet",
		ChainID:        *big.NewInt(0x5aff),
		DefaultGateway: "https://testnet.sapphire.oasis.io",
		RuntimeID:      "0x000000000000000000000000000000000000000000000000a6d1e3ebf60dff6c",
	},
	0x5afe: {
		Name:           "mainnet",
		ChainID:        *big.NewInt(0x5afe),
		DefaultGateway: "https://sapphire.oasis.io",
		RuntimeID:      "0x000000000000000000000000000000000000000000000000f80306c9858e7279",
	},
	0x5afd: {
		Name:           "localnet",
		ChainID:        *big.NewInt(0x5afd),
		DefaultGateway: "http://localhost:8545",
		RuntimeID:      "0x8000000000000000000000000000000000000000000000000000000000000000",
	},
}

Functions

func GetRuntimePublicKey

func GetRuntimePublicKey(chainID uint64) (*x25519.PublicKey, uint64, error)

GetRuntimePublicKey fetches the runtime calldata public key from the default Sapphire gateway.

func PackCall

func PackCall(msg ethereum.CallMsg, cipher Cipher) (*ethereum.CallMsg, error)

PackCall prepares `msg` for being sent to Sapphire. The call will be end-to-end encrypted, but the `from` address will be zero.

func PackSignedCall

func PackSignedCall(msg ethereum.CallMsg, cipher Cipher, sign SignerFn, chainID big.Int, leash *evm.Leash) (*ethereum.CallMsg, error)

PackSignedCall prepares `msg` in-place for being sent to Sapphire. The call will be end-to-end encrypted and a signature will be used to authenticate the `from` address.

func PackTx

func PackTx(tx types.Transaction, cipher Cipher) (*types.Transaction, error)

PackTx prepares a regular Eth transaction for Sapphire. The transaction returned from this function is what must be signed.

func WrapClient

func WrapClient(c *ethclient.Client, sign SignerFn) (bind.ContractBackend, error)

WrapClient wraps an ethclient.Client so that it can talk to Sapphire.

Types

type CallDataPublicKey added in v0.10.0

type CallDataPublicKey struct {
	// PublicKey is the requested public key.
	PublicKey hexutil.Bytes `json:"key"`
	// Checksum is the checksum of the key manager state.
	Checksum hexutil.Bytes `json:"checksum"`
	// Signature is the Sign(sk, (key || checksum)) from the key manager.
	Signature hexutil.Bytes `json:"signature"`
	// Epoch is the epoch of the ephemeral runtime key.
	Epoch uint64 `json:"epoch,omitempty"`
}

CallDataPublicKey is the public key alongside the key manager's signature. See Web3 gateway repository for more information https://github.com/oasisprotocol/oasis-web3-gateway/blob/d29efdafe4e07a9f0f9a0fd13379c58eb5b89723/rpc/oasis/api.go#L21-L33 This is a flattened `core.CallDataPublicKeyResponse` with hex-encoded bytes for easy consumption by Web3 clients.

type Cipher

type Cipher interface {
	CallFormat() types.CallFormat
	Encrypt(plaintext []byte) (ciphertext []byte, nonce []byte)
	Decrypt(nonce []byte, ciphertext []byte) (plaintext []byte, err error)
	EncryptEncode(plaintext []byte) []byte
	EncryptEnvelope(plaintext []byte) *types.Call
	DecryptEncoded(result []byte) ([]byte, error)
	DecryptCallResult(result []byte) ([]byte, error)
}

func NewCipher

func NewCipher(chainID uint64) (Cipher, error)

NewCipher creates a default cipher with encryption support.

If you use cipher over a longer period of time, you should create a new cipher instance every epoch to refresh the ParaTime's ephemeral key!

type Curve25519KeyPair

type Curve25519KeyPair struct {
	PublicKey x25519.PublicKey
	SecretKey x25519.PrivateKey
}

func NewCurve25519KeyPair

func NewCurve25519KeyPair() (*Curve25519KeyPair, error)

NewCurve25519KeyPair generates a random keypair suitable for use with the X25519DeoxysII cipher.

type Error

type Error struct {
	Code    int         `json:"code"`
	Message string      `json:"message"`
	Data    interface{} `json:"data,omitempty"`
}

type Kind

type Kind uint64

type NetworkParams

type NetworkParams struct {
	Name           string
	ChainID        big.Int
	DefaultGateway string
	RuntimeID      string
}

type PlainCipher

type PlainCipher struct{}

func NewPlainCipher

func NewPlainCipher() PlainCipher

NewPlainCipher creates a cipher instance without encryption support.

func (PlainCipher) CallFormat added in v0.11.0

func (c PlainCipher) CallFormat() types.CallFormat

func (PlainCipher) Decrypt

func (c PlainCipher) Decrypt(_ []byte, ciphertext []byte) (plaintext []byte, err error)

func (PlainCipher) DecryptCallResult

func (c PlainCipher) DecryptCallResult(response []byte) ([]byte, error)

func (PlainCipher) DecryptEncoded

func (c PlainCipher) DecryptEncoded(response []byte) ([]byte, error)

func (PlainCipher) Encrypt

func (c PlainCipher) Encrypt(plaintext []byte) (ciphertext []byte, nonce []byte)

func (PlainCipher) EncryptEncode

func (c PlainCipher) EncryptEncode(plaintext []byte) []byte

func (PlainCipher) EncryptEnvelope

func (c PlainCipher) EncryptEnvelope(plaintext []byte) *types.Call

type Request

type Request struct {
	Version string      `json:"jsonrpc"`
	Method  string      `json:"method"`
	Params  interface{} `json:"params"`
	ID      int         `json:"id"`
}

type Response

type Response struct {
	Error  *Error          `json:"error"`
	ID     int             `json:"id"`
	Result json.RawMessage `json:"result,omitempty"`
}

type SignerFn

type SignerFn = func(digest [32]byte) ([]byte, error)

SignerFn is a function that produces secp256k1 signatures in RSV format.

type WrappedBackend

type WrappedBackend struct {
	// contains filtered or unexported fields
}

WrappedBackend implements bind.ContractBackend.

func NewWrappedBackend

func NewWrappedBackend(backend bind.ContractBackend, chainID big.Int, cipher Cipher, sign SignerFn) WrappedBackend

func (WrappedBackend) CallContract

func (b WrappedBackend) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error)

CallContract implements ContractCaller.

func (WrappedBackend) CodeAt

func (b WrappedBackend) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error)

CodeAt implements ContractCaller.

func (WrappedBackend) EstimateGas

func (b WrappedBackend) EstimateGas(ctx context.Context, call ethereum.CallMsg) (gas uint64, err error)

EstimateGas implements ContractTransactor.

func (WrappedBackend) FilterLogs

func (b WrappedBackend) FilterLogs(ctx context.Context, query ethereum.FilterQuery) ([]types.Log, error)

FilterLogs implements ContractFilterer.

func (WrappedBackend) HeaderByNumber

func (b WrappedBackend) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error)

HeaderByNumber implements ContractTransactor.

func (WrappedBackend) PendingCodeAt

func (b WrappedBackend) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error)

PendingCodeAt implements ContractTransactor.

func (WrappedBackend) PendingNonceAt

func (b WrappedBackend) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error)

PendingNonceAt implements ContractTransactor.

func (WrappedBackend) SendTransaction

func (b WrappedBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error

SendTransaction implements ContractTransactor.

func (WrappedBackend) SubscribeFilterLogs

func (b WrappedBackend) SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error)

SubscribeFilterLogs implements ContractFilterer.

func (WrappedBackend) SuggestGasPrice

func (b WrappedBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error)

SuggestGasPrice implements ContractTransactor.

func (WrappedBackend) SuggestGasTipCap

func (b WrappedBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error)

SuggestGasTipCap implements ContractTransactor.

func (WrappedBackend) Transactor

func (b WrappedBackend) Transactor(from common.Address) *bind.TransactOpts

Transactor returns a TransactOpts that can be used with Sapphire.

type X25519DeoxysIICipher

type X25519DeoxysIICipher struct {
	// contains filtered or unexported fields
}

X25519DeoxysIICipher is the default cipher that does what it says on the tin.

func NewX25519DeoxysIICipher

func NewX25519DeoxysIICipher(keypair *Curve25519KeyPair, peerPublicKey *x25519.PublicKey, epoch uint64) (*X25519DeoxysIICipher, error)

NewX25519DeoxysIICipher creates a new cipher instance with encryption support.

func (X25519DeoxysIICipher) CallFormat added in v0.11.0

func (c X25519DeoxysIICipher) CallFormat() types.CallFormat

func (X25519DeoxysIICipher) Decrypt

func (c X25519DeoxysIICipher) Decrypt(nonce []byte, ciphertext []byte) ([]byte, error)

func (X25519DeoxysIICipher) DecryptCallResult

func (c X25519DeoxysIICipher) DecryptCallResult(response []byte) ([]byte, error)

func (X25519DeoxysIICipher) DecryptEncoded

func (c X25519DeoxysIICipher) DecryptEncoded(response []byte) ([]byte, error)

func (X25519DeoxysIICipher) Encrypt

func (c X25519DeoxysIICipher) Encrypt(plaintext []byte) (ciphertext []byte, nonce []byte)

func (X25519DeoxysIICipher) EncryptEncode

func (c X25519DeoxysIICipher) EncryptEncode(plaintext []byte) []byte

func (X25519DeoxysIICipher) EncryptEnvelope

func (c X25519DeoxysIICipher) EncryptEnvelope(plaintext []byte) *types.Call

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL