Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/add tx filter #15

Open
wants to merge 23 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
8cdb5b8
set isAllowedContractMethod at tx_allow_check
Tsuyoshi-Ishikawa Feb 10, 2023
284a45f
modify README
Tsuyoshi-Ishikawa Feb 10, 2023
492e09e
add tx_restriction by webhook
Tsuyoshi-Ishikawa Feb 15, 2023
f84ae2a
add how to set webhook restriction to README
Tsuyoshi-Ishikawa Feb 15, 2023
8a426cb
add webhook_mock to tests
Tsuyoshi-Ishikawa Feb 16, 2023
378c8eb
add checkWebhook to tx_service
Tsuyoshi-Ishikawa Feb 16, 2023
3d47e6b
set webhook error message
Tsuyoshi-Ishikawa Feb 16, 2023
a76a631
modify allowCheck.service.spec
Tsuyoshi-Ishikawa Feb 17, 2023
fb1ec67
set webhookCheck to checkAllowedTx
Tsuyoshi-Ishikawa Feb 20, 2023
8f09488
modify contractMethodList to string[]
Tsuyoshi-Ishikawa Feb 20, 2023
c20a609
add RequestContext
Tsuyoshi-Ishikawa Feb 20, 2023
c44295d
modify webhook_body
Tsuyoshi-Ishikawa Feb 20, 2023
40870fb
modify address_check_logic
Tsuyoshi-Ishikawa Feb 21, 2023
1b5f43e
Revert "modify address_check_logic"
Tsuyoshi-Ishikawa Mar 7, 2023
d33abfe
resolve conflict
Tsuyoshi-Ishikawa Mar 7, 2023
1dbfb34
add Webhook document to /docs
Tsuyoshi-Ishikawa Mar 7, 2023
127031f
enable to webhook restriction
Tsuyoshi-Ishikawa Mar 7, 2023
ab0fab6
rename variable at getMatchedTxAllowRule
Tsuyoshi-Ishikawa Mar 7, 2023
f83c2ac
add contract method test at e2e
Tsuyoshi-Ishikawa Mar 8, 2023
4e8dcbe
resolve conflict
Tsuyoshi-Ishikawa Mar 23, 2023
46c2f28
modify transaction.service test
Tsuyoshi-Ishikawa Mar 23, 2023
cca78fc
modify e2e_test
Tsuyoshi-Ishikawa Mar 23, 2023
d2e6c67
resolve conflict
Tsuyoshi-Ishikawa Apr 4, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,59 @@ export const getTxAllowList = (): Array<TransactionAllow> => {
| lt | txValue < condition is allowed |
| lte | txValue <= condition is allowed |

#### Contract(Option)
You cant restrict to transact contract method.

```typescript
// everyone can only transact to greet and setGreeting to 0x5FbDB2315678afecb367f032d93F642f64180aa3
export const getTxAllowList = (): Array<TransactionAllow> => {
return [
{
fromList: ['*'],
toList: ['0x5FbDB2315678afecb367f032d93F642f64180aa3'],
contractList: [
'greet',
'setGreeting(string)',
],
},
];
};
// everyone can only transact to greet and setGreeting to 0x5FbDB2315678afecb367f032d93F642f64180aa3 and 0x5FbDB2315678afecb367f032d93F642f64180aa4
export const getTxAllowList = (): Array<TransactionAllow> => {
return [
{
fromList: ['*'],
toList: ['0x5FbDB2315678afecb367f032d93F642f64180aa3', '0x5FbDB2315678afecb367f032d93F642f64180aa4'],
contractList: [
'greet',
'setGreeting(string)',
],
},
];
};
```

```typescript
// if contractList is [], all transaction is allowed.
export const getTxAllowList = (): Array<TransactionAllow> => {
return [
{
fromList: ['*'],
toList: ['0x5FbDB2315678afecb367f032d93F642f64180aa3'],
contractList: [],
},
];
};
```

#### Webhook(Option)
You can add webhook setting that execute your original transaction restriction in another environment.

This setting allows you to post a request to a specified location before executing a transaction.
You can then use that request to implement your own request control.

If you set webhook restriction, follow [Webhook](https://github.com/oasysgames/verse-proxy/blob/master/docs/Webhook.md)

#### Transaction access rate limit(Option)
If you set transaction access rate limit, follow [Transaction access rate limit](/docs/RateLimit.md)

Expand Down
169 changes: 169 additions & 0 deletions docs/Webhook.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
# Webhook
You can add webhook setting that execute your original transaction restriction in another environment.

This setting allows you to post a request to a specified location before executing a transaction.
You can then use that request to implement your own request control.

| key | description | Required |
| ---- | ---- | ---- |
| url | Your restriction webhook url | O |
| headers | Information that you want to send to webhook(described later) as http_header | X |
| timeout | Webhook request timeout(ms) | O |
| retry | Webhook request retry times | O |
| parse | Whether to parse tx in the proxy(described later) | O |

```typescript
export const getTxAllowList = (): Array<TransactionAllow> => {
return [
{
fromList: ['*'],
toList: ['*'],
webhooks: [
{
url: 'https://localhost:8080',
headers: {
Authorization: 'Bearer xxxxxxxx',
},
timeout: 1000, // 1000 is 1 second.
retry: 3,
parse: false,
},
],
},
];
};
// You can set multiple webhook to `webhooks`.
export const getTxAllowList = (): Array<TransactionAllow> => {
return [
{
fromList: ['*'],
toList: ['*'],
webhooks: [
{
url: 'https://localhost:8080',
headers: {
Authorization: 'Bearer xxxxxxxx',
},
timeout: 1000, // 1000 is 1 second.
retry: 3,
parse: false,
},
{
url: 'https://localhost:8000',
timeout: 1000, // 1000 is 1 second.
retry: 3,
parse: false,
},
],
},
];
};
```

Webhook Request body patterns are following.

| Body key | Description |
| ---- | ---- |
| _meta.ip | client ip address |
| _meta.headers.host | jsonrpc host |
| _meta.headers.user-agent | client user agent |
| * | transaction data is set in body according to parse setting.



In case that header is not set and parse setting is false
```typescript
// src/config/transactionAllowList.ts
export const getTxAllowList = (): Array<TransactionAllow> => {
return [
{
fromList: ['*'],
toList: ['*'],
webhooks: [
{
url: 'https://rpc.sandverse.oasys.games/',
headers: {
host: 'rpc.sandverse.oasys.games',
Authorization: 'Bearer xxxxxxxx',
},
timeout: 1000,
retry: 3,
parse: false,
},
],
},
];
};
```

```typescript
{
// jsonrpc body is set
method: 'eth_sendRawTransaction',
params: [
'0xf8c62780826b23945fbdb2315678afecb367f032d93f642f64180aa380b864a41368620000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000568656c6c6f000000000000000000000000000000000000000000000000000000829deda02c108533361ec243ad0d7f88c07165327c1b14ec64565edbbd50d7193399f0b8a071de69bb3d7cd3aa8697c0a459b2ccc29401d715135cb8a34d2d703b7cd77f47'
],
id: 56,
jsonrpc: '2.0',
// meta is client information
_meta: {
ip: '::1',
headers: {
host: 'localhost:3001',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36'
}
}
}
```

In case that header is set Authorization and parse setting is true
```typescript
// src/config/transactionAllowList.ts
export const getTxAllowList = (): Array<TransactionAllow> => {
return [
{
fromList: ['*'],
toList: ['*'],
webhooks: [
{
url: 'https://rpc.sandverse.oasys.games/',
headers: {
host: 'rpc.sandverse.oasys.games',
Authorization: 'Bearer xxxxxxxx',
},
timeout: 1000,
retry: 3,
parse: true,
},
],
},
];
};
```

```typescript
{
// parsed jsonrpc body is set
nonce: 40,
gasPrice: BigNumber { _hex: '0x00', _isBigNumber: true },
gasLimit: BigNumber { _hex: '0x6b23', _isBigNumber: true },
to: '0x5FbDB2315678afecb367f032d93F642f64180aa3',
value: BigNumber { _hex: '0x00', _isBigNumber: true },
data: '0xa41368620000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000568656c6c6f000000000000000000000000000000000000000000000000000000',
chainId: 20197,
v: 40429,
r: '0x8a5a8f761bd45ba475b6b2a535a6d1a4d0941b657269b91ab19467e8a9d9d195',
s: '0x11c0fb7057f3e18a6e01c324ff8a084abd369acc5289ab586c7f13b4ae88933d',
from: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
hash: '0x159625a3df0467d5be60adf105dfc5626294434f45d7f6ce4aeefbee9cedf383',
type: null,
// meta is client information
_meta: {
ip: '::1',
headers: {
host: 'localhost:3001',
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36'
}
}
}
```
2 changes: 2 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
VerseService,
AllowCheckService,
TypeCheckService,
WebhookService,
RateLimitService,
} from './services';
import { DatastoreService } from './repositories';
Expand All @@ -28,6 +29,7 @@ import configuration from './config/configuration';
ProxyService,
AllowCheckService,
TypeCheckService,
WebhookService,
DatastoreService,
RateLimitService,
],
Expand Down
12 changes: 12 additions & 0 deletions src/config/transactionAllowList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ export interface ComparisonOperation {
lte?: string; // txValue <= condition is allowed
}

