diff --git a/call_get_code.sh b/call_get_code.sh new file mode 100755 index 000000000..ab31552e5 --- /dev/null +++ b/call_get_code.sh @@ -0,0 +1,6 @@ +#!/usr/bin/bash + +curl http://127.0.0.1:8545 \ + -X POST \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","method":"eth_getCode","params": ["0xA1b8EB1F13d8D5Db976a653BbDF8972cfD14691C", "0x10"],"id":1}' \ No newline at end of file diff --git a/cmd/cartesi-rollups-cli/root/app/add/add.go b/cmd/cartesi-rollups-cli/root/app/add/add.go index 3deff428b..53f2303d0 100644 --- a/cmd/cartesi-rollups-cli/root/app/add/add.go +++ b/cmd/cartesi-rollups-cli/root/app/add/add.go @@ -34,7 +34,6 @@ var ( templateHash string inputBoxDeploymentBlockNumber uint64 snapshotUri string - epochLength uint64 status string ) @@ -72,14 +71,6 @@ func init() { "Application snapshot URI", ) - Cmd.Flags().Uint64VarP( - &epochLength, - "epoch-length", - "e", - 1, - "Application epoch length in blocks", - ) - Cmd.Flags().StringVarP( &status, "status", @@ -113,8 +104,6 @@ func run(cmd *cobra.Command, args []string) { ContractAddress: common.HexToAddress(applicationAddress), TemplateHash: common.HexToHash(templateHash), LastProcessedBlock: inputBoxDeploymentBlockNumber, - SnapshotURI: snapshotUri, - EpochLength: epochLength, Status: applicationStatus, } diff --git a/docs/cli/cartesi-rollups-cli_app_add.md b/docs/cli/cartesi-rollups-cli_app_add.md index 669666d3e..8c4552420 100644 --- a/docs/cli/cartesi-rollups-cli_app_add.md +++ b/docs/cli/cartesi-rollups-cli_app_add.md @@ -17,7 +17,6 @@ cartesi-rollups-cli app add -a 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -n 10 ``` -a, --address string Application contract address - -e, --epoch-length uint Application epoch length in blocks (default 1) -h, --help help for add -n, --inputbox-block-number uint InputBox deployment block number -u, --snapshot-uri string Application snapshot URI diff --git a/internal/node/model/models.go b/internal/node/model/models.go index b70b6b1b2..d2338852f 100644 --- a/internal/node/model/models.go +++ b/internal/node/model/models.go @@ -12,9 +12,10 @@ type ( Hash = common.Hash Address = common.Address InputCompletionStatus string - ClaimStatus string - ApplicationStatus string - DefaultBlock string + EpochStatus string + // ClaimStatus string + ApplicationStatus string + DefaultBlock string ) const ( @@ -29,11 +30,21 @@ const ( ) const ( - ClaimStatusPending ClaimStatus = "PENDING" - ClaimStatusSubmitted ClaimStatus = "SUBMITTED" - ClaimStatusFinalized ClaimStatus = "FINALIZED" + EpochStatusReceivingInputs EpochStatus = "RECEIVING_INPUTS" + EpochStatusReceivedLastInput EpochStatus = "RECEIVED_LAST_INPUT" + EpochStatusProcessedAllInputs EpochStatus = "PROCESSED_ALL_INPUTS" + EpochStatusCalculatedClaim EpochStatus = "CALCULATED_CLAIM" + EpochStatusSubmittedClaim EpochStatus = "SUBMITTED_CLAIM" + EpochStatusAcceptedClaim EpochStatus = "ACCEPTED_CLAIM" + EpochStatusRejectedClaim EpochStatus = "REJECTED_CLAIM" ) +// const ( +// ClaimStatusPending ClaimStatus = "PENDING" +// ClaimStatusSubmitted ClaimStatus = "SUBMITTED" +// ClaimStatusFinalized ClaimStatus = "FINALIZED" +// ) + const ( ApplicationStatusRunning ApplicationStatus = "RUNNING" ApplicationStatusNotRunning ApplicationStatus = "NOT RUNNING" @@ -52,18 +63,28 @@ type NodePersistentConfig struct { InputBoxAddress Address ChainId uint64 IConsensusAddress Address + EpochLength uint64 } type Application struct { Id uint64 ContractAddress Address TemplateHash Hash - SnapshotURI string LastProcessedBlock uint64 - EpochLength uint64 Status ApplicationStatus } +type Epoch struct { + Id uint64 + AppAddress Address + Index uint64 + FirstBlock uint64 + LastBlock uint64 + ClaimHash *Hash + TransactionHash *Hash + Status EpochStatus +} + type Input struct { Id uint64 Index uint64 @@ -73,6 +94,7 @@ type Input struct { MachineHash *Hash OutputsHash *Hash AppAddress Address + EpochId uint64 } type Output struct { @@ -91,11 +113,11 @@ type Report struct { InputId uint64 } -type Claim struct { - Id uint64 - Index uint64 - Status ClaimStatus - OutputMerkleRootHash Hash - TransactionHash *Hash - AppAddress Address -} +// type Claim struct { +// Id uint64 +// Index uint64 +// Status ClaimStatus +// OutputMerkleRootHash Hash +// TransactionHash *Hash +// AppAddress Address +// } diff --git a/internal/repository/base.go b/internal/repository/base.go index 9bfc6dac4..5c6420665 100644 --- a/internal/repository/base.go +++ b/internal/repository/base.go @@ -59,13 +59,15 @@ func (pg *Database) InsertNodeConfig( input_box_deployment_block, input_box_address, chain_id, - iconsensus_address) + iconsensus_address, + epoch_length) SELECT @defaultBlock, @deploymentBlock, @inputBoxAddress, @chainId, - @iConsensusAddress + @iConsensusAddress, + @epochLength WHERE NOT EXISTS (SELECT * FROM node_config)` args := pgx.NamedArgs{ @@ -74,6 +76,7 @@ func (pg *Database) InsertNodeConfig( "inputBoxAddress": config.InputBoxAddress, "chainId": config.ChainId, "iConsensusAddress": config.IConsensusAddress, + "epochLength": config.EpochLength, } _, err := pg.db.Exec(ctx, query, args) @@ -92,24 +95,18 @@ func (pg *Database) InsertApplication( INSERT INTO application (contract_address, template_hash, - snapshot_uri, last_processed_block, - epoch_length, status) VALUES (@contractAddress, @templateHash, - @snapshotUri, @lastProcessedBlock, - @epochLength, @status)` args := pgx.NamedArgs{ "contractAddress": app.ContractAddress, "templateHash": app.TemplateHash, - "snapshotUri": app.SnapshotURI, "lastProcessedBlock": app.LastProcessedBlock, - "epochLength": app.EpochLength, "status": app.Status, } @@ -121,6 +118,46 @@ func (pg *Database) InsertApplication( return nil } +func (pg *Database) InsertEpoch( + ctx context.Context, + epoch *Epoch, +) error { + query := ` + INSERT INTO epoch + (index, + first_block, + last_block, + transaction_hash, + claim_hash, + status, + application_address) + VALUES + (@index, + @firstBlock, + @lastBlock, + @transactionHash, + @claimHash, + @status, + @applicationAddress)` + + args := pgx.NamedArgs{ + "index": epoch.Index, + "firstBlock": epoch.FirstBlock, + "lastBlock": epoch.LastBlock, + "transactionHash": epoch.TransactionHash, + "claimHash": epoch.ClaimHash, + "status": epoch.Status, + "applicationAddress": epoch.AppAddress, + } + + _, err := pg.db.Exec(ctx, query, args) + if err != nil { + return fmt.Errorf("%w: %w", ErrInsertRow, err) + } + + return nil +} + func (pg *Database) InsertInput( ctx context.Context, input *Input, @@ -133,7 +170,8 @@ func (pg *Database) InsertInput( block_number, machine_hash, outputs_hash, - application_address) + application_address, + epoch_id) VALUES (@index, @status, @@ -141,7 +179,8 @@ func (pg *Database) InsertInput( @blockNumber, @machineHash, @outputsHash, - @applicationAddress)` + @applicationAddress, + @epochId)` args := pgx.NamedArgs{ "index": input.Index, @@ -151,6 +190,7 @@ func (pg *Database) InsertInput( "machineHash": input.MachineHash, "outputsHash": input.OutputsHash, "applicationAddress": input.AppAddress, + "epochId": input.EpochId, } _, err := pg.db.Exec(ctx, query, args) @@ -220,40 +260,6 @@ func (pg *Database) InsertReport( return nil } -func (pg *Database) InsertClaim( - ctx context.Context, - claim *Claim, -) error { - query := ` - INSERT INTO claim - (index, - output_merkle_root_hash, - transaction_hash, - status, - application_address) - VALUES - (@index, - @outputMerkleRootHash, - @transactionHash, - @status, - @applicationAddress)` - - args := pgx.NamedArgs{ - "index": claim.Index, - "outputMerkleRootHash": claim.OutputMerkleRootHash, - "transactionHash": claim.TransactionHash, - "status": claim.Status, - "applicationAddress": claim.AppAddress, - } - - _, err := pg.db.Exec(ctx, query, args) - if err != nil { - return fmt.Errorf("%w: %w", ErrInsertRow, err) - } - - return nil -} - func (pg *Database) GetNodeConfig( ctx context.Context, ) (*NodePersistentConfig, error) { @@ -305,9 +311,7 @@ func (pg *Database) GetApplication( id uint64 contractAddress Address templateHash Hash - snapshotUri string lastProcessedBlock uint64 - epochLength uint64 status ApplicationStatus ) @@ -316,9 +320,7 @@ func (pg *Database) GetApplication( id, contract_address, template_hash, - snapshot_uri, last_processed_block, - epoch_length, status FROM application @@ -333,9 +335,7 @@ func (pg *Database) GetApplication( &id, &contractAddress, &templateHash, - &snapshotUri, &lastProcessedBlock, - &epochLength, &status, ) if err != nil { @@ -350,15 +350,82 @@ func (pg *Database) GetApplication( Id: id, ContractAddress: contractAddress, TemplateHash: templateHash, - SnapshotURI: snapshotUri, LastProcessedBlock: lastProcessedBlock, - EpochLength: epochLength, Status: status, } return &app, nil } +func (pg *Database) GetEpoch( + ctx context.Context, + indexKey uint64, + appAddressKey Address, +) (*Epoch, error) { + var ( + id uint64 + index uint64 + firstBlock uint64 + lastBlock uint64 + transactionHash *Hash + claimHash *Hash + status EpochStatus + applicationAddress Address + ) + + query := ` + SELECT + id, + index, + first_block, + last_block, + transaction_hash, + claim_hash, + status, + application_address + FROM + epoch + WHERE + index=@index and application_address=@appAddress` + + args := pgx.NamedArgs{ + "index": indexKey, + "appAddress": appAddressKey, + } + + err := pg.db.QueryRow(ctx, query, args).Scan( + &id, + &index, + &firstBlock, + &lastBlock, + &transactionHash, + &claimHash, + &status, + &applicationAddress, + ) + if err != nil { + if errors.Is(err, pgx.ErrNoRows) { + slog.Info("GetEpoch returned no rows", "service", "repository") + return nil, nil + } + return nil, fmt.Errorf("GetEpoch QueryRow failed: %w\n", err) + } + + epoch := Epoch{ + Id: id, + Index: index, + FirstBlock: firstBlock, + LastBlock: lastBlock, + TransactionHash: transactionHash, + ClaimHash: claimHash, + Status: status, + AppAddress: applicationAddress, + } + + return &epoch, nil + +} + func (pg *Database) GetInput( ctx context.Context, indexKey uint64, @@ -373,6 +440,7 @@ func (pg *Database) GetInput( machineHash *Hash outputsHash *Hash appAddress Address + epochId uint64 ) query := ` @@ -384,7 +452,8 @@ func (pg *Database) GetInput( block_number, machine_hash, outputs_hash, - application_address + application_address, + epoch_id FROM input WHERE @@ -404,6 +473,7 @@ func (pg *Database) GetInput( &machineHash, &outputsHash, &appAddress, + &epochId, ) if err != nil { if errors.Is(err, pgx.ErrNoRows) { @@ -422,6 +492,7 @@ func (pg *Database) GetInput( MachineHash: machineHash, OutputsHash: outputsHash, AppAddress: appAddress, + EpochId: epochId, } return &input, nil @@ -544,63 +615,3 @@ func (pg *Database) GetReport( return &report, nil } - -func (pg *Database) GetClaim( - ctx context.Context, - appAddressKey Address, - indexKey uint64, -) (*Claim, error) { - var ( - id uint64 - index uint64 - outputMerkleRootHash Hash - transactionHash *Hash - status ClaimStatus - address Address - ) - - query := ` - SELECT - id, - index, - output_merkle_root_hash, - transaction_hash, - status, - application_address - FROM - claim - WHERE - application_address=@appAddress and index=@index` - - args := pgx.NamedArgs{ - "appAddress": appAddressKey, - "index": indexKey, - } - - err := pg.db.QueryRow(ctx, query, args).Scan( - &id, - &index, - &outputMerkleRootHash, - &transactionHash, - &status, - &address, - ) - if err != nil { - if errors.Is(err, pgx.ErrNoRows) { - slog.Info("GetClaim returned no rows", "service", "repository") - return nil, nil - } - return nil, fmt.Errorf("GetClaim QueryRow failed: %w\n", err) - } - - claim := Claim{ - Id: id, - Index: index, - OutputMerkleRootHash: outputMerkleRootHash, - TransactionHash: transactionHash, - Status: status, - AppAddress: address, - } - - return &claim, nil -} diff --git a/internal/repository/base_test.go b/internal/repository/base_test.go index 29b7f1d6b..63533d06f 100644 --- a/internal/repository/base_test.go +++ b/internal/repository/base_test.go @@ -5,6 +5,7 @@ package repository import ( "context" + "math" "testing" "time" @@ -62,28 +63,23 @@ func (s *RepositorySuite) SetupDatabase() { InputBoxAddress: common.HexToAddress("deadbeef"), ChainId: 1, IConsensusAddress: common.HexToAddress("deadbeef"), + EpochLength: 10, } err := s.database.InsertNodeConfig(s.ctx, &config) s.Require().Nil(err) app := Application{ - Id: 1, ContractAddress: common.HexToAddress("deadbeef"), TemplateHash: common.HexToHash("deadbeef"), - SnapshotURI: "this/is/a/test", LastProcessedBlock: 1, - EpochLength: 10, Status: ApplicationStatusRunning, } app2 := Application{ - Id: 1, ContractAddress: common.HexToAddress("feadbeef"), TemplateHash: common.HexToHash("deadbeef"), - SnapshotURI: "this/is/a/test", LastProcessedBlock: 1, - EpochLength: 10, Status: ApplicationStatusNotRunning, } @@ -95,27 +91,45 @@ func (s *RepositorySuite) SetupDatabase() { genericHash := common.HexToHash("deadbeef") + epoch1 := Epoch{ + Id: 1, + Index: 0, + FirstBlock: 0, + LastBlock: math.MaxUint64, + AppAddress: app.ContractAddress, + ClaimHash: nil, + TransactionHash: nil, + Status: EpochStatusReceivingInputs, + } + + err = s.database.InsertEpoch(s.ctx, &epoch1) + s.Require().Nil(err) + input1 := Input{ + Id: 1, Index: 1, CompletionStatus: InputStatusAccepted, RawData: common.Hex2Bytes("deadbeef"), BlockNumber: 1, MachineHash: &genericHash, OutputsHash: &genericHash, - AppAddress: common.HexToAddress("deadbeef"), + AppAddress: app.ContractAddress, + EpochId: 1, } err = s.database.InsertInput(s.ctx, &input1) s.Require().Nil(err) input2 := Input{ + Id: 2, Index: 2, CompletionStatus: InputStatusNone, RawData: common.Hex2Bytes("deadbeef"), BlockNumber: 3, MachineHash: &genericHash, OutputsHash: &genericHash, - AppAddress: common.HexToAddress("deadbeef"), + AppAddress: app.ContractAddress, + EpochId: 1, } err = s.database.InsertInput(s.ctx, &input2) @@ -128,7 +142,7 @@ func (s *RepositorySuite) SetupDatabase() { Index: 1, InputId: 1, RawData: common.Hex2Bytes("deadbeef"), - OutputHashesSiblings: nil, + OutputHashesSiblings: siblings, } err = s.database.InsertOutput(s.ctx, &output0) @@ -138,7 +152,7 @@ func (s *RepositorySuite) SetupDatabase() { Index: 2, InputId: 1, RawData: common.Hex2Bytes("deadbeef"), - OutputHashesSiblings: nil, + OutputHashesSiblings: siblings, } err = s.database.InsertOutput(s.ctx, &output1) @@ -163,16 +177,6 @@ func (s *RepositorySuite) SetupDatabase() { err = s.database.InsertReport(s.ctx, &report) s.Require().Nil(err) - claim := Claim{ - Status: ClaimStatusPending, - Index: 1, - OutputMerkleRootHash: genericHash, - TransactionHash: &genericHash, - AppAddress: common.HexToAddress("deadbeef"), - } - - err = s.database.InsertClaim(s.ctx, &claim) - s.Require().Nil(err) } func (s *RepositorySuite) TestApplicationExists() { @@ -180,9 +184,7 @@ func (s *RepositorySuite) TestApplicationExists() { Id: 1, ContractAddress: common.HexToAddress("deadbeef"), TemplateHash: common.HexToHash("deadbeef"), - SnapshotURI: "this/is/a/test", LastProcessedBlock: 1, - EpochLength: 10, Status: ApplicationStatusRunning, } @@ -202,9 +204,7 @@ func (s *RepositorySuite) TestApplicationFailsDuplicateRow() { Id: 1, ContractAddress: common.HexToAddress("deadbeef"), TemplateHash: common.HexToHash("deadbeef"), - SnapshotURI: "this/is/a/test", LastProcessedBlock: 1, - EpochLength: 10, Status: ApplicationStatusRunning, } @@ -224,6 +224,7 @@ func (s *RepositorySuite) TestInputExists() { MachineHash: &genericHash, OutputsHash: &genericHash, AppAddress: common.HexToAddress("deadbeef"), + EpochId: 1, } response, err := s.database.GetInput(s.ctx, 1, common.HexToAddress("deadbeef")) @@ -351,50 +352,57 @@ func (s *RepositorySuite) TestReportFailsInputDoesntExist() { s.Require().ErrorContains(err, "violates foreign key constraint") } -func (s *RepositorySuite) TestClaimExists() { - genericHash := common.HexToHash("deadbeef") - - claim := Claim{ - Id: 1, - Status: ClaimStatusPending, - Index: 1, - TransactionHash: &genericHash, - OutputMerkleRootHash: common.HexToHash("deadbeef"), - AppAddress: common.HexToAddress("deadbeef"), +func (s *RepositorySuite) TestEpochExists() { + + epoch := Epoch{ + Id: 1, + Status: EpochStatusReceivingInputs, + Index: 0, + FirstBlock: 0, + LastBlock: math.MaxUint64, + TransactionHash: nil, + ClaimHash: nil, + AppAddress: common.HexToAddress("deadbeef"), } - response, err := s.database.GetClaim(s.ctx, common.HexToAddress("deadbeef"), 1) - s.Require().Equal(claim, *response) + response, err := s.database.GetEpoch(s.ctx, 0, common.HexToAddress("deadbeef")) + s.Require().Equal(epoch, *response) s.Require().Nil(err) } -func (s *RepositorySuite) TestClaimDoesntExist() { - response, err := s.database.GetClaim(s.ctx, common.HexToAddress("deadbeef"), 0) +func (s *RepositorySuite) TestEpochDoesntExist() { + response, err := s.database.GetEpoch(s.ctx, 3, common.HexToAddress("deadbeef")) s.Require().Nil(response) s.Require().Nil(err) } -func (s *RepositorySuite) TestClaimFailsDuplicateRow() { - claim := Claim{ - Status: ClaimStatusPending, - Index: 1, - OutputMerkleRootHash: common.HexToHash("deadbeef"), - AppAddress: common.HexToAddress("deadbeef"), +func (s *RepositorySuite) TestEpochFailsDuplicateRow() { + epoch := Epoch{ + Status: EpochStatusReceivingInputs, + Index: 0, + FirstBlock: 0, + LastBlock: math.MaxUint64, + TransactionHash: nil, + ClaimHash: nil, + AppAddress: common.HexToAddress("deadbeef"), } - err := s.database.InsertClaim(s.ctx, &claim) + err := s.database.InsertEpoch(s.ctx, &epoch) s.Require().ErrorContains(err, "duplicate key value") } -func (s *RepositorySuite) TestClaimFailsApplicationDoesntExist() { - claim := Claim{ - Status: ClaimStatusPending, - Index: 2, - OutputMerkleRootHash: common.HexToHash("deadbeef"), - AppAddress: common.HexToAddress("deadbeefaaa"), +func (s *RepositorySuite) TestEpochFailsApplicationDoesntExist() { + hash := common.HexToHash("deadbeef") + epoch := Epoch{ + Status: EpochStatusReceivingInputs, + Index: 2, + FirstBlock: 0, + LastBlock: math.MaxUint64, + ClaimHash: &hash, + AppAddress: common.HexToAddress("deadbeefaaa"), } - err := s.database.InsertClaim(s.ctx, &claim) + err := s.database.InsertEpoch(s.ctx, &epoch) s.Require().ErrorContains(err, "violates foreign key constraint") } diff --git a/internal/repository/evmreader.go b/internal/repository/evmreader.go index 1fa7a5be9..b98f088ad 100644 --- a/internal/repository/evmreader.go +++ b/internal/repository/evmreader.go @@ -26,13 +26,15 @@ func (pg *Database) InsertInputsAndUpdateLastProcessedBlock( status, raw_data, block_number, - application_address) + application_address, + epoch_id) VALUES (@index, @status, @rawData, @blockNumber, - @appAddress)` + @appAddress, + @epochId)` query2 := ` UPDATE application @@ -57,21 +59,22 @@ func (pg *Database) InsertInputsAndUpdateLastProcessedBlock( "rawData": input.RawData, "blockNumber": input.BlockNumber, "appAddress": input.AppAddress, + "epochId": input.EpochId, } _, err = tx.Exec(ctx, query, inputArgs) if err != nil { - return fmt.Errorf("%w: %w", errInsertInputs, err) + return errors.Join(fmt.Errorf("%w: %w", errInsertInputs, err), tx.Rollback(ctx)) } } _, err = tx.Exec(ctx, query2, args) if err != nil { - return fmt.Errorf("%w: %w", errInsertInputs, err) + return errors.Join(fmt.Errorf("%w: %w", errInsertInputs, err), tx.Rollback(ctx)) } err = tx.Commit(ctx) if err != nil { - return fmt.Errorf("%w: %w", errInsertInputs, err) + return errors.Join(fmt.Errorf("%w: %w", errInsertInputs, err), tx.Rollback(ctx)) } return nil @@ -98,9 +101,7 @@ func (pg *Database) getAllApplicationsByStatus( id uint64 contractAddress Address templateHash Hash - snapshotUri string lastProcessedBlock uint64 - epochLength uint64 status ApplicationStatus results []Application ) @@ -110,9 +111,7 @@ func (pg *Database) getAllApplicationsByStatus( id, contract_address, template_hash, - snapshot_uri, last_processed_block, - epoch_length, status FROM application @@ -130,16 +129,14 @@ func (pg *Database) getAllApplicationsByStatus( } _, err = pgx.ForEachRow(rows, - []any{&id, &contractAddress, &templateHash, &snapshotUri, - &lastProcessedBlock, &epochLength, &status}, + []any{&id, &contractAddress, &templateHash, + &lastProcessedBlock, &status}, func() error { app := Application{ Id: id, ContractAddress: contractAddress, TemplateHash: templateHash, - SnapshotURI: snapshotUri, LastProcessedBlock: lastProcessedBlock, - EpochLength: epochLength, Status: status, } results = append(results, app) diff --git a/internal/repository/evmreader_test.go b/internal/repository/evmreader_test.go index be106c9cc..c634a9cd5 100644 --- a/internal/repository/evmreader_test.go +++ b/internal/repository/evmreader_test.go @@ -16,6 +16,7 @@ func (s *RepositorySuite) TestInsertInputsAndUpdateLastProcessedBlock() { RawData: common.Hex2Bytes("deadbeef"), BlockNumber: 5, AppAddress: common.HexToAddress("deadbeef"), + EpochId: 1, } input1 := Input{ @@ -25,6 +26,7 @@ func (s *RepositorySuite) TestInsertInputsAndUpdateLastProcessedBlock() { RawData: common.Hex2Bytes("deadbeef"), BlockNumber: 6, AppAddress: common.HexToAddress("deadbeef"), + EpochId: 1, } var inputs []Input @@ -74,6 +76,7 @@ func (s *RepositorySuite) TestInsertInputsAndUpdateLastProcessedBlockInputAlread RawData: common.Hex2Bytes("deadbeef"), BlockNumber: 5, AppAddress: common.HexToAddress("deadbeef"), + EpochId: 1, } var inputs []Input @@ -96,6 +99,7 @@ func (s *RepositorySuite) TestInsertInputsAndUpdateLastProcessedBlockDuplicateIn RawData: common.Hex2Bytes("deadbeef"), BlockNumber: 7, AppAddress: common.HexToAddress("deadbeef"), + EpochId: 1, } input1 := Input{ @@ -105,6 +109,7 @@ func (s *RepositorySuite) TestInsertInputsAndUpdateLastProcessedBlockDuplicateIn RawData: common.Hex2Bytes("deadbeef"), BlockNumber: 7, AppAddress: common.HexToAddress("deadbeef"), + EpochId: 1, } var inputs []Input @@ -125,9 +130,7 @@ func (s *RepositorySuite) TestGetAllRunningApplications() { Id: 1, ContractAddress: common.HexToAddress("deadbeef"), TemplateHash: common.HexToHash("deadbeef"), - SnapshotURI: "this/is/a/test", LastProcessedBlock: 1, - EpochLength: 10, Status: ApplicationStatusRunning, } diff --git a/internal/repository/migrations/000001_create_application_input_claim_output_report_nodeconfig.down.sql b/internal/repository/migrations/000001_create_application_input_claim_output_report_nodeconfig.down.sql index 0fba61bf5..6bd0ab0cc 100644 --- a/internal/repository/migrations/000001_create_application_input_claim_output_report_nodeconfig.down.sql +++ b/internal/repository/migrations/000001_create_application_input_claim_output_report_nodeconfig.down.sql @@ -2,13 +2,15 @@ -- SPDX-License-Identifier: Apache-2.0 (see LICENSE) DROP TABLE IF EXISTS "node_config"; +DROP TABLE IF EXISTS "snapshot"; DROP TABLE IF EXISTS "report"; DROP TABLE IF EXISTS "output"; -DROP TABLE IF EXISTS "claim"; DROP TABLE IF EXISTS "input"; +DROP TABLE IF EXISTS "epoch"; DROP TABLE IF EXISTS "application"; + DROP TYPE IF EXISTS "InputCompletionStatus"; -DROP TYPE IF EXISTS "ClaimStatus"; +DROP TYPE IF EXISTS "EpochStatus"; DROP TYPE IF EXISTS "ApplicationStatus"; DROP TYPE IF EXISTS "DefaultBlock"; diff --git a/internal/repository/migrations/000001_create_application_input_claim_output_report_nodeconfig.up.sql b/internal/repository/migrations/000001_create_application_input_claim_output_report_nodeconfig.up.sql index 31a0fd635..7d259326a 100644 --- a/internal/repository/migrations/000001_create_application_input_claim_output_report_nodeconfig.up.sql +++ b/internal/repository/migrations/000001_create_application_input_claim_output_report_nodeconfig.up.sql @@ -5,10 +5,10 @@ CREATE TYPE "ApplicationStatus" AS ENUM ('RUNNING', 'NOT RUNNING'); CREATE TYPE "InputCompletionStatus" AS ENUM ('NONE', 'ACCEPTED', 'REJECTED', 'EXCEPTION', 'MACHINE_HALTED', 'CYCLE_LIMIT_EXCEEDED', 'TIME_LIMIT_EXCEEDED', 'PAYLOAD_LENGTH_LIMIT_EXCEEDED'); -CREATE TYPE "ClaimStatus" AS ENUM ('PENDING', 'SUBMITTED', 'FINALIZED'); - CREATE TYPE "DefaultBlock" AS ENUM ('FINALIZED', 'LATEST', 'PENDING', 'SAFE'); +CREATE TYPE "EpochStatus" AS ENUM ('RECEIVING_INPUTS', 'RECEIVED_LAST_INPUT', 'PROCESSED_ALL_INPUTS', 'CALCULATED_CLAIM', 'SUBMITTED_CLAIM', 'ACCEPTED_CLAIM', 'REJECTED_CLAIM'); + CREATE FUNCTION public.f_maxuint64() RETURNS NUMERIC(20,0) LANGUAGE sql IMMUTABLE PARALLEL SAFE AS @@ -19,14 +19,27 @@ CREATE TABLE "application" "id" SERIAL, "contract_address" BYTEA NOT NULL, "template_hash" BYTEA NOT NULL, - "snapshot_uri" VARCHAR(4096) NOT NULL, "last_processed_block" NUMERIC(20,0) NOT NULL CHECK ("last_processed_block" >= 0 AND "last_processed_block" <= f_maxuint64()), "status" "ApplicationStatus" NOT NULL, - "epoch_length" INT NOT NULL, CONSTRAINT "application_pkey" PRIMARY KEY ("id"), UNIQUE("contract_address") ); +CREATE TABLE "epoch" +( + "id" BIGSERIAL, + "application_address" BYTEA NOT NULL, + "index" BIGINT NOT NULL, + "first_block" NUMERIC(20,0) NOT NULL CHECK ("first_block" >= 0 AND "first_block" <= f_maxuint64()), + "last_block" NUMERIC(20,0) NOT NULL CHECK ("last_block" >= 0 AND "last_block" <= f_maxuint64()), + "claim_hash" BYTEA, + "transaction_hash" BYTEA, + "status" "EpochStatus" NOT NULL, + CONSTRAINT "epoch_pkey" PRIMARY KEY ("id"), + CONSTRAINT "epoch_application_address_fkey" FOREIGN KEY ("application_address") REFERENCES "application"("contract_address"), + UNIQUE ("index","application_address") +); + CREATE TABLE "input" ( "id" BIGSERIAL, @@ -37,26 +50,15 @@ CREATE TABLE "input" "machine_hash" BYTEA, "outputs_hash" BYTEA, "application_address" BYTEA NOT NULL, + "epoch_id" BIGINT NOT NULL, CONSTRAINT "input_pkey" PRIMARY KEY ("id"), CONSTRAINT "input_application_address_fkey" FOREIGN KEY ("application_address") REFERENCES "application"("contract_address"), + CONSTRAINT "input_epoch_fkey" FOREIGN KEY ("epoch_id") REFERENCES "epoch"("id"), UNIQUE("index", "application_address") ); CREATE INDEX "input_idx" ON "input"("block_number"); -CREATE TABLE "claim" -( - "id" BIGSERIAL, - "index" NUMERIC(20,0) NOT NULL CHECK ("index" >= 0 AND "index" <= f_maxuint64()), - "output_merkle_root_hash" BYTEA NOT NULL, - "transaction_hash" BYTEA, - "status" "ClaimStatus" NOT NULL, - "application_address" BYTEA NOT NULL, - CONSTRAINT "claim_pkey" PRIMARY KEY ("id"), - CONSTRAINT "claim_application_address_fkey" FOREIGN KEY ("application_address") REFERENCES "application"("contract_address"), - UNIQUE("index", "application_address") -); - CREATE TABLE "output" ( "id" BIGSERIAL, @@ -83,11 +85,25 @@ CREATE TABLE "report" CREATE UNIQUE INDEX "report_idx" ON "report"("index"); +CREATE TABLE "snapshot" +( + "id" BIGSERIAL, + "input_id" BIGINT NOT NULL, + "application_address" BYTEA NOT NULL, + "uri" VARCHAR(4096) NOT NULL, + CONSTRAINT "snapshot_pkey" PRIMARY KEY ("id"), + CONSTRAINT "snapshot_input_id_fkey" FOREIGN KEY ("input_id") REFERENCES "input"("id"), + CONSTRAINT "snapshot_application_address_fkey" FOREIGN KEY ("application_address") REFERENCES "application"("contract_address") +); + CREATE TABLE "node_config" ( "default_block" "DefaultBlock" NOT NULL, "input_box_deployment_block" INT NOT NULL, "input_box_address" BYTEA NOT NULL, "chain_id" INT NOT NULL, - "iconsensus_address" BYTEA NOT NULL + "iconsensus_address" BYTEA NOT NULL, + "epoch_length" INT NOT NULL ); + + diff --git a/internal/repository/migrations/000002_create_postgraphile_view.up.sql b/internal/repository/migrations/000002_create_postgraphile_view.up.sql index e1062596b..f999f998d 100644 --- a/internal/repository/migrations/000002_create_postgraphile_view.up.sql +++ b/internal/repository/migrations/000002_create_postgraphile_view.up.sql @@ -8,24 +8,37 @@ CREATE OR REPLACE VIEW graphql."applications" AS SELECT "contract_address", "template_hash", - "snapshot_uri", "last_processed_block", - "epoch_length", "status" FROM "application"; -CREATE OR REPLACE VIEW graphql."inputs" AS +CREATE OR REPLACE VIEW graphql."epochs" AS SELECT "index", - "status", - "block_number", - "raw_data", - "machine_hash", - "outputs_hash", - "application_address" + "application_address", + "first_block", + "last_block", + "transaction_hash", + "claim_hash", + "status" FROM - "input"; + "epoch"; + +CREATE OR REPLACE VIEW graphql."inputs" AS + SELECT + i."index", + i."status", + i."block_number", + i."raw_data", + i."machine_hash", + i."outputs_hash", + i."application_address", + e."index" as "epoch_index" + FROM + "input" i + INNER JOIN + "epoch" e on i."epoch_id" = e."id"; CREATE OR REPLACE VIEW graphql."outputs" AS SELECT @@ -48,24 +61,8 @@ CREATE OR REPLACE VIEW graphql."reports" AS INNER JOIN "input" i on r."input_id"=i."id"; -CREATE OR REPLACE VIEW graphql."claims" AS - SELECT - c."index", - c."output_merkle_root_hash", - c."status", - c."application_address", - o."index" as "output_index" - FROM - "claim" c - INNER JOIN - "application" a ON c."application_address"=a."contract_address" - INNER JOIN - "input" i ON a."contract_address"=i."application_address" - INNER JOIN - "output" o ON i."id"=o."input_id"; - COMMENT ON VIEW graphql."inputs" is - E'@foreignKey (application_address) references applications(contract_address)|@fieldName applicationByApplicationAddress'; + E'@foreignKey (application_address) references applications(contract_address)|@fieldName applicationByApplicationAddress\n@foreignKey (epoch_index) references epochs(index)|@fieldName epochByEpochIndex'; COMMENT ON VIEW graphql."outputs" is E'@foreignKey (input_index) references inputs(index)|@fieldName inputByInputIndex'; @@ -73,5 +70,5 @@ COMMENT ON VIEW graphql."outputs" is COMMENT ON VIEW graphql."reports" is E'@foreignKey (input_index) references inputs(index)|@fieldName inputByInputIndex'; -COMMENT ON VIEW graphql."claims" is - E'@foreignKey (output_index) references outputs(index)|@fieldName outputByOutputIndex\n@foreignKey (application_address) references applications(contract_address)|@fieldName applicationByApplicationAddress'; \ No newline at end of file +COMMENT ON VIEW graphql."epochs" is + E'@foreignKey (application_address) references applications(contract_address)|@fieldName applicationByApplicationAddress'; \ No newline at end of file diff --git a/internal/repository/validator.go b/internal/repository/validator.go index cc4b5a558..32641d7e0 100644 --- a/internal/repository/validator.go +++ b/internal/repository/validator.go @@ -5,6 +5,7 @@ package repository import ( "context" + "errors" "fmt" "time" @@ -132,33 +133,22 @@ func (pg *Database) getAllOutputsFromProcessedInputs( func (pg *Database) FinishEpoch( ctx context.Context, - claim *Claim, + epochId uint64, + claimHash Hash, outputs []Output, ) error { query1 := ` - INSERT INTO claim - (index, - output_merkle_root_hash, - status, - application_address) - VALUES - (@index, - @outputMerkleRootHash, - @status, - @appAddress)` - - query2 := ` - UPDATE output + UPDATE epoch SET - output_hashes_siblings=@outputHashesSiblings + claim_hash=@claimHash, + status=@status WHERE - index=@index` + id=@id` args := pgx.NamedArgs{ - "index": claim.Index, - "status": ClaimStatusPending, - "outputMerkleRootHash": claim.OutputMerkleRootHash, - "appAddress": claim.AppAddress, + "id": epochId, + "claimHash": claimHash, + "status": EpochStatusCalculatedClaim, } tx, err := pg.db.Begin(ctx) @@ -167,23 +157,30 @@ func (pg *Database) FinishEpoch( } _, err = tx.Exec(ctx, query1, args) if err != nil { - return fmt.Errorf("unable to finish epoch: %w\n", err) + return errors.Join(fmt.Errorf("unable to finish epoch: %w\n", err), tx.Rollback(ctx)) } + query2 := ` + UPDATE output + SET + output_hashes_siblings=@outputHashesSiblings + WHERE + id=@id` + for _, output := range outputs { outputArgs := pgx.NamedArgs{ "outputHashesSiblings": output.OutputHashesSiblings, - "index": output.Index, + "id": output.Id, } _, err = tx.Exec(ctx, query2, outputArgs) if err != nil { - return fmt.Errorf("unable to finish epoch: %w\n", err) + return errors.Join(fmt.Errorf("unable to finish epoch: %w\n", err), tx.Rollback(ctx)) } } err = tx.Commit(ctx) if err != nil { - return fmt.Errorf("unable to finish epoch: %w\n", err) + return errors.Join(fmt.Errorf("unable to finish epoch: %w\n", err), tx.Rollback(ctx)) } return nil diff --git a/internal/repository/validator_test.go b/internal/repository/validator_test.go index fc0061f09..41ddf6e61 100644 --- a/internal/repository/validator_test.go +++ b/internal/repository/validator_test.go @@ -4,6 +4,7 @@ package repository import ( + "math" "time" . "github.com/cartesi/rollups-node/internal/node/model" @@ -63,23 +64,27 @@ func (s *RepositorySuite) TestFinishEpochTransaction() { OutputHashesSiblings: siblings, } - claim := Claim{ - Id: 4, - Index: 2, - Status: ClaimStatusPending, - OutputMerkleRootHash: common.HexToHash("deadbeef"), - AppAddress: common.HexToAddress("deadbeef"), + hash := common.HexToHash("deadbeef") + epoch := Epoch{ + Id: 1, + Index: 0, + FirstBlock: 0, + LastBlock: math.MaxUint64, + TransactionHash: nil, + Status: EpochStatusCalculatedClaim, + ClaimHash: &hash, + AppAddress: common.HexToAddress("deadbeef"), } var outputs []Output outputs = append(outputs, output) - err := s.database.FinishEpoch(s.ctx, &claim, outputs) + err := s.database.FinishEpoch(s.ctx, epoch.Id, hash, outputs) s.Require().Nil(err) - response0, err := s.database.GetClaim(s.ctx, common.HexToAddress("deadbeef"), 2) + response0, err := s.database.GetEpoch(s.ctx, 0, common.HexToAddress("deadbeef")) s.Require().Nil(err) - s.Require().Equal(claim, *response0) + s.Require().Equal(epoch, *response0) response1, err := s.database.GetOutput(s.ctx, 1, common.HexToAddress("deadbeef")) s.Require().Nil(err) @@ -91,23 +96,33 @@ func (s *RepositorySuite) TestFinishEpochTransactionRollback() { siblings = append(siblings, common.HexToHash("deadbeef")) output := Output{ - Id: 2, - Index: 2, + Id: 5, + Index: 4, InputId: 1, RawData: common.Hex2Bytes("deadbeef"), OutputHashesSiblings: siblings, } - claim := Claim{ - Index: 2, - Status: ClaimStatusPending, - OutputMerkleRootHash: common.HexToHash("deadbeef"), - AppAddress: common.HexToAddress("deadbeef"), + hash := common.HexToHash("deadbeef") + epoch := Epoch{ + Id: 1, + Index: 0, + FirstBlock: 0, + LastBlock: math.MaxUint64, + TransactionHash: nil, + Status: EpochStatusReceivingInputs, + ClaimHash: nil, + AppAddress: common.HexToAddress("deadbeef"), } var outputs []Output outputs = append(outputs, output) - err := s.database.FinishEpoch(s.ctx, &claim, outputs) + err := s.database.FinishEpoch(s.ctx, 10, hash, outputs) s.Require().ErrorContains(err, "unable to finish epoch") + + actualEpoch, err := s.database.GetEpoch(s.ctx, 0, epoch.AppAddress) + s.Require().Nil(err) + s.Require().NotNil(actualEpoch) + s.Require().Equal(epoch, *actualEpoch) }