Skip to content

Commit

Permalink
Add gh actions script to retrieve funds from the bridge
Browse files Browse the repository at this point in the history
  • Loading branch information
BedrockSquirrel committed Oct 2, 2023
1 parent 42b98cf commit 76f4fc6
Show file tree
Hide file tree
Showing 7 changed files with 354 additions and 0 deletions.
70 changes: 70 additions & 0 deletions .github/workflows/manual-recover-network-funds.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Run script to retrieve funds from an existing testnet deployment

name: '[M] Recover Testnet Funds'
run-name: '[M] Recover Testnet Funds ( ${{ github.event.inputs.testnet_type }} )'
on:
workflow_dispatch:
inputs:
testnet_type:
description: 'Testnet Type'
required: true
default: 'dev-testnet'
type: choice
options:
- 'dev-testnet'
- 'testnet'
- 'sepolia-testnet'
acc_to_pay:
description: 'Address which will receive the funds'
required: true
default: '0x563EAc5dfDFebA3C53c2160Bf1Bd62941E3D0005'
type: string
mgmt_contract_addr:
description: 'Deployed management contract which will be used to request the funds'
required: true
type: string

jobs:
recover-network-funds:
runs-on: ubuntu-latest
environment:
name: ${{ github.event.inputs.testnet_type }}
steps:
- uses: actions/checkout@v3

- name: 'Login to Azure docker registry'
uses: azure/docker-login@v1
with:
login-server: testnetobscuronet.azurecr.io
username: testnetobscuronet
password: ${{ secrets.REGISTRY_PASSWORD }}

- name: 'Build and push docker image'
run: |
DOCKER_BUILDKIT=1 docker build -t ${{vars.L2_HARDHATDEPLOYER_DOCKER_BUILD_TAG}} -f tools/hardhatdeployer/Dockerfile .
docker push ${{vars.L2_HARDHATDEPLOYER_DOCKER_BUILD_TAG}}
- name: 'Deploy L2 contracts'
id: deployL2Contracts
shell: bash
run: |
go run ./testnet/launcher/fundsrecovery/cmd \
-l1_http_url=${{ secrets.L1_HTTP_URL }} \
-private_key=${{ secrets.WORKER_PK }} \
-mgmt_contract_addr=${{ github.event.inputs.mgmt_contract_addr }} \
-docker_image=${{vars.L2_HARDHATDEPLOYER_DOCKER_BUILD_TAG}} \
-acc_to_pay=${{ github.event.inputs.acc_to_pay }}
- name: 'Save container logs on failure'
if: failure()
run: |
docker logs `docker ps -aqf "name=recover-funds"` > recover-funds.out 2>&1
- name: 'Upload container logs on failure'
uses: actions/upload-artifact@v3
if: failure()
with:
name: recover-funds
path: |
recover-funds.out
retention-days: 2
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {HardhatRuntimeEnvironment} from 'hardhat/types';
import {DeployFunction} from 'hardhat-deploy/types';


const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
const layer1 = hre.companionNetworks.layer1;

const {deployer} = await hre.getNamedAccounts();
const l1Accs = await layer1.getNamedAccounts();

const mgmtContractAddress = process.env.MGMT_CONTRACT_ADDRESS!!
// todo: if we want to support this we need to add the payAcc address param to the RetrieveAllBridgeFunds solidity defn
const addressToPay = process.env.ACC_TO_PAY!!

const mgmtContract = (await hre.ethers.getContractFactory('ManagementContract')).attach(mgmtContractAddress)
const tx = await mgmtContract.RetrieveAllBridgeFunds();
const receipt = await tx.wait();

// Check the receipt for success, logs, etc.
if (receipt.status === 1) {
console.log("Successfully recovered funds from the bridge.");
} else {
console.log("Recovery transaction failed");
}
};

export default func;
// No dependencies
36 changes: 36 additions & 0 deletions testnet/launcher/fundsrecovery/cmd/cli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package main

import (
"flag"
)

// FundsRecoveryConfigCLI represents the configurations passed into the deployer over CLI
type FundsRecoveryConfigCLI struct {
l1HTTPURL string
privateKey string
dockerImage string
mgmtContractAddress string
accToPay string
}

// ParseConfigCLI returns a NodeConfigCLI based the cli params and defaults.
func ParseConfigCLI() *FundsRecoveryConfigCLI {
cfg := &FundsRecoveryConfigCLI{}
flagUsageMap := getFlagUsageMap()

l1HTTPURL := flag.String(l1HTTPURLFlag, "", flagUsageMap[l1HTTPURLFlag])
privateKey := flag.String(privateKeyFlag, "", flagUsageMap[privateKeyFlag])
dockerImage := flag.String(dockerImageFlag, "", flagUsageMap[dockerImageFlag])
mgmtContractAddr := flag.String(mgmtContractAddrFlag, "", flagUsageMap[mgmtContractAddrFlag])
accToPay := flag.String(accToPayFlag, "", flagUsageMap[accToPayFlag])

flag.Parse()

cfg.l1HTTPURL = *l1HTTPURL
cfg.privateKey = *privateKey
cfg.dockerImage = *dockerImage
cfg.mgmtContractAddress = *mgmtContractAddr
cfg.accToPay = *accToPay

return cfg
}
22 changes: 22 additions & 0 deletions testnet/launcher/fundsrecovery/cmd/cli_flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package main

// Flag names.
const (
l1HTTPURLFlag = "l1_http_url"
privateKeyFlag = "private_key"
dockerImageFlag = "docker_image"
mgmtContractAddrFlag = "mgmt_contract_addr"
accToPayFlag = "acc_to_pay"
)

