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

Management contract challenge period #2245

Merged
merged 7 commits into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .github/workflows/manual-deploy-testnet-l2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,40 @@ jobs:
grant-sequencers.out
retention-days: 7

set-challenge-period:
needs:
- build
- check-obscuro-is-healthy
runs-on: ubuntu-latest
environment:
name: ${{ github.event.inputs.testnet_type }}
steps:
- uses: actions/checkout@v4

- name: 'Set challenge period on management contract'
id: setChallengePeriod
shell: bash
run: |
go run ./testnet/launcher/l1challengeperiod/cmd \
-l1_http_url=${{ secrets.L1_HTTP_URL }} \
-private_key=${{ secrets.ACCOUNT_PK_WORKER }} \
-management_contract_addr=${{ needs.build.outputs.MGMT_CONTRACT_ADDR }} \
-docker_image=${{ vars.L2_HARDHATDEPLOYER_DOCKER_BUILD_TAG }} \
-l1_challenge_period=${{ vars.L1_CHALLENGE_PERIOD }} \
echo "Setting challenge period to ${{ vars.L1_CHALLENGE_PERIOD }}"

- name: 'Save challenge period container logs'
run: |
docker logs `docker ps -aqf "name=set-challenge-period"` > set-challenge-period.out 2>&1

- name: 'Upload challenge period container logs'
uses: actions/upload-artifact@v4
with:
name: set-challenge-period
path: |
set-challenge-period.out
retention-days: 7

deploy-l2-contracts:
needs:
- build
Expand Down
56 changes: 54 additions & 2 deletions contracts/generated/ManagementContract/ManagementContract.go

Large diffs are not rendered by default.

39 changes: 39 additions & 0 deletions contracts/scripts/delay/001_set_challenge_period.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { ethers } from "hardhat";
import { ManagementContract } from "../../typechain-types";

const setChallengePeriod = async function (mgmtContractAddr: string, challengPeriod: number) {
const managementContract = await ethers.getContractAt(
"ManagementContract",
mgmtContractAddr
) as ManagementContract;


console.log(`Setting challenge period to: ${challengPeriod}`);
const tx = await managementContract.SetChallengePeriod(BigInt(challengPeriod));
await tx.wait();
console.log(`Successfully set challenge period to: ${challengPeriod}`);

const mgmtContractChallengePeriod = await managementContract.GetChallengePeriod();
if (BigInt(challengPeriod) !== mgmtContractChallengePeriod) {
throw new Error(`Failed to set the challenge period to: ${challengPeriod}. Returned value is: ${mgmtContractChallengePeriod}`);
}
}

const mgmtContractAddr = process.env.MGMT_CONTRACT_ADDRESS;
const challengePeriod = process.env.L1_CHALLENGE_PERIOD ?
Number(process.env.L1_CHALLENGE_PERIOD) : 0;


if (!mgmtContractAddr) {
console.error("Missing required environment variables: MGMT_CONTRACT_ADDRESS");
process.exit(1);
}

setChallengePeriod(mgmtContractAddr, challengePeriod)
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});