export interface Webhook {
url: string;
headers: {
[name: string]: string;
host: string;
};
timeout: number;
retry: number;
parse: boolean;
}
export interface RateLimit {
name: string;
perFrom?: boolean;
Expand All @@ -20,6 +30,8 @@ export interface TransactionAllow {
fromList: Array<string>;
toList: Array<string>;
value?: ComparisonOperation;
contractMethodList?: string[];
webhooks?: Array<Webhook>;
rateLimit?: RateLimit;
}

Expand Down
2 changes: 2 additions & 0 deletions src/controllers/__tests__/proxy.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
ProxyService,
AllowCheckService,
TypeCheckService,
WebhookService,
RateLimitService,
} from 'src/services';
import { ProxyController } from 'src/controllers';
Expand All @@ -28,6 +29,7 @@ describe('ProxyController', () => {
VerseService,
TransactionService,
AllowCheckService,
WebhookService,
TypeCheckService,
ProxyService,
RateLimitService,
Expand Down
1 change: 1 addition & 0 deletions src/controllers/proxy.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
Body,
ForbiddenException,
Res,
Ip,
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { IncomingHttpHeaders } from 'http';
Expand Down
8 changes: 8 additions & 0 deletions src/entities/Webhook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Transaction } from 'ethers';
import { JsonrpcRequestBody, RequestContext } from 'src/entities';

export interface WebhookTransferData {
requestContext: RequestContext;
body: JsonrpcRequestBody;
tx: Transaction;
}
2 changes: 2 additions & 0 deletions src/entities/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export * from './Request';
export * from './TransactionParam';
export * from './Jsonrpc';
export * from './Verse';
export * from './Webhook';
export * from './Error';
export * from './Request';
50 changes: 50 additions & 0 deletions src/services/__tests__/allowCheck.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,56 @@ describe('isIncludedAddress', () => {
});
});

