Skip to content

Commit d7985e2

Browse files
committed
feat(cli): add PRT consensus to deploy application
1 parent ab6f86b commit d7985e2

5 files changed

Lines changed: 244 additions & 10 deletions

File tree

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ env:
9797
@echo export CARTESI_CONTRACTS_AUTHORITY_FACTORY_ADDRESS="0xC7003566dD09Aa0fC0Ce201aC2769aFAe3BF0051"
9898
@echo export CARTESI_CONTRACTS_APPLICATION_FACTORY_ADDRESS="0xc7006f70875BaDe89032001262A846D3Ee160051"
9999
@echo export CARTESI_CONTRACTS_SELF_HOSTED_APPLICATION_FACTORY_ADDRESS="0xc700285Ab555eeB5201BC00CFD4b2CC8DED90051"
100+
@echo export CARTESI_CONTRACTS_PRT_FACTORY_ADDRESS="0x6e362c9458fE812D4aA796651C64D02C87AbD1cB"
100101
@echo export CARTESI_AUTH_MNEMONIC=\"test test test test test test test test test test test junk\"
101102
@echo export CARTESI_DATABASE_CONNECTION="postgres://postgres:password@localhost:5432/rollupsdb?sslmode=disable"
102103
@echo export CARTESI_SNAPSHOTS_DIR="snapshots"

cmd/cartesi-rollups-cli/root/deploy/application.go

Lines changed: 72 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ var (
2929
applicationRegisterParam bool
3030
applicationTemplateHashParam string
3131
factoryAddressParam string
32+
prtFactoryAddressParam string
33+
deploymentTypePRT bool
3234
)
3335

