Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deploy gateway backend as sgx #2050

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
17 changes: 10 additions & 7 deletions .github/workflows/manual-deploy-obscuro-gateway.yml
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ jobs:

- name: Build and Push Docker Image
run: |
DOCKER_BUILDKIT=1 docker build --build-arg TESTNET_TYPE=${{ github.event.inputs.testnet_type }} -t ${{ env.DOCKER_BUILD_TAG_GATEWAY }} -f ./tools/walletextension/Dockerfile .
DOCKER_BUILDKIT=1 docker build --build-arg TESTNET_TYPE=${{ github.event.inputs.testnet_type }} -t ${{ env.DOCKER_BUILD_TAG_GATEWAY }} -f ./tools/walletextension/enclave.Dockerfile .
docker push ${{ env.DOCKER_BUILD_TAG_GATEWAY }}

# This will fail some deletions due to resource dependencies ( ie. you must first delete the vm before deleting the disk)
Expand Down Expand Up @@ -178,7 +178,7 @@ jobs:
--public-ip-address "${{ env.PUBLIC_IP }}" \
--tags deploygroup="${{ env.DEPLOY_GROUP }}" ${{ env.AZURE_DEPLOY_GROUP_GATEWAY }}=true \
--vnet-name "${{ env.VNET_NAME }}" --subnet "${{ env.SUBNET_NAME }}" \
--size Standard_D4_v5 --image Canonical:0001-com-ubuntu-server-focal:20_04-lts-gen2:latest \
--size Standard_DC2s_v3 --storage-sku StandardSSD_LRS --image ObscuroConfUbuntu \
--authentication-type password

- name: 'Open Ten node-${{ matrix.host_id }} ports on Azure'
Expand Down Expand Up @@ -207,9 +207,12 @@ jobs:
&& docker network create --driver bridge node_network || true \
&& cd /home/obscuro/go-obscuro/ \
&& docker run -d -p 80:80 -p 81:81 --name "${{ env.VM_NAME }}" \
--device /dev/sgx_enclave --device /dev/sgx_provision \
--entrypoint "/home/ten/go-ten/tools/walletextension/main/entry.sh" \
-e OBSCURO_GATEWAY_VERSION="${{ GITHUB.RUN_NUMBER }}-${{ GITHUB.SHA }}" \
--log-opt max-file=3 --log-opt max-size=10m \
${{ env.DOCKER_BUILD_TAG_GATEWAY }} \
-host=0.0.0.0 -port=80 -portWS=81 -nodeHost=${{ env.L2_RPC_URL_VALIDATOR }} -verbose=true \
-logPath=sys_out -dbType=mariaDB -dbConnectionURL="obscurouser:${{ secrets.OBSCURO_GATEWAY_MARIADB_USER_PWD }}@tcp(obscurogateway-mariadb-${{ github.event.inputs.testnet_type }}.uksouth.cloudapp.azure.com:3306)/ogdb" \
-rateLimitUserComputeTime=${{ env.GATEWAY_RATE_LIMIT_USER_COMPUTE_TIME }} -rateLimitWindow=${{ env.GATEWAY_RATE_LIMIT_WINDOW }} -maxConcurrentRequestsPerUser=${{ env.GATEWAY_MAX_CONCURRENT_REQUESTS_PER_USER }} '
-e OE_SIMULATION=0 \
${{ env.DOCKER_BUILD_TAG_GATEWAY }} \
ego run /home/ten/go-ten/tools/walletextension/main/main \
-host=0.0.0.0 -port=80 -portWS=81 -nodeHost=${{ env.L2_RPC_URL_VALIDATOR }} -verbose=true \
-logPath=sys_out -dbType=mariaDB -dbConnectionURL="obscurouser:${{ secrets.OBSCURO_GATEWAY_MARIADB_USER_PWD }}@tcp(obscurogateway-mariadb-${{ github.event.inputs.testnet_type }}.uksouth.cloudapp.azure.com:3306)/ogdb" \
-rateLimitUserComputeTime=${{ env.GATEWAY_RATE_LIMIT_USER_COMPUTE_TIME }} -rateLimitWindow=${{ env.GATEWAY_RATE_LIMIT_WINDOW }} -maxConcurrentRequestsPerUser=${{ env.GATEWAY_MAX_CONCURRENT_REQUESTS_PER_USER }} '
58 changes: 58 additions & 0 deletions go/common/gethapi/transaction_args.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"math/big"
"strings"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
Expand Down Expand Up @@ -39,6 +40,63 @@ type TransactionArgs struct {
ChainID *hexutil.Big `json:"chainId,omitempty"`
}

