Skip to content
This repository has been archived by the owner on Oct 31, 2024. It is now read-only.

Commit

Permalink
feat: use receipt trie instead of transaction trie (#14)
Browse files Browse the repository at this point in the history
  • Loading branch information
sebastiendan authored Aug 28, 2023
1 parent d3b10bf commit 0394885
Show file tree
Hide file tree
Showing 13 changed files with 101 additions and 127 deletions.
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ REDIS_HOST=
REDIS_PORT=
TOPOS_SUBNET_ENDPOINT=
SUBNET_REGISTRATOR_CONTRACT_ADDRESS=
TOPOS_CORE_CONTRACT_ADDRESS=
TOPOS_CORE_PROXY_CONTRACT_ADDRESS=
TRACING_OTEL_COLLECTOR_ENDPOINT=
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ REDIS_HOST=
REDIS_PORT=
TOPOS_SUBNET_ENDPOINT=
SUBNET_REGISTRATOR_CONTRACT_ADDRESS=
TOPOS_CORE_CONTRACT_ADDRESS=
TOPOS_CORE_PROXY_CONTRACT_ADDRESS=
ERC20_MESSAGING_CONTRACT_ADDRESS=
```

Expand Down
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"@opentelemetry/sdk-node": "^0.41.1",
"@opentelemetry/sdk-trace-base": "^1.15.1",
"@opentelemetry/semantic-conventions": "^1.15.1",
"@topos-protocol/topos-smart-contracts": "^1.1.2",
"@topos-protocol/topos-smart-contracts": "^1.2.0",
"bcrypt": "^5.1.0",
"bull": "^4.10.1",
"class-transformer": "^0.5.1",
Expand Down
7 changes: 3 additions & 4 deletions src/execute/execute.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@ import { ExecuteServiceV1 } from './execute.service'
import { Observable, observable } from 'rxjs'

const validExecuteDto: ExecuteDto = {
indexOfDataInTxRaw: 4,
logIndexes: [],
messagingContractAddress: '',
receiptTrieRoot: '',
receiptTrieMerkleProof: '',
subnetId: '',
txRaw: '',
txTrieRoot: '',
txTrieMerkleProof: '',
}

describe('ExecuteController', () => {
Expand Down
28 changes: 10 additions & 18 deletions src/execute/execute.dto.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { ApiProperty } from '@nestjs/swagger'
import {
IsDefined,
IsEthereumAddress,
IsNotEmpty,
IsNumber,
Expand All @@ -9,42 +8,35 @@ import {

export class ExecuteDto {
@ApiProperty({
description:
'The raw transaction of a cross-subnet message from the sending subnet',
description: 'The id of the receiving subnet',
})
@IsNotEmpty()
@IsString()
txRaw: string

@ApiProperty({
description: 'The index of the data binary in the raw transaction',
})
@IsDefined()
@IsNumber()
indexOfDataInTxRaw: number
subnetId: string

@ApiProperty({
description: 'The id of the receiving subnet',
description:
'The array of indexes that the messaging contract should use to validate the cross-subnet message semantically',
})
@IsNotEmpty()
@IsString()
subnetId: string
@IsNumber({}, { each: true })
logIndexes: number[]

@ApiProperty({
description:
'The root of the transaction trie including the cross-subnet message transaction from the sending subnet',
'The root of the receipt trie including the cross-subnet message tx receipt from the sending subnet',
})
@IsNotEmpty()
@IsString()
txTrieRoot: string
receiptTrieRoot: string

@ApiProperty({
description:
'The merkle proof proving the inclusion of the cross-subnet message transaction from the sending subnet in the certified transaction trie',
'The merkle proof proving the inclusion of the cross-subnet message tx receipt from the sending subnet in the certified receipt trie',
})
@IsNotEmpty()
@IsString()
txTrieMerkleProof: string
receiptTrieMerkleProof: string

@ApiProperty({
description: 'The address of the messaging contract',
Expand Down
8 changes: 6 additions & 2 deletions src/execute/execute.errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ export enum CONTRACT_ERRORS {
}

export enum QUEUE_ERRORS {
JOB_NOT_FOUND = 'Job // A job with the provided id could not be found!',
REDIS_NOT_AVAILABLE = 'Redis // Could not connect to Redis!',
JOB_NOT_FOUND = 'Queue // A job with the provided id could not be found!',
REDIS_NOT_AVAILABLE = 'Queue // Could not connect to Redis!',
}

export enum JOB_ERRORS {
MISSING_CERTIFICATE = 'Job // Could not find the related certificate!',
}
21 changes: 10 additions & 11 deletions src/execute/execute.processor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@ import { ExecutionProcessorV1 } from './execute.processor'

const VALID_PRIVATE_KEY =
'0xc6cbd7d76bc5baca530c875663711b947efa6a86a900a9e8645ce32e5821484e'
const TOPOS_CORE_CONTRACT_ADDRESS = '0x1D7b9f9b1FF6cf0A3BEB0F84fA6F8628E540E97F'
const TOPOS_CORE_PROXY_CONTRACT_ADDRESS =
'0x1D7b9f9b1FF6cf0A3BEB0F84fA6F8628E540E97F'
const TOPOS_SUBNET_ENDPOINT = 'topos-subnet-endpoint'

const validExecuteJob: Partial<Job<ExecuteDto>> = {
data: {
indexOfDataInTxRaw: 4,
logIndexes: [],
messagingContractAddress: '',
receiptTrieRoot: '',
receiptTrieMerkleProof: '',
subnetId: 'id',
txRaw: '',
txTrieRoot: '',
txTrieMerkleProof: '',
},
progress: jest.fn(),
}
Expand Down Expand Up @@ -52,8 +52,8 @@ describe('ExecuteProcessor', () => {
switch (key) {
case 'PRIVATE_KEY':
return VALID_PRIVATE_KEY
case 'TOPOS_CORE_CONTRACT_ADDRESS':
return TOPOS_CORE_CONTRACT_ADDRESS
case 'TOPOS_CORE_PROXY_CONTRACT_ADDRESS':
return TOPOS_CORE_PROXY_CONTRACT_ADDRESS
case 'TOPOS_SUBNET_ENDPOINT':
return TOPOS_SUBNET_ENDPOINT
}
Expand Down Expand Up @@ -98,10 +98,9 @@ describe('ExecuteProcessor', () => {
expect(validExecuteJob.progress).toHaveBeenCalledWith(50)

expect(contractMock.execute).toHaveBeenCalledWith(
validExecuteJob.data.indexOfDataInTxRaw,
validExecuteJob.data.txTrieMerkleProof,
validExecuteJob.data.txRaw,
validExecuteJob.data.txTrieRoot,
validExecuteJob.data.logIndexes,
validExecuteJob.data.receiptTrieRoot,
validExecuteJob.data.receiptTrieMerkleProof,
{
gasLimit: 4_000_000,
}
Expand Down
44 changes: 25 additions & 19 deletions src/execute/execute.processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,14 @@ import { ethers, providers } from 'ethers'
import { ExecuteDto } from './execute.dto'
import {
CONTRACT_ERRORS,
JOB_ERRORS,
PROVIDER_ERRORS,
WALLET_ERRORS,
} from './execute.errors'
import { sanitizeURLProtocol } from '../utils'

const UNDEFINED_CERTIFICATE_ID =
'0x0000000000000000000000000000000000000000000000000000000000000000'

@Processor('execute')
export class ExecutionProcessorV1 {
Expand All @@ -38,16 +43,15 @@ export class ExecutionProcessorV1 {
@Process('execute')
async execute(job: Job<ExecuteDto>) {
const {
indexOfDataInTxRaw,
logIndexes,
messagingContractAddress,
receiptTrieMerkleProof,
receiptTrieRoot,
subnetId,
txRaw,
txTrieMerkleProof,
txTrieRoot,
} = job.data

const toposCoreContractAddress = this.configService.get<string>(
'TOPOS_CORE_CONTRACT_ADDRESS'
'TOPOS_CORE_PROXY_CONTRACT_ADDRESS'
)

const receivingSubnetEndpoint =
Expand All @@ -72,28 +76,30 @@ export class ExecutionProcessorV1 {
wallet
)) as ToposMessaging

this.logger.debug(`Trie root: ${txTrieRoot}`)
this.logger.debug(`Trie root: ${receiptTrieRoot}`)

let certId =
'0x0000000000000000000000000000000000000000000000000000000000000000'
let certId = UNDEFINED_CERTIFICATE_ID
let i = 1
while (
certId ==
'0x0000000000000000000000000000000000000000000000000000000000000000'
) {

while (certId == UNDEFINED_CERTIFICATE_ID && i < 40) {
this.logger.debug(`Waiting for cert to be imported (${i})`)
certId = await toposCoreContract.txRootToCertId(txTrieRoot)
certId = await toposCoreContract.receiptRootToCertId(receiptTrieRoot)
this.logger.debug(`Cert id: ${certId}`)
await new Promise<void>((r) => setTimeout(r, 1000))
i++
}

if (certId == UNDEFINED_CERTIFICATE_ID) {
await job.moveToFailed({ message: JOB_ERRORS.MISSING_CERTIFICATE })
return
}

await job.progress(50)

const tx = await messagingContract.execute(
indexOfDataInTxRaw,
txTrieMerkleProof,
txRaw,
txTrieRoot,
logIndexes,
receiptTrieMerkleProof,
receiptTrieRoot,
{
gasLimit: 4_000_000,
}
Expand All @@ -110,7 +116,7 @@ export class ExecutionProcessorV1 {
'TOPOS_SUBNET_ENDPOINT'
)
const toposCoreContractAddress = this.configService.get<string>(
'TOPOS_CORE_CONTRACT_ADDRESS'
'TOPOS_CORE_PROXY_CONTRACT_ADDRESS'
)
const subnetRegistratorContractAddress = this.configService.get<string>(
'SUBNET_REGISTRATOR_CONTRACT_ADDRESS'
Expand Down Expand Up @@ -143,7 +149,7 @@ export class ExecutionProcessorV1 {
private _createProvider(endpoint: string) {
return new Promise<providers.WebSocketProvider>((resolve, reject) => {
const provider = new ethers.providers.WebSocketProvider(
`ws://${endpoint}/ws`
sanitizeURLProtocol('ws', `${endpoint}/ws`)
)