3436
var applicationCmd = &cobra.Command{
@@ -59,6 +61,9 @@ const applicationExamples = `
5961
# deploy an application contract using an existing consensus, then register the application
6062
- cli deploy application echo-dapp applications/echo-dapp/ --consensus=0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
6163
64+
# deploy an application contract with a PRT consensus, then register the application
65+
- cli deploy application echo-dapp applications/echo-dapp/ --prt
66+
6267
# deploy but don't register into the database
6368
- cli deploy application echo-dapp applications/echo-dapp/ --register=false
6469
@@ -73,6 +78,8 @@ func init() {
7378
"Consensus address. A new authority consensus will be created if this field is left empty.")
7479
applicationCmd.Flags().StringVarP(&factoryAddressParam, "factory", "f", "",
7580
"Application factory address. Default value is retrieved from configuration.")
81+
applicationCmd.Flags().StringVarP(&prtFactoryAddressParam, "prt-factory", "", "",
82+
"PRT Application factory address. Default value is retrieved from configuration.")
7683
applicationCmd.Flags().StringVarP(&applicationOwnerAddressParam, "application-owner", "o", "",
7784
"Application owner address. If not defined, it will be derived from the auth method.")
7885
applicationCmd.Flags().StringVarP(&applicationDataAvailabilityParam, "data-availability", "d", "",
@@ -85,6 +92,8 @@ func init() {
8592
"Start processing the application, requires 'register=true'.")
8693
applicationCmd.Flags().StringVarP(&authorityOwnerAddressParam, "authority-owner", "O", "",
8794
"Authority Owner address. If not defined, it will be derived from the auth method.")
95+
applicationCmd.Flags().BoolVarP(&deploymentTypePRT, "prt", "", false,
96+
"Deploy a PRT application.")
8897

8998
origHelpFunc := applicationCmd.HelpFunc()
9099
applicationCmd.SetHelpFunc(func(command *cobra.Command, strings []string) {
@@ -141,7 +150,9 @@ func runDeployApplication(cmd *cobra.Command, args []string) {
141150
}
142151

143152
var deployment ethutil.IApplicationDeployment
144-
if deploySelfhosted := !cmd.Flags().Changed("consensus"); deploySelfhosted {
153+
if deploymentTypePRT {
154+
deployment, err = buildPrtApplicationDeployment(cmd, args, client, txOpts)
155+
} else if deploySelfhosted := !cmd.Flags().Changed("consensus"); deploySelfhosted {
145156
deployment, err = buildSelfhostedApplicationDeployment(cmd, args, client, txOpts)
146157
} else {
147158
deployment, err = buildApplicationOnlyDeployment(cmd, args, client, txOpts)
@@ -210,6 +221,15 @@ func runDeployApplication(cmd *cobra.Command, args []string) {
210221
application.EpochLength = res.Deployment.EpochLength
211222
application.DataAvailability = res.Deployment.DataAvailability
212223
application.IInputBoxBlock = res.Deployment.IInputBoxBlock
224+
225+
case *ethutil.PRTApplicationDeploymentResult:
226+
application.IApplicationAddress = res.ApplicationResult.ApplicationAddress
227+
application.IConsensusAddress = res.ApplicationResult.Deployment.Consensus
228+
application.IInputBoxAddress = res.ApplicationResult.Deployment.InputBoxAddress
229+
application.TemplateHash = res.ApplicationResult.Deployment.TemplateHash
230+
application.EpochLength = res.ApplicationResult.Deployment.EpochLength
231+
application.DataAvailability = res.ApplicationResult.Deployment.DataAvailability
232+
application.IInputBoxBlock = res.ApplicationResult.Deployment.IInputBoxBlock
213233
default:
214234
panic("unimplemented deployment type\n")
215235
}
@@ -339,7 +359,7 @@ func buildSelfhostedApplicationDeployment(
339359
return request, nil
340360
}
341361

342-
func buildApplicationOnlyDeployment(
362+
func buildApplicationOnlyDeploymentWithoutConsensus(
343363
cmd *cobra.Command,
344364
args []string,
345365
client *ethclient.Client,
@@ -360,11 +380,6 @@ func buildApplicationOnlyDeployment(
360380
return nil, fmt.Errorf("error on parameter factory: %w", err)
361381
}
362382

363-
request.Consensus, err = parseHexAddress(applicationConsensusAddressParam)
364-
if err != nil {
365-
return nil, fmt.Errorf("error on parameter consensus: %w", err)
366-
}
367-
368383
if !cmd.Flags().Changed("template-hash") {
369384
if len(args) >= 2 { // args[1] is mandatory if `template-hash` was absent
370385
request.TemplateHash, err = readHash(args[1])
@@ -402,17 +417,64 @@ func buildApplicationOnlyDeployment(
402417
return nil, fmt.Errorf("error on parameter data-availability: %w", err)
403418
}
404419

420+
request.Salt, err = ethutil.ParseSalt(saltParam)
421+
if err != nil {
422+
return nil, fmt.Errorf("error on parameter salt: %w", err)
423+
}
424+
425+
request.Verbose = verboseParam
426+
return request, nil
427+
}
428+
429+
func buildApplicationOnlyDeployment(
430+
cmd *cobra.Command,
431+
args []string,
432+
client *ethclient.Client,
433+
txOpts *bind.TransactOpts,
434+
) (
435+
*ethutil.ApplicationDeployment,
436+
error,
437+
) {
438+
request, err := buildApplicationOnlyDeploymentWithoutConsensus(cmd, args, client, txOpts)
439+
440+
request.Consensus, err = parseHexAddress(applicationConsensusAddressParam)
441+
if err != nil {
442+
return nil, fmt.Errorf("error on parameter consensus: %w", err)
443+
}
444+
405445
request.Consensus, request.EpochLength, err = customConsensus(client, applicationConsensusAddressParam)
406446
if err != nil {
407447
return nil, fmt.Errorf("error on parameter consensus: %w", err)
408448
}
409449

410-
request.Salt, err = ethutil.ParseSalt(saltParam)
450+
return request, nil
451+
}
452+
453+
func buildPrtApplicationDeployment(
454+
cmd *cobra.Command,
455+
args []string,
456+
client *ethclient.Client,
457+
txOpts *bind.TransactOpts,
458+
) (
459+
*ethutil.PRTApplicationDeployment,
460+
error,
461+
) {
462+
var err error
463+
request := &ethutil.PRTApplicationDeployment{}
464+
if !cmd.Flags().Changed("prt-factory") {
465+
request.FactoryAddress, err = config.GetContractsPrtFactoryAddress()
466+
} else {
467+
request.FactoryAddress, err = parseHexAddress(factoryAddressParam)
468+
}
411469
if err != nil {
412-
return nil, fmt.Errorf("error on parameter salt: %w", err)
470+
return nil, fmt.Errorf("error on parameter factory: %w", err)
471+
}
472+
473+
request.Application, err = buildApplicationOnlyDeploymentWithoutConsensus(cmd, args, client, txOpts)
474+
if err != nil {
475+
return nil, fmt.Errorf("error on application: %w", err)
413476
}
414477

415-
request.Verbose = verboseParam
416478
return request, nil
417479
}
418480

internal/config/generate/Config.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,13 @@ Address of the SelfHostedApplicationFactory contract. Not required, used only by
194194
omit = true
195195
used-by = ["cli"]
196196

197+
[contracts.CARTESI_CONTRACTS_PRT_CONSENSUS_FACTORY_ADDRESS]
198+
go-type = "Address"
199+
description = """
200+
Address of the PRT consensus contract. Not required, used only by the CLI and tests"""
201+
omit = true
202+
used-by = ["cli"]
203+
197204
#
198205
# Snapshot
199206
#

internal/config/generated.go

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/ethutil/prt.go

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
// (c) Cartesi and individual authors (see AUTHORS)
2+
// SPDX-License-Identifier: Apache-2.0 (see LICENSE)
3+
package ethutil
4+
5+
import (
6+
"context"
7+
"encoding/hex"
8+
"fmt"
9+
"math/big"
10+
11+
"github.com/cartesi/rollups-node/pkg/contracts/daveconsensusfactory"
12+
"github.com/cartesi/rollups-node/pkg/contracts/iapplication"
13+
"github.com/ethereum/go-ethereum/accounts/abi/bind"
14+
"github.com/ethereum/go-ethereum/common"
15+
"github.com/ethereum/go-ethereum/core/types"
16+
"github.com/ethereum/go-ethereum/ethclient"
17+
)
18+
19+
type PRTApplicationDeployment struct {
20+
FactoryAddress common.Address
21+
TemplateHash common.Hash
22+
Salt SaltBytes
23+
24+
Application *ApplicationDeployment
25+
}
26+
27+
type PRTApplicationDeploymentResult struct {
28+
Deployment *PRTApplicationDeployment
29+
ApplicationResult *ApplicationDeploymentResult
30+
}
31+
32+
func (me *PRTApplicationDeployment) String() string {
33+
result := ""
34+
result += fmt.Sprintf("PRT application deployment:\n")
35+
result += fmt.Sprintf("\tapplication owner: %v\n", me.Application.OwnerAddress)
36+
if me.Application.Verbose {
37+
result += fmt.Sprintf("\tPRT factory address: %v\n", me.FactoryAddress)
38+
result += fmt.Sprintf("\tAPP factory address: %v\n", me.Application.FactoryAddress)
39+
result += fmt.Sprintf("\ttemplate hash: %v\n", me.Application.TemplateHash)
40+
result += fmt.Sprintf("\tdata availability: 0x%v\n", hex.EncodeToString(me.Application.DataAvailability))
41+
result += fmt.Sprintf("\tsalt: %v\n", me.Application.Salt)
42+
}
43+
return result
44+
}
45+
46+
func (me *PRTApplicationDeploymentResult) String() string {
47+
result := ""
48+
result += fmt.Sprintf("\tapplication address: %v\n", me.ApplicationResult.ApplicationAddress)
49+
result += fmt.Sprintf("\tconsensus address: %v\n", me.ApplicationResult.Deployment.Consensus)
50+
return result
51+
}
52+
53+
func (me *PRTApplicationDeployment) deployPRT(
54+
ctx context.Context,
55+
client *ethclient.Client,
56+
txOpts *bind.TransactOpts,
57+
applicationAddress common.Address,
58+
) (common.Address, error) {
59+
zero := common.Address{}
60+
61+
factory, err := daveconsensusfactory.NewDaveConsensusFactory(me.FactoryAddress, client)
62+
if err != nil {
63+
return zero, fmt.Errorf("failed to instantiate contract: %v", err)
64+
}
65+
tx, err := factory.NewDaveConsensus(txOpts, applicationAddress, me.TemplateHash, me.Salt)
66+
if err != nil {
67+
return zero, fmt.Errorf("transaction failed: %v", err)
68+
}
69+
70+
receipt, err := bind.WaitMined(ctx, client, tx)
71+
if err != nil {
72+
return zero, fmt.Errorf("failed to wait for transaction mining: %v", err)
73+
}
74+
75+
if receipt.Status != 1 {
76+
return zero, fmt.Errorf("transaction failed")
77+
}
78+
79+
// Look for the specific event in the receipt logs
80+
for _, vLog := range receipt.Logs {
81+
// Parse log for DaveConsensusCreated event
82+
event, err := factory.ParseDaveConsensusCreated(*vLog)
83+
if err != nil {
84+
continue // Skip logs that don't match
85+
}
86+
return event.DaveConsensus, nil
87+
}
88+
return zero, fmt.Errorf("failed to find DaveConsensusCreated event in receipt logs")
89+
}
90+
91+
// Do the consensus/application dance based on: https://github.com/cartesi/dave/blob/v1.0.0/cartesi-rollups/contracts/cannonfile.prod-instance.toml
92+
func (me *PRTApplicationDeployment) Deploy(
93+
ctx context.Context,
94+
client *ethclient.Client,
95+
txOpts *bind.TransactOpts,
96+
) (common.Address, IApplicationDeploymentResult, error) {
97+
zero := common.Address{}
98+
result := &PRTApplicationDeploymentResult{}
99+
result.Deployment = me
100+
101+
var err error
102+
applicationAddress, appResult, err := me.Application.Deploy(ctx, client, txOpts)
103+
if err != nil {
104+
return zero, nil, err
105+
}
106+
107+
switch appRes := appResult.(type) {
108+
case *ApplicationDeploymentResult:
109+
result.ApplicationResult = appRes
110+
default:
111+
panic("Application deployment returned an impossible type.")
112+
}
113+
114+
consensus, err := me.deployPRT(ctx, client, txOpts, applicationAddress)
115+
if err != nil {
116+
return zero, nil, fmt.Errorf("failed to deploy PRT contract: %w", err)
117+
}
118+
119+
application, err := iapplication.NewIApplication(applicationAddress, client)
120+
if err != nil {
121+
return zero, nil, fmt.Errorf("failed to instantiate application: %v", err)
122+
}
123+
124+
_, err = sendTransaction(ctx, client, txOpts, big.NewInt(0), GasLimit,
125+
func(txOpts *bind.TransactOpts) (*types.Transaction, error) {
126+
return application.MigrateToOutputsMerkleRootValidator(txOpts, consensus)
127+
},
128+
)
129+
if err != nil {
130+
return zero, nil, fmt.Errorf("failed to create a self hosted application: execution reverted")
131+
}
132+
133+
_, err = sendTransaction(ctx, client, txOpts, big.NewInt(0), GasLimit,
134+
func(txOpts *bind.TransactOpts) (*types.Transaction, error) {
135+
return application.RenounceOwnership(txOpts)
136+
},
137+
)
138+
if err != nil {
139+
return zero, nil, fmt.Errorf("failed to create a self hosted application: execution reverted")
140+
}
141+
142+
result.ApplicationResult.Deployment.Consensus = consensus
143+
return applicationAddress, result, nil
144+
}
145+
146+
func (me *PRTApplicationDeployment) GetFactoryAddress() common.Address {
147+
return me.FactoryAddress
148+
}

0 commit comments

Comments
 (0)