// String returns a human-readable representation of the transaction arguments.
// This is necessary for printing the transaction arguments in SGX mode
func (args TransactionArgs) String() string {
var parts []string
if args.From != nil {
parts = append(parts, fmt.Sprintf("From:%s", args.From.Hex()))
}
if args.To != nil {
parts = append(parts, fmt.Sprintf("To:%s", args.To.Hex()))
}
if args.Gas != nil {
parts = append(parts, fmt.Sprintf("Gas:%d", *args.Gas))
}
if args.GasPrice != nil {
parts = append(parts, fmt.Sprintf("GasPrice:%s", args.GasPrice.String()))
}
if args.MaxFeePerGas != nil {
parts = append(parts, fmt.Sprintf("MaxFeePerGas:%s", args.MaxFeePerGas.String()))
}
if args.MaxPriorityFeePerGas != nil {
parts = append(parts, fmt.Sprintf("MaxPriorityFeePerGas:%s", args.MaxPriorityFeePerGas.String()))
}
if args.Value != nil {
parts = append(parts, fmt.Sprintf("Value:%s", args.Value.String()))
}
if args.Nonce != nil {
parts = append(parts, fmt.Sprintf("Nonce:%d", *args.Nonce))
}
if args.Data != nil {
parts = append(parts, fmt.Sprintf("Data:0x%x", *args.Data))
}
if args.Input != nil {
parts = append(parts, fmt.Sprintf("Input:0x%x", *args.Input))
}
if args.AccessList != nil {
parts = append(parts, fmt.Sprintf("AccessList:%s", accessListToString(*args.AccessList)))
}
if args.ChainID != nil {
parts = append(parts, fmt.Sprintf("ChainID:%s", args.ChainID.String()))
}

return fmt.Sprintf("TransactionArgs{%s}", strings.Join(parts, " "))
}

// Helper function to convert AccessList to string
func accessListToString(list types.AccessList) string {
var accessListParts []string
for _, tuple := range list {
storageKeys := make([]string, len(tuple.StorageKeys))
for i, key := range tuple.StorageKeys {
storageKeys[i] = key.Hex()
}
accessListParts = append(accessListParts, fmt.Sprintf("{%s: [%s]}", tuple.Address.Hex(), strings.Join(storageKeys, ", ")))
}
return fmt.Sprintf("[%s]", strings.Join(accessListParts, ", "))
}