describe('isAllowedContractMethod', () => {
const allowCheckService = new AllowCheckService();
test('contractMethodList is undefined', () => {
const contractMethodList = undefined;
const methodId = '0x095ea7b3'; // approve(address,uint256)

const result = allowCheckService.isAllowedContractMethod(
contractMethodList,
methodId,
);
expect(result).toBe(true);
});

test('contractMethodList is empty array', () => {
const contractMethodList: string[] = [];
const methodId = '0x095ea7b3'; // approve(address,uint256)

const result = allowCheckService.isAllowedContractMethod(
contractMethodList,
methodId,
);
expect(result).toBe(true);
});

test('allowedMethod is not in contractMethodList', () => {
const contractMethodList = ['transfer(address,uint256)'];
const methodId = '0x095ea7b3'; // approve(address,uint256)

const result = allowCheckService.isAllowedContractMethod(
contractMethodList,
methodId,
);
expect(result).toBe(false);
});

test('allowedMethod is in contractMethodList', () => {
const contractMethodList = [
'approve(address,uint256)',
'transfer(address,uint256)',
];
const methodId = '0x095ea7b3'; // approve(address,uint256)

const result = allowCheckService.isAllowedContractMethod(
contractMethodList,
methodId,
);
expect(result).toBe(true);
});
});

describe('isAllowedValue', () => {
const allowCheckService = new AllowCheckService();

Expand Down
2 changes: 2 additions & 0 deletions src/services/__tests__/proxy.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
AllowCheckService,
RateLimitService,
TypeCheckService,
WebhookService,
} from 'src/services';
import { JsonrpcError } from 'src/entities';
import { DatastoreService } from 'src/repositories';
Expand Down Expand Up @@ -87,6 +88,7 @@ describe('ProxyService', () => {
AllowCheckService,
TypeCheckService,
RateLimitService,
WebhookService,
DatastoreService,
],
}).compile();
Expand Down
Loading