Skip to content

Commit 17607bf

Browse files
authored
fix validateDDO signature logic (#1143)
* fix validateDDO signature logic
1 parent a515618 commit 17607bf

File tree

7 files changed

+84
-28
lines changed

7 files changed

+84
-28
lines changed

.github/workflows/ci.yml

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
- uses: actions/checkout@v4
1818
- uses: actions/setup-node@v4
1919
with:
20-
node-version: "v22.15.0"
20+
node-version: 'v22.15.0'
2121
- name: Cache node_modules
2222
uses: actions/cache@v3
2323
env:
@@ -42,7 +42,7 @@ jobs:
4242
fail-fast: false
4343
matrix:
4444
os: [ubuntu-latest]
45-
node: ["18.20.4", "v20.19.0", "v22.15.0"]
45+
node: ['18.20.4', 'v20.19.0', 'v22.15.0']
4646

4747
steps:
4848
- uses: actions/checkout@v4
@@ -66,7 +66,7 @@ jobs:
6666
- uses: actions/checkout@v4
6767
- uses: actions/setup-node@v4
6868
with:
69-
node-version: "v22.15.0"
69+
node-version: 'v22.15.0'
7070
- name: Cache node_modules
7171
uses: actions/cache@v3
7272
env:
@@ -87,6 +87,8 @@ jobs:
8787
RPCS: '{ "1": {"rpc": "https://rpc.eth.gateway.fm", "chainId": 1, "network": "mainet", "chunkSize": 100}, "137": {"rpc": "https://polygon.meowrpc.com", "chainId": 137, "network": "polygon", "chunkSize": 100 }, "80001": {"rpc": "https://rpc-mumbai.maticvigil.com","chainId": 80001, "network": "polygon-mumbai", "chunkSize": 100 } }'
8888
DB_URL: 'http://localhost:8108/?apiKey=xyz'
8989
DB_TYPE: 'typesense'
90+
DB_USERNAME: 'elastic'
91+
DB_PASSWORD: 'changeme'
9092
FEE_TOKENS: '{ "1": "0x967da4048cD07aB37855c090aAF366e4ce1b9F48", "137": "0x282d8efCe846A88B159800bd4130ad77443Fa1A1", "80001": "0xd8992Ed72C445c35Cb4A2be468568Ed1079357c8", "56": "0xDCe07662CA8EbC241316a15B611c89711414Dd1a" }'
9193
FEE_AMOUNT: '{ "amount": 1, "unit": "MB" }'
9294
- uses: actions/upload-artifact@v4
@@ -100,7 +102,7 @@ jobs:
100102
- uses: actions/checkout@v4
101103
- uses: actions/setup-node@v4
102104
with:
103-
node-version: "v22.15.0"
105+
node-version: 'v22.15.0'
104106
- name: Cache node_modules
105107
uses: actions/cache@v3
106108
env:
@@ -178,7 +180,7 @@ jobs:
178180
- name: Set up Node.js
179181
uses: actions/setup-node@v4
180182
with:
181-
node-version: "v22.15.0"
183+
node-version: 'v22.15.0'
182184

183185
- name: Cache node_modules
184186
uses: actions/cache@v3
@@ -258,6 +260,8 @@ jobs:
258260
P2P_ENABLE_AUTONAT: 'false'
259261
ALLOWED_ADMINS: '["0xe2DD09d719Da89e5a3D0F2549c7E24566e947260"]'
260262
DB_TYPE: 'elasticsearch'
263+
DB_USERNAME: 'elastic'
264+
DB_PASSWORD: 'changeme'
261265
MAX_REQ_PER_MINUTE: 320
262266
MAX_CONNECTIONS_PER_MINUTE: 320
263267
DOCKER_COMPUTE_ENVIRONMENTS: '[{"socketPath":"/var/run/docker.sock","resources":[{"id":"disk","total":10}],"storageExpiry":604800,"maxJobDuration":3600,"minJobDuration": 60,"fees":{"8996":[{"prices":[{"id":"cpu","price":1}]}]},"free":{"maxJobDuration":60,"minJobDuration": 10,"maxJobs":3,"resources":[{"id":"cpu","max":1},{"id":"ram","max":1},{"id":"disk","max":1}]}}]'
@@ -309,7 +313,7 @@ jobs:
309313
- uses: actions/checkout@v4
310314
- uses: actions/setup-node@v4
311315
with:
312-
node-version: "v22.15.0"
316+
node-version: 'v22.15.0'
313317
- name: Cache node_modules
314318
uses: actions/cache@v3
315319
env:

src/components/core/handler/ddoHandler.ts

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -798,11 +798,6 @@ export class FindDdoHandler extends CommandHandler {
798798
}
799799
}
800800