// Returns a map of the flag usages.
// While we could just use constants instead of a map, this approach allows us to test that all the expected flags are defined.
func getFlagUsageMap() map[string]string {
return map[string]string{
l1HTTPURLFlag: "Layer 1 network http RPC addr",
privateKeyFlag: "L1 mgmt contract owning key",
dockerImageFlag: "Docker image to run",
mgmtContractAddrFlag: "Address of the management contract",
accToPayFlag: "Address to receive the recovered funds",
}
}
40 changes: 40 additions & 0 deletions testnet/launcher/fundsrecovery/cmd/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package main

import (
"fmt"
"os"

funds "github.com/obscuronet/go-obscuro/testnet/launcher/fundsrecovery"
)

func main() {
cliConfig := ParseConfigCLI()

fundsRecovery, err := funds.NewFundsRecovery(
funds.NewFundsRecoveryConfig(
funds.WithL1HTTPURL(cliConfig.l1HTTPURL),
funds.WithL1PrivateKey(cliConfig.privateKey),
funds.WithMgmtContractAddress(cliConfig.mgmtContractAddress),
funds.WithDockerImage(cliConfig.dockerImage),
funds.WithAccToPay(cliConfig.accToPay),
),
)
if err != nil {
fmt.Println("unable to configure the funds recovery - ", err)
os.Exit(1)
}

err = fundsRecovery.Start()
if err != nil {
fmt.Println("unable to start the funds recovery - ", err)
os.Exit(1)
}

err = fundsRecovery.WaitForFinish()
if err != nil {
fmt.Println("unexpected error waiting for funds recovery to finish - ", err)
os.Exit(1)
}
fmt.Println("Funds recovery was successful...")
os.Exit(0)
}
53 changes: 53 additions & 0 deletions testnet/launcher/fundsrecovery/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package fundsrecovery

// Option is a function that applies configs to a Config Object
type Option = func(c *Config)

// Config holds the properties that configure the package
type Config struct {
l1HTTPURL string
l1privateKey string
mgmtContractAddress string
dockerImage string
accToPay string
}

func NewFundsRecoveryConfig(opts ...Option) *Config {
defaultConfig := &Config{}

for _, opt := range opts {
opt(defaultConfig)
}

return defaultConfig
}

func WithL1HTTPURL(s string) Option {
return func(c *Config) {
c.l1HTTPURL = s
}
}

func WithL1PrivateKey(s string) Option {
return func(c *Config) {
c.l1privateKey = s
}
}

func WithMgmtContractAddress(s string) Option {
return func(c *Config) {
c.mgmtContractAddress = s
}
}

func WithDockerImage(s string) Option {
return func(c *Config) {
c.dockerImage = s
}
}

func WithAccToPay(s string) Option {
return func(c *Config) {
c.accToPay = s
}
}
105 changes: 105 additions & 0 deletions testnet/launcher/fundsrecovery/docker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package fundsrecovery

import (
"bytes"
"context"
"fmt"
"io"
"time"

"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
"github.com/obscuronet/go-obscuro/go/common/docker"
"github.com/sanity-io/litter"
)

type FundsRecovery struct {
cfg *Config
containerID string
}

func NewFundsRecovery(cfg *Config) (*FundsRecovery, error) {
return &FundsRecovery{
cfg: cfg,
}, nil // todo (@pedro) - add validation
}

func (n *FundsRecovery) Start() error {
fmt.Printf("Starting L2 contract deployer with config: \n%s\n\n", litter.Sdump(*n.cfg))

cmds := []string{
"npx", "hardhat", "obscuro:deploy",
"--network", "layer1",
}

envs := map[string]string{
"ACC_TO_PAY": n.cfg.accToPay,
"MGMT_CONTRACT_ADDRESS": n.cfg.mgmtContractAddress,
"NETWORK_JSON": fmt.Sprintf(`
{
"layer1" : {
"url" : "%s",
"live" : false,
"saveDeployments" : true,
"deploy": [
"deployment_scripts/testnet/recoverfunds"
],
"accounts": [
"%s"
]
}
}
`, n.cfg.l1HTTPURL, n.cfg.l1privateKey),
}

containerID, err := docker.StartNewContainer("recover-funds", n.cfg.dockerImage, cmds, nil, envs, nil, nil)
if err != nil {
return err
}
n.containerID = containerID
return nil
}

func (n *FundsRecovery) GetID() string {
return n.containerID
}

func (n *FundsRecovery) WaitForFinish() error {
cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil {
return err
}
defer cli.Close()

// make sure the container has finished execution
err = docker.WaitForContainerToFinish(n.containerID, 10*time.Minute)
if err != nil {
n.PrintLogs(cli)
return err
}

// if we want to read anything from the container logs we can do it here (see RetrieveL1ContractAddresses as example)

return nil
}

func (n *FundsRecovery) PrintLogs(cli *client.Client) {
logsOptions := types.ContainerLogsOptions{
ShowStdout: true,
ShowStderr: true,
}

// Read the container logs
out, err := cli.ContainerLogs(context.Background(), n.containerID, logsOptions)
if err != nil {
fmt.Printf("Error printing out container %s logs... %v\n", n.containerID, err)
}
defer out.Close()

var buf bytes.Buffer
_, err = io.Copy(&buf, out)
if err != nil {
fmt.Printf("Error getting logs for container %s\n", n.containerID)
}
fmt.Println(buf.String())
}

0 comments on commit 76f4fc6

Please sign in to comment.