-
Notifications
You must be signed in to change notification settings - Fork 71
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Create a devnet docker image for local development. It uses rollups-contracts own deployment scripts instead of foundry-rs and replaces the shell script previousaly developed for the same purpose.
1 parent
5bd4be4
commit d1ccc10
Showing
15 changed files
with
475 additions
and
653 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// (c) Cartesi and individual authors (see AUTHORS) | ||
// SPDX-License-Identifier: Apache-2.0 (see LICENSE) | ||
|
||
package main | ||
|
||
import ( | ||
"os" | ||
"strconv" | ||
|
||
"github.com/cartesi/rollups-node/internal/services" | ||
) | ||
|
||
const ANVIL_STATE_INTERVAL int = 1 | ||
|
||
func newAnvilService(depConfig DeploymentConfig) services.CommandService { | ||
var s services.CommandService | ||
s.Name = "anvil" | ||
s.HealthcheckPort = 8545 | ||
s.Path = "anvil" | ||
|
||
s.Args = append(s.Args, "--host", depConfig.AnvilIpAddr) | ||
s.Args = append(s.Args, "--dump-state", depConfig.AnvilStateFilePath) | ||
s.Args = append(s.Args, "--state-interval", strconv.Itoa(ANVIL_STATE_INTERVAL)) | ||
s.Args = append(s.Args, "--silent") | ||
|
||
s.Env = append(s.Env, "ANVIL_IP_ADDR=0.0.0.0") | ||
s.Env = append(s.Env, os.Environ()...) | ||
|
||
return s | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
// (c) Cartesi and individual authors (see AUTHORS) | ||
// SPDX-License-Identifier: Apache-2.0 (see LICENSE) | ||
|
||
package main | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"os/exec" | ||
"strings" | ||
|
||
"github.com/cartesi/rollups-node/internal/config" | ||
) | ||
|
||
func deploy(ctx context.Context, | ||
depConfig DeploymentConfig) (DeploymentInfo, error) { | ||
var depInfo DeploymentInfo | ||
|
||
config.InfoLogger.Printf("deployer: deploying %s", depConfig.RollupsContractsPath) | ||
err := deployRollupsContracts(ctx, depConfig) | ||
if err != nil { | ||
return depInfo, fmt.Errorf("could not deploy rollups-contracts: %v", err) | ||
} | ||
|
||
config.InfoLogger.Println("deployer: creating application...") | ||
depInfo, err = createApplication(ctx, depConfig) | ||
if err != nil { | ||
return depInfo, fmt.Errorf("could not create Application: %v", err) | ||
} | ||
|
||
config.InfoLogger.Println("deployer: gathering application info...") | ||
return depInfo, nil | ||
} | ||
|
||
// Create a Rollups Application by calling the necessary factories | ||
func createApplication(ctx context.Context, depConfig DeploymentConfig) (DeploymentInfo, error) { | ||
var depInfo DeploymentInfo | ||
if depConfig.Hash == "" { | ||
return DeploymentInfo{}, fmt.Errorf("machine hash is missing") | ||
} | ||
|
||
// Create the Authority/History pair | ||
addresses, _, err := execContract(ctx, | ||
depConfig, | ||
depConfig.AuthorityHistoryFactoryAddress, | ||
"newAuthorityHistoryPair(address,bytes32)(address,address)", | ||
depConfig.SignerAddress, | ||
depConfig.Salt) | ||
if err != nil { | ||
return DeploymentInfo{}, fmt.Errorf("could not create authority/history pair: %v", err) | ||
} | ||
|
||
depInfo.AuthorityAddress = addresses[0] | ||
depInfo.HistoryAddress = addresses[1] | ||
|
||
// Create the Application, passing the address of the newly created Authority | ||
addresses, blockNumber, err := execContract(ctx, | ||
depConfig, | ||
depConfig.ApplicationFactoryAddress, | ||
"newApplication(address,address,bytes32,bytes32)(address)", | ||
depInfo.AuthorityAddress, | ||
depConfig.SignerAddress, | ||
depConfig.Hash, | ||
depConfig.Salt) | ||
if err != nil { | ||
return DeploymentInfo{}, fmt.Errorf("could not create application: %v", err) | ||
} | ||
|
||
depInfo.ApplicationAddress = addresses[0] | ||
depInfo.BlockNumber = blockNumber | ||
|
||
return depInfo, nil | ||
} | ||
|
||
// Call a contract factory, passing a factory function to be executed. | ||
// Returns the resulting contract address(es) and the corresponding | ||
// block number. | ||
// | ||
// Warning: a second call to a contract with the same arguments will fail. | ||
func execContract(ctx context.Context, | ||
depConfig DeploymentConfig, | ||
args ...string) ([]string, string, error) { | ||
rpcUrl := "http://" + depConfig.AnvilIpAddr + ":" + depConfig.AnvilPort | ||
commonArgs := []string{"--rpc-url", rpcUrl} | ||
commonArgs = append(commonArgs, args...) | ||
|
||
var addresses []string | ||
// Calculate the resulting deterministic address(es) | ||
castCall := exec.CommandContext(ctx, | ||
"cast", | ||
"call") | ||
castCall.Args = append(castCall.Args, commonArgs...) | ||
var outStrBuilder strings.Builder | ||
castCall.Stdout = &outStrBuilder | ||
err := castCall.Run() | ||
if err != nil { | ||
return addresses, "", fmt.Errorf("command failed %v: %v", castCall.Args, err) | ||
} | ||
addresses = strings.Fields(outStrBuilder.String()) | ||
|
||
// Perform actual transaction on the contract | ||
castSend := exec.CommandContext(ctx, | ||
"cast", | ||
"send", | ||
"--json", | ||
"--mnemonic", | ||
depConfig.Mnemonic) | ||
castSend.Args = append(castSend.Args, commonArgs...) | ||
outStrBuilder.Reset() | ||
castSend.Stdout = &outStrBuilder | ||
err = castSend.Run() | ||
if err != nil { | ||
return addresses, "", fmt.Errorf("command failed %v: %v", castSend.Args, err) | ||
} | ||
if !depConfig.IsSilent { | ||
config.InfoLogger.Printf("deployer: executed command %s", castSend.Args) | ||
config.InfoLogger.Printf("deployer: output: %s", outStrBuilder.String()) | ||
} | ||
|
||
// Extract blockNumber from JSON output | ||
jsonMap := make(map[string](any)) | ||
err = json.Unmarshal([]byte([]byte(outStrBuilder.String())), &jsonMap) | ||
if err != nil { | ||
return addresses, "", fmt.Errorf("failed to extract block number, %s", err.Error()) | ||
} | ||
blockNumber := jsonMap["blockNumber"].(string) | ||
|
||
return addresses, blockNumber, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
// (c) Cartesi and individual authors (see AUTHORS) | ||
// SPDX-License-Identifier: Apache-2.0 (see LICENSE) | ||
|
||
package main | ||
|
||
import ( | ||
"context" | ||
"encoding/hex" | ||
"encoding/json" | ||
"fmt" | ||
"os" | ||
|
||
"github.com/cartesi/rollups-node/internal/config" | ||
"github.com/cartesi/rollups-node/internal/services" | ||
"github.com/cartesi/rollups-node/pkg/addresses" | ||
"github.com/cartesi/rollups-node/pkg/ethutil" | ||
"github.com/ethereum/go-ethereum/common" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
const CMD_NAME = "gen-devnet" | ||
|
||
var Cmd = &cobra.Command{ | ||
Use: CMD_NAME, | ||
Short: "Generates a devnet for testing", | ||
Long: `Generates a devnet to be used for testing. | ||
It uses a previously generated Cartesi Machine snapshot | ||
and deploys an Application based upon it's hash file.`, | ||
Run: run, | ||
} | ||
|
||
var ( | ||
hashFile string | ||
anvilStateFilePath string | ||
isSilent bool | ||
|
||
depConfig = DeploymentConfig{ | ||
AnvilPort: "8545", | ||
AuthorityHistoryFactoryAddress: "0x3890A047Cf9Af60731E80B2105362BbDCD70142D", | ||
ApplicationFactoryAddress: common.Address. | ||
Hex(addresses.GetTestBook().CartesiDAppFactory), | ||
SignerAddress: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", | ||
Mnemonic: ethutil.FoundryMnemonic, | ||
Salt: "0x0000000000000000000000000000000000000000000000000000000000000000", | ||
} | ||
) | ||
|
||
func init() { | ||
Cmd.Flags().StringVarP(&hashFile, | ||
"template-hash-file", | ||
"t", | ||
"", | ||
"path for a Cartesi Machine template hash file") | ||
cobra.CheckErr(Cmd.MarkFlagRequired("template-hash-file")) | ||
|
||
Cmd.Flags().StringVarP(&anvilStateFilePath, | ||
"anvil-state-file", | ||
"a", | ||
"./anvil_state.json", | ||
"path for the resulting anvil state file") | ||
Cmd.Flags().BoolVarP(&isSilent, | ||
"silent", | ||
"s", | ||
false, | ||
"verbose output") | ||
} | ||
|
||
func main() { | ||
err := Cmd.Execute() | ||
if err != nil { | ||
os.Exit(1) | ||
} | ||
} | ||
|
||
func run(cmd *cobra.Command, args []string) { | ||
ctx, cancel := context.WithCancel(context.Background()) | ||
defer cancel() | ||
|
||
depConfig.AnvilStateFilePath = anvilStateFilePath | ||
depConfig.IsSilent = isSilent | ||
|
||
value, err := loadEnvVar("ROLLUPS_CONTRACTS_PATH") | ||
if err != nil { | ||
config.ErrorLogger.Println(err) | ||
return | ||
} | ||
depConfig.RollupsContractsPath = value | ||
|
||
value, err = loadEnvVar("ANVIL_IP_ADDR") | ||
if err != nil { | ||
config.ErrorLogger.Println(err) | ||
return | ||
} | ||
depConfig.AnvilIpAddr = value | ||
|
||
// Add machine hash to config | ||
hash, err := readMachineHash(hashFile) | ||
if err != nil { | ||
config.ErrorLogger.Printf("failed to read hash: %v", err) | ||
return | ||
} | ||
depConfig.Hash = hash | ||
|
||
// Supervisor handles only Anvil | ||
ready := make(chan struct{}, 1) | ||
go func() { | ||
var s []services.Service | ||
|
||
s = append(s, newAnvilService(depConfig)) | ||
|
||
supervisor := newSupervisorService(s) | ||
if err := supervisor.Start(ctx, ready); err != nil { | ||
config.ErrorLogger.Printf("%s: %v", supervisor.Name, err) | ||
cancel() | ||
} | ||
}() | ||
|
||
// Deploy rollups-contracts and create Application | ||
go func() { | ||
defer cancel() | ||
select { | ||
case <-ready: | ||
depInfo, err := deploy(ctx, depConfig) //, result) | ||
if err != nil { | ||
config.ErrorLogger.Printf("%s: deployment failed. %v", CMD_NAME, err) | ||
} | ||
|
||
jsonInfo, err := json.MarshalIndent(depInfo, "", "\t") | ||
if err != nil { | ||
config.ErrorLogger.Printf("%s: failed to parse deployment info. %v", CMD_NAME, err) | ||
} else { | ||
os.Stdout.Write(jsonInfo) | ||
} | ||
|
||
case <-ctx.Done(): | ||
} | ||
}() | ||
|
||
<-ctx.Done() | ||
} | ||
|
||
// Read template machine hash from file | ||
func readMachineHash(hashPath string) (string, error) { | ||
data, err := os.ReadFile(hashPath) | ||
if err != nil { | ||
return "", fmt.Errorf("error reading %v (%v)", hashPath, err) | ||
} | ||
|
||
return hex.EncodeToString(data), nil | ||
} | ||
|
||
func newSupervisorService(s []services.Service) services.SupervisorService { | ||
return services.SupervisorService{ | ||
Name: CMD_NAME + "-supervisor", | ||
Services: s, | ||
} | ||
} | ||
|
||
func loadEnvVar(key string) (string, error) { | ||
value, exist := os.LookupEnv(key) | ||
if !exist { | ||
return "", fmt.Errorf("required env var %v is not set", key) | ||
} | ||
|
||
return value, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
// (c) Cartesi and individual authors (see AUTHORS) | ||
// SPDX-License-Identifier: Apache-2.0 (see LICENSE) | ||
|
||
package main | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"os" | ||
"os/exec" | ||
|
||
cp "github.com/otiai10/copy" | ||
) | ||
|
||
const ( | ||
PROJECT_NAME = "rollups-contracts" | ||
ZIP_FILE = PROJECT_NAME + ".tar.gz" | ||
) | ||
|
||
// Deploy rollups-contracts to a local anvil instance | ||
func deployRollupsContracts(ctx context.Context, depConfig DeploymentConfig) error { | ||
tmpDir, err := os.MkdirTemp("", "") | ||
if err != nil { | ||
return fmt.Errorf("failed to create temp dir: %v", err) | ||
} | ||
defer os.RemoveAll(tmpDir) | ||
|
||
//Assumes gitmodule location relative to cmd/gen-devnet | ||
if err := copyContracts(depConfig.RollupsContractsPath, tmpDir); err != nil { | ||
return fmt.Errorf("failed to copy contracts: %v", err) | ||
} | ||
|
||
if err := deployContracts(ctx, depConfig, tmpDir); err != nil { | ||
return fmt.Errorf("failed to deploy contracts: %v", err) | ||
} | ||
return nil | ||
} | ||
|
||
// Copy only the rollups hardhat project | ||
func copyContracts(srcDir string, destDir string) error { | ||
//Assumes hardhat project location from current rollups-contract version | ||
srcPath := srcDir + string(os.PathSeparator) + "onchain" + string(os.PathSeparator) + "rollups" | ||
destPath := destDir + string(os.PathSeparator) + "rollups" | ||
return cp.Copy(srcPath, destPath) | ||
} | ||
|
||
// Deploy contracts by using the project deployment script | ||
func deployContracts(ctx context.Context, depConfig DeploymentConfig, execDir string) error { | ||
cmd := exec.CommandContext(ctx, "yarn", "install") | ||
cmd.Dir = execDir + string(os.PathSeparator) + "rollups" | ||
if err := cmd.Run(); err != nil { | ||
return fmt.Errorf("command '%v' failed with %v", cmd.String(), err) | ||
} | ||
|
||
cmd = exec.CommandContext(ctx, "yarn", "deploy:development") | ||
cmd.Env = os.Environ() | ||
cmd.Env = append(cmd.Env, "RPC_URL=http://"+depConfig.AnvilIpAddr+":"+depConfig.AnvilPort) | ||
cmd.Dir = execDir + string(os.PathSeparator) + "rollups" | ||
if !depConfig.IsSilent { | ||
cmd.Stdout = os.Stdout | ||
} | ||
|
||
if err := cmd.Run(); err != nil { | ||
return fmt.Errorf("command '%v' failed with %v", cmd.String(), err) | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
// (c) Cartesi and individual authors (see AUTHORS) | ||
// SPDX-License-Identifier: Apache-2.0 (see LICENSE) | ||
|
||
package main | ||
|
||
type DeploymentInfo struct { | ||
AuthorityAddress string `json:"CARTESI_CONTRACTS_AUTHORITY_ADDRESS"` | ||
HistoryAddress string `json:"CARTESI_CONTRACTS_HISTORY_ADDRESS"` | ||
ApplicationAddress string `json:"CARTESI_CONTRACTS_APPLICATION_ADDRESS"` | ||
BlockNumber string `json:"CARTESI_CONTRACTS_APPLICATION_DEPLOYMENT_BLOCK_NUMBER"` | ||
} | ||
|
||
type DeploymentConfig struct { | ||
AnvilIpAddr string | ||
AnvilPort string | ||
AnvilStateFilePath string | ||
RollupsContractsPath string | ||
AuthorityHistoryFactoryAddress string | ||
ApplicationFactoryAddress string | ||
SignerAddress string | ||
Mnemonic string | ||
Salt string | ||
Hash string | ||
IsSilent bool | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.