Skip to content

Commit e1ab6bf

Browse files
ebadierequiet-node
andauthored
feat: Added support for OPERATOR_KEY_FORMAT (#2591)
* feat: Added support for OPERATOR_KEY_FORMAT Signed-off-by: ebadiere <[email protected]> * Update packages/relay/tests/lib/sdkClient.spec.ts Co-authored-by: Logan Nguyen <[email protected]> Signed-off-by: Eric Badiere <[email protected]> * fix: refactored case condition to handle bad key format. Signed-off-by: ebadiere <[email protected]> * Update packages/relay/src/lib/services/hapiService/hapiService.ts Co-authored-by: Logan Nguyen <[email protected]> Signed-off-by: Eric Badiere <[email protected]> * fix: Added tests. Signed-off-by: ebadiere <[email protected]> --------- Signed-off-by: ebadiere <[email protected]> Signed-off-by: Eric Badiere <[email protected]> Co-authored-by: Logan Nguyen <[email protected]>
1 parent 54880b1 commit e1ab6bf

File tree

3 files changed

+119
-9
lines changed

3 files changed

+119
-9
lines changed

docs/configuration.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,14 @@ Unless you need to set a non-default value, it is recommended to only populate o
3535
| `INPUT_SIZE_LIMIT` | "1mb" | The [koa-jsonrpc](https://github.com/Bitclimb/koa-jsonrpc) maximum size allowed for requests |
3636
| `MAX_BLOCK_RANGE` | "5" | The maximum block number greater than the mirror node's latest block to query for |
3737
| `OPERATOR_ID_MAIN` | "" | Operator account ID used to pay for transactions. In `S.R.N` format, e.g. `0.0.1001`. |
38+
| `OPERATOR_KEY_FORMAT` | "DER" | Operator private key format. Valid types are `DER`, `HEX_ECDSA`, or `HEX_ED25519` |
3839
| `OPERATOR_KEY_MAIN` | "" | Operator private key used to sign transactions in hex encoded DER format. This may be either an ED22519 private key or an ECDSA private key. The private key must be associated with/used to derive `OPERATOR_ID_MAIN`. |
3940
| `RATE_LIMIT_DISABLED` | "false" | Flag to disable IP based rate limiting. |
4041
| `REQUEST_ID_IS_OPTIONAL` | "" | Flag to set it the JSON RPC request id field in the body should be optional. Note, this breaks the API spec and is not advised and is provided for test purposes only where some wallets may be non compliant |
4142
| `SERVER_PORT` | "7546" | The RPC server port number to listen for requests on. Currently a static value defaulting to 7546. See [#955](https://github.com/hashgraph/hedera-json-rpc-relay/issues/955) |
4243
| `SERVER_REQUEST_TIMEOUT_MS`| "60000" | The time of inactivity allowed before a timeout is triggered and the socket is closed. See [NodeJs Server Timeout](https://nodejs.org/api/http.html#serversettimeoutmsecs-callback) |
4344

45+
4446
## Relay
4547

4648
The following table lists the available properties along with their default values for the [Relay package](/packages/relay/).

packages/relay/src/lib/services/hapiService/hapiService.ts

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ export default class HAPIService {
209209
* @returns Client
210210
*/
211211
private initClient(logger: Logger, hederaNetwork: string, type: string | null = null): Client {
212-
let client: Client;
212+
let client: Client, privateKey: PrivateKey;
213213
if (hederaNetwork in constants.CHAIN_IDS) {
214214
client = Client.forName(hederaNetwork);
215215
} else {
@@ -218,19 +218,15 @@ export default class HAPIService {
218218

219219
if (type === 'eth_sendRawTransaction') {
220220
if (process.env.OPERATOR_ID_ETH_SENDRAWTRANSACTION && process.env.OPERATOR_KEY_ETH_SENDRAWTRANSACTION) {
221-
client = client.setOperator(
222-
AccountId.fromString(process.env.OPERATOR_ID_ETH_SENDRAWTRANSACTION),
223-
PrivateKey.fromString(process.env.OPERATOR_KEY_ETH_SENDRAWTRANSACTION),
224-
);
221+
privateKey = this.createPrivateKeyBasedOnFormat(process.env.OPERATOR_KEY_ETH_SENDRAWTRANSACTION);
222+
client = client.setOperator(AccountId.fromString(process.env.OPERATOR_ID_ETH_SENDRAWTRANSACTION), privateKey);
225223
} else {
226224
logger.warn(`Invalid 'ETH_SENDRAWTRANSACTION' env variables provided`);
227225
}
228226
} else {
229227
if (process.env.OPERATOR_ID_MAIN && process.env.OPERATOR_KEY_MAIN) {
230-
client = client.setOperator(
231-
AccountId.fromString(process.env.OPERATOR_ID_MAIN.trim()),
232-
PrivateKey.fromString(process.env.OPERATOR_KEY_MAIN),
233-
);
228+
privateKey = this.createPrivateKeyBasedOnFormat(process.env.OPERATOR_KEY_MAIN);
229+
client = client.setOperator(AccountId.fromString(process.env.OPERATOR_ID_MAIN.trim()), privateKey);
234230
} else {
235231
logger.warn(`Invalid 'OPERATOR' env variables provided`);
236232
}
@@ -250,6 +246,21 @@ export default class HAPIService {
250246
return client;
251247
}
252248

249+
private createPrivateKeyBasedOnFormat(operatorMainKey: string): PrivateKey {
250+
switch (process.env.OPERATOR_KEY_FORMAT) {
251+
case 'DER':
252+
case undefined:
253+
case null:
254+
return PrivateKey.fromStringDer(operatorMainKey);
255+
case 'HEX_ED25519':
256+
return PrivateKey.fromStringED25519(operatorMainKey);
257+
case 'HEX_ECDSA':
258+
return PrivateKey.fromStringECDSA(operatorMainKey);
259+
default:
260+
throw new Error(`Invalid OPERATOR_KEY_FORMAT provided: ${process.env.OPERATOR_KEY_FORMAT}`);
261+
}
262+
}
263+
253264
/**
254265
* Return current main client instance
255266
* @returns Main Client

packages/relay/tests/lib/sdkClient.spec.ts

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@
2121
import path from 'path';
2222
import dotenv from 'dotenv';
2323
import { expect } from 'chai';
24+
import { Context } from 'mocha';
2425
import sinon from 'sinon';
2526
import { Registry } from 'prom-client';
27+
import HAPIService from '../../src/lib/services/hapiService/hapiService';
2628
dotenv.config({ path: path.resolve(__dirname, '../test.env') });
2729
const registry = new Registry();
2830
import pino from 'pino';
@@ -49,6 +51,7 @@ import { CacheService } from '../../src/lib/services/cacheService/cacheService';
4951
import { MAX_GAS_LIMIT_HEX } from './eth/eth-config';
5052
import { getRequestId, signTransaction } from '../helpers';
5153
import { TransactionReceipt } from 'ethers';
54+
import exp from 'constants';
5255

5356
describe('SdkClient', async function () {
5457
this.timeout(20000);
@@ -174,4 +177,98 @@ describe('SdkClient', async function () {
174177
sinon.assert.calledOnce(convertGasPriceToTinyBarsStub);
175178
});
176179
});
180+
181+
describe('HAPIService', async () => {
182+
let originalEnv: NodeJS.ProcessEnv;
183+
184+
const OPERATOR_KEY_ED25519 = {
185+
DER: '302e020100300506032b65700422042091132178e72057a1d7528025956fe39b0b847f200ab59b2fdd367017f3087137',
186+
HEX_ED25519: '0x91132178e72057a1d7528025956fe39b0b847f200ab59b2fdd367017f3087137',
187+
};
188+
189+
const OPERATOR_KEY_ECDSA = {
190+
DER: '3030020100300706052b8104000a0422042008e926c84220295b5db5df25be107ce905b41e237ac748dd04d479c23dcdf2d5',
191+
HEX_ECDSA: '0x08e926c84220295b5db5df25be107ce905b41e237ac748dd04d479c23dcdf2d5',
192+
};
193+
194+
before(function (this: Context) {
195+
// Store the original process.env
196+
originalEnv = process.env;
197+
198+
if (
199+
this.currentTest?.title ===
200+
'Initialize the privateKey for default which is DER when OPERATOR_KEY_FORMAT is null'
201+
) {
202+
process.env = new Proxy(process.env, {
203+
get: (target, prop) => {
204+
if (prop === 'OPERATOR_KEY_FORMAT') {
205+
return null;
206+
}
207+
return target[prop];
208+
},
209+
});
210+
}
211+
});
212+
213+
after(() => {
214+
// Restore the original process.env after the test
215+
process.env = originalEnv;
216+
});
217+
218+
it('Initialize the privateKey for default which is DER', async () => {
219+
const hapiService = new HAPIService(logger, registry, hbarLimiter, new CacheService(logger, registry));
220+
const privateKey = (hapiService as any).createPrivateKeyBasedOnFormat.call(hapiService, OPERATOR_KEY_ED25519.DER);
221+
expect(privateKey.toString()).to.eq(OPERATOR_KEY_ED25519.DER);
222+
});
223+
224+
it('Initialize the privateKey for default which is DER when OPERATOR_KEY_FORMAT is undefined', async () => {
225+
delete process.env.OPERATOR_KEY_FORMAT;
226+
const hapiService = new HAPIService(logger, registry, hbarLimiter, new CacheService(logger, registry));
227+
const privateKey = (hapiService as any).createPrivateKeyBasedOnFormat.call(hapiService, OPERATOR_KEY_ED25519.DER);
228+
expect(privateKey.toString()).to.eq(OPERATOR_KEY_ED25519.DER);
229+
});
230+
231+
it('Initialize the privateKey for default which is DER when OPERATOR_KEY_FORMAT is null', async () => {
232+
const hapiService = new HAPIService(logger, registry, hbarLimiter, new CacheService(logger, registry));
233+
const privateKey = (hapiService as any).createPrivateKeyBasedOnFormat.call(hapiService, OPERATOR_KEY_ED25519.DER);
234+
expect(privateKey.toString()).to.eq(OPERATOR_KEY_ED25519.DER);
235+
});
236+
237+
it('Initialize the privateKey for OPERATOR_KEY_FORMAT set to DER', async () => {
238+
process.env.OPERATOR_KEY_FORMAT = 'DER';
239+
const hapiService = new HAPIService(logger, registry, hbarLimiter, new CacheService(logger, registry));
240+
const privateKey = (hapiService as any).createPrivateKeyBasedOnFormat.call(hapiService, OPERATOR_KEY_ECDSA.DER);
241+
expect(privateKey.toString()).to.eq(OPERATOR_KEY_ECDSA.DER);
242+
});
243+
244+
it('Initialize the privateKey for OPERATOR_KEY_FORMAT set to HEX_ED25519', async () => {
245+
process.env.OPERATOR_KEY_FORMAT = 'HEX_ED25519';
246+
const hapiService = new HAPIService(logger, registry, hbarLimiter, new CacheService(logger, registry));
247+
const privateKey = (hapiService as any).createPrivateKeyBasedOnFormat.call(
248+
hapiService,
249+
OPERATOR_KEY_ED25519.HEX_ED25519,
250+
);
251+
expect(privateKey.toString()).to.eq(OPERATOR_KEY_ED25519.DER);
252+
});
253+
254+
it('Initialize the privateKey for OPERATOR_KEY_FORMAT set to HEX_ECDSA', async () => {
255+
process.env.OPERATOR_KEY_FORMAT = 'HEX_ECDSA';
256+
const hapiService = new HAPIService(logger, registry, hbarLimiter, new CacheService(logger, registry));
257+
const privateKey = (hapiService as any).createPrivateKeyBasedOnFormat.call(
258+
hapiService,
259+
OPERATOR_KEY_ECDSA.HEX_ECDSA,
260+
);
261+
expect(privateKey.toString()).to.eq(OPERATOR_KEY_ECDSA.DER);
262+
});
263+
264+
it('It should throw an Error when an unexpected string is set', async () => {
265+
process.env.OPERATOR_KEY_FORMAT = 'BAD_FORMAT';
266+
try {
267+
const hapiService = new HAPIService(logger, registry, hbarLimiter, new CacheService(logger, registry));
268+
expect(true).to.be.false; // Should not make it here
269+
} catch (e: any) {
270+
expect(e.message).to.eq('Invalid OPERATOR_KEY_FORMAT provided: BAD_FORMAT');
271+
}
272+
});
273+
});
177274
});

0 commit comments

Comments
 (0)