// Fix: Timeout to leave time to errors to be asynchronously caught
Expand Down
11 changes: 5 additions & 6 deletions src/execute/execute.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,11 @@ import { ConfigService } from '@nestjs/config'
import { first, firstValueFrom, lastValueFrom } from 'rxjs'

const validExecuteDto: ExecuteDto = {
indexOfDataInTxRaw: 4,
logIndexes: [],
messagingContractAddress: '',
receiptTrieRoot: '',
receiptTrieMerkleProof: '',
subnetId: '',
txRaw: '',
txTrieRoot: '',
txTrieMerkleProof: '',
}

const VALID_PRIVATE_KEY =
Expand Down Expand Up @@ -131,15 +130,15 @@ describe('ExecuteService', () => {
const job: Partial<Job> = {
id: jobId,
failedReason: 'errorMock',
finished: jest.fn().mockRejectedValueOnce(''),
finished: jest.fn().mockRejectedValueOnce('errorMock'),
}
resolve(job)
})
)

await expect(
lastValueFrom(executeService.subscribeToJobById(jobId))
).rejects.toStrictEqual({ data: 'errorMock' })
).rejects.toStrictEqual('errorMock')
})
})
})
7 changes: 2 additions & 5 deletions src/execute/execute.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,9 @@ export class ExecuteServiceV1 {
subscriber.complete()
})
.catch((error) => {
const messageEvent: MessageEvent = {
data: job.failedReason,
}
this.logger.debug(`Job failed!`)
this.logger.debug(job.failedReason)
subscriber.error(messageEvent)
this.logger.debug(error)
subscriber.error(error)
subscriber.complete()
})
})
Expand Down
6 changes: 6 additions & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export function sanitizeURLProtocol(protocol: 'ws' | 'http', endpoint: string) {
return endpoint.indexOf('localhost') > -1 ||
endpoint.indexOf('127.0.0.1') > -1
? `${protocol}://${endpoint}`
: `${protocol}s://${endpoint}`
}
Loading

0 comments on commit 0394885

Please sign in to comment.