export default setChallengePeriod;
21 changes: 17 additions & 4 deletions contracts/src/management/ManagementContract.sol
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ contract ManagementContract is Initializable, OwnableUpgradeable {

bytes32 public lastBatchHash;

uint256 private challengePeriod;

function initialize() public initializer {
__Ownable_init(msg.sender);
lastBatchSeqNo = 0;
Expand Down Expand Up @@ -118,7 +120,7 @@ contract ManagementContract is Initializable, OwnableUpgradeable {
require(block.number < (blockNum + 255), "Block binding too old");
require(block.number != blockNum, "Cannot bind to the block that is being currently mined");

bytes32 knownBlockHash = blockhash(blockNum);
bytes32 knownBlockHash = blockhash(blockNum);
require(knownBlockHash != 0x0, "Unknown block hash");
require(knownBlockHash == providedBlockHash, "Block binding mismatch");
require(rollups.toUniqueForkID[rollupNumber] == forkID, "Invalid forkID");
Expand All @@ -131,14 +133,15 @@ contract ManagementContract is Initializable, OwnableUpgradeable {
bytes32 bundleHash = bytes32(0);

for(uint256 i = 0; i < crossChainHashes.length; i++) {
merkleMessageBus.addStateRoot(bytes32(crossChainHashes[i]), block.timestamp); //todo: change the activation time.
merkleMessageBus.addStateRoot(
bytes32(crossChainHashes[i]), block.timestamp + challengePeriod
);
bundleHash = keccak256(abi.encode(bundleHash, bytes32(crossChainHashes[i])));
}

isBundleSaved[bundleHash] = true;
}

// TODO: ensure challenge period is added on top of block timestamp.
function pushCrossChainMessages(Structs.HeaderCrossChainData calldata crossChainData) internal {
uint256 messagesLength = crossChainData.messages.length;
for (uint256 i = 0; i < messagesLength; ++i) {
Expand Down Expand Up @@ -263,4 +266,14 @@ contract ManagementContract is Initializable, OwnableUpgradeable {
function GetImportantContractKeys() public view returns(string[] memory) {
return importantContractKeys;
}
}

// Return the challenge period delay for message bus root
function GetChallengePeriod() public view returns (uint256) {
return challengePeriod;
}

// Sets the challenge period for message bus root (owner only)
function SetChallengePeriod(uint256 _delay) public onlyOwner {
challengePeriod = _delay;
}
}
35 changes: 35 additions & 0 deletions testnet/launcher/l1challengeperiod/cmd/cli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package main

import (
"flag"
)

// L1ChallengePeriodConfigCLI represents the configurations needed to grant enclaves sequencer roles over CLI
type L1ChallengePeriodConfigCLI struct {
l1HTTPURL string
privateKey string
mgmtContractAddress string
dockerImage string
challengePeriod int
}

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

l1HTTPURL := flag.String(l1HTTPURLFlag, "http://eth2network:8025", flagUsageMap[l1HTTPURLFlag])
privateKey := flag.String(privateKeyFlag, "", flagUsageMap[privateKeyFlag])
mgmtContractAddress := flag.String(mgmtContractAddressFlag, "", flagUsageMap[mgmtContractAddressFlag])
dockerImage := flag.String(dockerImageFlag, "testnetobscuronet.azurecr.io/obscuronet/hardhatdeployer:latest", flagUsageMap[dockerImageFlag])
challengePeriod := flag.Int(challengePeriodFlag, 0, flagUsageMap[challengePeriodFlag])
flag.Parse()

cfg.l1HTTPURL = *l1HTTPURL
cfg.privateKey = *privateKey
cfg.mgmtContractAddress = *mgmtContractAddress
cfg.dockerImage = *dockerImage
cfg.challengePeriod = *challengePeriod

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

// Flag names.
const (
l1HTTPURLFlag = "l1_http_url"
privateKeyFlag = "private_key"
mgmtContractAddressFlag = "management_contract_addr"
dockerImageFlag = "docker_image"
contractsEnvFileFlag = "contracts_env_file"
challengePeriodFlag = "l1_challenge_period"
)

// 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 and L2 private key used in the node",
mgmtContractAddressFlag: "L1 management contract address",
dockerImageFlag: "Docker image to run",
contractsEnvFileFlag: "If set, it will write the contract addresses to the file",
challengePeriodFlag: "L1 delay when setting message bus root for the challenge period",
}
}
40 changes: 40 additions & 0 deletions testnet/launcher/l1challengeperiod/cmd/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package main

import (
"fmt"
"os"

l1cp "github.com/ten-protocol/go-ten/testnet/launcher/l1challengeperiod"
)

func main() {
cliConfig := ParseConfigCLI()

l1challengeperiod, err := l1cp.NewSetChallengePeriod(
l1cp.NewChallengePeriodConfig(
l1cp.WithL1HTTPURL(cliConfig.l1HTTPURL),
l1cp.WithPrivateKey(cliConfig.privateKey),
l1cp.WithDockerImage(cliConfig.dockerImage),
l1cp.WithMgmtContractAddress(cliConfig.mgmtContractAddress),
l1cp.WithChallengePeriod(cliConfig.challengePeriod),
),
)
if err != nil {
fmt.Println("unable to configure l1 contract deployer - %w", err)
os.Exit(1)
}

err = l1challengeperiod.Start()
if err != nil {
fmt.Println("unable to start l1 contract deployer - %w", err)
os.Exit(1)
}

err = l1challengeperiod.WaitForFinish()
if err != nil {
fmt.Println("unexpected error waiting for set challenge period script to finish - %w", err)
os.Exit(1)
}
fmt.Println("L1 challenge period was successfully set...")
os.Exit(0)
}
55 changes: 55 additions & 0 deletions testnet/launcher/l1challengeperiod/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package l1grantsequencers

// 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
privateKey string
mgmtContractAddress string
dockerImage string
challengePeriod int

// debugEnabled bool
}

func NewChallengePeriodConfig(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 WithPrivateKey(s string) Option {
return func(c *Config) {
c.privateKey = 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 WithChallengePeriod(i int) Option {
return func(c *Config) {
c.challengePeriod = i
}
}
105 changes: 105 additions & 0 deletions testnet/launcher/l1challengeperiod/docker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package l1grantsequencers

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

"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
"github.com/ten-protocol/go-ten/go/common/docker"
)

type SetChallengePeriod struct {
cfg *Config
containerID string
}

func NewSetChallengePeriod(cfg *Config) (*SetChallengePeriod, error) {
return &SetChallengePeriod{
cfg: cfg,
}, nil
}

func (s *SetChallengePeriod) Start() error {
var err error
cmds := []string{
"npx",
"hardhat",
"run",
"--network",
"layer1",
"scripts/delay/001_set_challenge_period.ts",
}

envs := map[string]string{
"NETWORK_JSON": fmt.Sprintf(`{
"layer1": {
"url": "%s",
"live": false,
"saveDeployments": true,
"accounts": [ "%s" ]
}
}`, s.cfg.l1HTTPURL, s.cfg.privateKey),
"MGMT_CONTRACT_ADDRESS": s.cfg.mgmtContractAddress,
"L1_CHALLENGE_PERIOD": strconv.Itoa(s.cfg.challengePeriod),
}

containerID, err := docker.StartNewContainer(
"set-challenge-period",
s.cfg.dockerImage,
cmds,
nil,
envs,
nil,
nil,
false,
)
if err != nil {
return err
}
s.containerID = containerID
return nil
}

func (s *SetChallengePeriod) WaitForFinish() error {
cli, err := client.NewClientWithOpts(client.FromEnv)
if err != nil {
return fmt.Errorf("failed to create docker client: %w", err)
}
defer cli.Close()

// make sure the container has finished execution
err = docker.WaitForContainerToFinish(s.containerID, 15*time.Minute)
if err != nil {
fmt.Println("Error waiting for container to finish: ", err)
s.PrintLogs(cli)
return err
}

return nil
}

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

// Read the container logs
out, err := cli.ContainerLogs(context.Background(), s.containerID, logsOptions)
if err != nil {
fmt.Printf("Error printing out container %s logs... %v\n", s.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", s.containerID)
}
fmt.Println(buf.String())
}
Loading