801-
export async function skipValidation(): Promise<boolean> {
802-
const configuration = await getConfiguration()
803-
return configuration.validateUnsignedDDO
804-
}
805-
806801
export class ValidateDDOHandler extends CommandHandler {
807802
validate(command: ValidateDDOCommand): ValidateParams {
808803
let validation = validateCommandParameters(command, ['ddo'])
@@ -815,8 +810,15 @@ export class ValidateDDOHandler extends CommandHandler {
815810

816811
async handle(task: ValidateDDOCommand): Promise<P2PCommandResponse> {
817812
const validationResponse = await this.verifyParamsAndRateLimits(task)
818-
const shouldSkipValidation = await skipValidation()
819-
if (!shouldSkipValidation) {
813+
if (this.shouldDenyTaskHandling(validationResponse)) {
814+
return validationResponse
815+
}
816+
let shouldSign = false
817+
const configuration = await getConfiguration()
818+
if (configuration.validateUnsignedDDO) {
819+
shouldSign = true
820+
}
821+
if (task.authorization || task.signature || task.nonce || task.publisherAddress) {
820822
const validationResponse = await this.validateTokenOrSignature(
821823
task.authorization,
822824
task.publisherAddress,
@@ -827,10 +829,7 @@ export class ValidateDDOHandler extends CommandHandler {
827829
if (validationResponse.status.httpStatus !== 200) {
828830
return validationResponse
829831
}
830-
}
831-
832-
if (this.shouldDenyTaskHandling(validationResponse)) {
833-
return validationResponse
832+
shouldSign = true
834833
}
835834

836835
try {
@@ -849,9 +848,12 @@ export class ValidateDDOHandler extends CommandHandler {
849848
status: { httpStatus: 400, error: `Validation error: ${validation[1]}` }
850849
}
851850
}
852-
const signature = await getValidationSignature(JSON.stringify(task.ddo))
853851
return {
854-
stream: Readable.from(JSON.stringify(signature)),
852+
stream: shouldSign
853+
? Readable.from(
854+
JSON.stringify(await getValidationSignature(JSON.stringify(task.ddo)))
855+
)
856+
: null,
855857
status: { httpStatus: 200 }
856858
}
857859
} catch (error) {

src/test/.env.test

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ PRIVATE_KEY=0xc594c6e5def4bab63ac29eed19a134c130388f74f019bc74b8f4389df2837a58
44
RPCS='{ "8996": {"rpc": "http://127.0.0.1:8545", "chainId": 8996, "network": "development", "chunkSize": 100}}'
55
INDEXER_NETWORKS='[8996]'
66
DB_URL=http://localhost:9200
7+
DB_USERNAME=elastic
8+
DB_PASSWORD=changeme
79
IPFS_GATEWAY=https://ipfs.io/
810
ARWEAVE_GATEWAY=https://arweave.net/
911
NODE1_PRIVATE_KEY=0xcb345bd2b11264d523ddaf383094e2675c420a17511c3102a53817f13474a7ff

src/test/integration/database.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ const typesenseConfig: OceanNodeDBConfig = {
1515

1616
const elasticConfig: OceanNodeDBConfig = {
1717
url: 'http://localhost:9200',
18-
dbType: DB_TYPES.ELASTIC_SEARCH
18+
dbType: DB_TYPES.ELASTIC_SEARCH,
19+
username: process.env.DB_USERNAME,
20+
password: process.env.DB_PASSWORD
1921
}
2022

2123
const emptyDBConfig: OceanNodeDBConfig = {

src/test/integration/elasticsearch.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ import { SQLLiteNonceDatabase } from '../../components/database/SQLLiteNonceData
1313

1414
const dbConfig = {
1515
url: 'http://localhost:9200',
16-
dbType: DB_TYPES.ELASTIC_SEARCH
16+
dbType: DB_TYPES.ELASTIC_SEARCH,
17+
username: 'elastic',
18+
password: 'changeme'
1719
}
1820
const elasticsearch: Database = await Database.init(dbConfig)
1921

src/test/unit/indexer/validation.test.ts

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ import { PROTOCOL_COMMANDS } from '../../../utils/constants.js'
1717
import { RPCS } from '../../../@types/blockchain.js'
1818
import { Database } from '../../../components/database/index.js'
1919
import { OceanNodeConfig } from '../../../@types/OceanNode.js'
20-
import sinon, { SinonSandbox } from 'sinon'
20+
// import sinon, { SinonSandbox } from 'sinon'
21+
import { ethers } from 'ethers'
22+
import { Readable } from 'stream'
2123

2224
describe('Schema validation tests', () => {
2325
const mockSupportedNetworks: RPCS = getMockSupportedNetworks()
@@ -26,7 +28,7 @@ describe('Schema validation tests', () => {
2628
let mockDatabase: Database
2729
let config: OceanNodeConfig
2830
let oceanNode: OceanNode
29-
let sandbox: SinonSandbox
31+
// let sandbox: SinonSandbox
3032

3133
// For token validation, please check integration test cases
3234
before(async () => {
@@ -48,7 +50,7 @@ describe('Schema validation tests', () => {
4850
)
4951
envOverrides = await setupEnvironment(TEST_ENV_CONFIG_FILE, envOverrides)
5052
config = await getConfiguration(true)
51-
sandbox = sinon.createSandbox()
53+
/* sandbox = sinon.createSandbox()
5254
sandbox.stub(Database, 'init').resolves({
5355
nonce: {},
5456
c2d: {},
@@ -60,6 +62,7 @@ describe('Schema validation tests', () => {
6062
order: {},
6163
ddoState: {}
6264
} as any)
65+
*/
6366
mockDatabase = await Database.init(config.dbConfig)
6467
oceanNode = await OceanNode.getInstance(
6568
config,
@@ -166,4 +169,35 @@ describe('Schema validation tests', () => {
166169

167170
expect(result.status.httpStatus).to.equal(401)
168171
})
172+
173+
it('should have node signature for valid user', async () => {
174+
const handler = new ValidateDDOHandler(oceanNode)
175+
const ddoInstance = DDOManager.getDDOClass(DDOExample)
176+
const ddo: DDO = {
177+
...(ddoInstance.getDDOData() as DDO)
178+
}
179+
const wallet = new ethers.Wallet(
180+
'0xef4b441145c1d0f3b4bc6d61d29f5c6e502359481152f869247c7a4244d45209'
181+
)
182+
const nonce = Date.now().toString()
183+
const message = String((await wallet.getAddress()) + nonce)
184+
const consumerMessage = ethers.solidityPackedKeccak256(
185+
['bytes'],
186+
[ethers.hexlify(ethers.toUtf8Bytes(message))]
187+
)
188+
const messageHashBytes = ethers.toBeArray(consumerMessage)
189+
const signature = await wallet.signMessage(messageHashBytes)
190+
const task = {
191+
ddo,
192+
publisherAddress: await wallet.getAddress(),
193+
nonce,
194+
signature,
195+
command: PROTOCOL_COMMANDS.VALIDATE_DDO
196+
}
197+
198+
const result = await handler.handle(task)
199+
200+
expect(result.status.httpStatus).to.equal(200)
201+
expect(result.stream).to.be.instanceOf(Readable)
202+
})
169203
})

src/utils/database.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,21 @@ export function hasValidDBConfiguration(configuration: OceanNodeDBConfig): boole
2323
if (!configuration || !configuration.dbType) {
2424
return false
2525
}
26-
return (
27-
configuration.url &&
28-
URLUtils.isValidUrl(configuration.url) &&
29-
[DB_TYPES.ELASTIC_SEARCH, DB_TYPES.TYPESENSE].includes(configuration.dbType)
26+
const hasValidUrl = configuration.url && URLUtils.isValidUrl(configuration.url)
27+
const hasValidDbType = [DB_TYPES.ELASTIC_SEARCH, DB_TYPES.TYPESENSE].includes(
28+
configuration.dbType
3029
)
30+
31+
if (!hasValidUrl || !hasValidDbType) {
32+
return false
33+
}
34+
35+
// For Elasticsearch, username and password are required
36+
if (configuration.dbType === DB_TYPES.ELASTIC_SEARCH) {
37+
return !!(configuration.username && configuration.password)
38+
}
39+
40+
return true
3141
}
3242

3343
// we can use this to check if DB connection is available

0 commit comments

Comments
 (0)