// from retrieves the transaction sender address.
func (args *TransactionArgs) from() common.Address {
if args.From == nil {
Expand Down
3 changes: 3 additions & 0 deletions lib/gethfork/rpc/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,9 @@ func (bnh *BlockNumberOrHash) Number() (BlockNumber, bool) {
}

func (bnh *BlockNumberOrHash) String() string {
if bnh == nil {
return "nil"
}
if bnh.BlockNumber != nil {
return strconv.Itoa(int(*bnh.BlockNumber))
}
Expand Down
48 changes: 48 additions & 0 deletions tools/walletextension/enclave.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Build Stages:
# build-base = downloads modules and prepares the directory for compilation. Based on the ego-dev image
# build-enclave = copies over the actual source code of the project and builds it using a compiler cache
# deploy = copies over only the enclave executable without the source
# in a lightweight base image specialized for deployment

# Final container folder structure:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this comment here is not accurate

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

# /home/ten/go-ten/tools/walletextension/main contains the executable for the enclave


FROM ghcr.io/edgelesssys/ego-dev:v1.5.3 AS build-base

# setup container data structure
RUN mkdir -p /home/ten/go-ten

# Ensures container layer caching when dependencies are not changed
WORKDIR /home/ten/go-ten
COPY go.mod .
COPY go.sum .
RUN ego-go mod download


# Trigger new build stage for compiling the enclave
FROM build-base AS build-enclave
COPY . .

WORKDIR /home/ten/go-ten/tools/walletextension/main

# Build the enclave using the cross image build cache.
RUN --mount=type=cache,target=/root/.cache/go-build \
ego-go build

# Sign the enclave executable
RUN ego sign enclave.json


# Trigger a new build stage and use the smaller ego version:
FROM ghcr.io/edgelesssys/ego-deploy:v1.5.3

# Copy just the binary for the enclave into this build stage
COPY --from=build-enclave \
/home/ten/go-ten/tools/walletextension/main /home/ten/go-ten/tools/walletextension/main

WORKDIR /home/ten/go-ten/tools/walletextension/main

# simulation mode is ACTIVE by default
ENV OE_SIMULATION=1
EXPOSE 3000
29 changes: 29 additions & 0 deletions tools/walletextension/main/enclave.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"exe": "main",
"key": "testnet.pem",
"debug": true,
"heapSize": 4096,
"executableHeap": true,
"productID": 1,
"securityVersion": 1,
"env": [
{
"name": "TESTMODE",
"value": "false"
}
],
"files": [
{
"source": "../storage/database/mariadb/001_init.sql",
"target": "/home/ten/go-ten/tools/walletextension/storage/database/mariadb/001_init.sql"
},
{
"source": "../storage/database/mariadb/002_store_incoming_txs.sql",
"target": "/home/ten/go-ten/tools/walletextension/storage/database/mariadb/002_store_incoming_txs.sql"
},
{
"source": "../storage/database/mariadb/003_add_signature_type.sql",
"target": "/home/ten/go-ten/tools/walletextension/storage/database/mariadb/003_add_signature_type.sql"
}
]
}
21 changes: 21 additions & 0 deletions tools/walletextension/main/entry.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/bin/sh
set -e
#
# This script is the entry point for starting the enclave under a Docker container.
# It allows running SGX sdk using different parameters.
#

# It's expected to be a link between the /dev/sgx_enclave Docker device and the container /dev/sgx/enclave
mkdir -p /dev/sgx
if [ ! -L /dev/sgx/enclave ]; then
ln -s /dev/sgx_enclave /dev/sgx/enclave
fi

PCCS_URL=https://global.acccache.azure.net/sgx/certification/v4/
echo "PCCS_URL: ${PCCS_URL}"

apt-get install -qq libsgx-dcap-default-qpl

echo "PCCS_URL=${PCCS_URL}\nUSE_SECURE_CERT=FALSE" > /etc/sgx_default_qcnl.conf

"$@"
2 changes: 1 addition & 1 deletion tools/walletextension/main/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const (
tcp = "tcp"
// @fixme -
// this is a temporary fix as out forked version of log.go does not map with gethlog.Level<Level>
//and should be fixed as part of logging refactoring in the future
// and should be fixed as part of logging refactoring in the future
legacyLevelDebug = 4
legacyLevelError = 1
)
Expand Down
64 changes: 60 additions & 4 deletions tools/walletextension/rpcapi/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,12 @@ import (
"encoding/json"
"errors"
"fmt"
"reflect"
"strings"
"time"

"github.com/status-im/keycard-go/hexutils"

"github.com/ten-protocol/go-ten/go/common/measure"
"github.com/ten-protocol/go-ten/go/enclave/core"

Expand All @@ -22,8 +26,6 @@ import (

"github.com/ten-protocol/go-ten/lib/gethfork/rpc"

"github.com/status-im/keycard-go/hexutils"

"github.com/ten-protocol/go-ten/tools/walletextension/cache"

gethcommon "github.com/ethereum/go-ethereum/common"
Expand Down Expand Up @@ -166,8 +168,7 @@ func ExecAuthRPC[R any](ctx context.Context, w *Services, cfg *ExecCfg, method s
}
return nil, rpcErr
})

audit(w, "RPC call. uid=%s, method=%s args=%v result=%s error=%s time=%d", hexutils.BytesToHex(userID), method, args, res, err, time.Since(requestStartTime).Milliseconds())
audit(w, "RPC call. uid=%s, method=%s args=%v result=%s error=%s time=%d", hexutils.BytesToHex(userID), method, args, SafeGenericToString(res), err, time.Since(requestStartTime).Milliseconds())
return res, err
}

Expand Down Expand Up @@ -340,3 +341,58 @@ func withPlainRPCConnection[R any](ctx context.Context, w *Services, execute fun
defer returnConn(w.rpcHTTPConnPool, rpcClient, w.logger)
return execute(rpcClient)
}

func SafeGenericToString[R any](r *R) string {
if r == nil {
return "nil"
}

v := reflect.ValueOf(r).Elem()
t := v.Type()

switch v.Kind() {
case reflect.Struct:
return structToString(v, t)
default:
return fmt.Sprintf("%v", v.Interface())
}
}

func structToString(v reflect.Value, t reflect.Type) string {
var parts []string
for i := 0; i < v.NumField(); i++ {
field := v.Field(i)
fieldType := t.Field(i)
fieldName := fieldType.Name

if !fieldType.IsExported() {
parts = append(parts, fmt.Sprintf("%s: <unexported>", fieldName))
continue
}

fieldStr := fmt.Sprintf("%s: ", fieldName)

switch field.Kind() {
case reflect.Ptr:
if field.IsNil() {
fieldStr += "nil"
} else {
fieldStr += fmt.Sprintf("%v", field.Elem().Interface())
}
case reflect.Slice, reflect.Array:
if field.Len() > 10 {
fieldStr += fmt.Sprintf("%v (length: %d)", field.Slice(0, 10).Interface(), field.Len())
} else {
fieldStr += fmt.Sprintf("%v", field.Interface())
}
case reflect.Struct:
fieldStr += "{...}" // Avoid recursive calls for nested structs
default:
fieldStr += fmt.Sprintf("%v", field.Interface())
}

parts = append(parts, fieldStr)
}

return fmt.Sprintf("%s{%s}", t.Name(), strings.Join(parts, ", "))
}
Loading