这是 ShiningRay 提供的限流器 Aspect 的简化版,用于限制某些智能合约的请求速率。
典型场景:
- 在空投中,限制每个地址的领取频率。
- 对于有影响力的项目,保护其免受 DDoS 攻击。
在很多场景中, 我们不希望某些特定的接口、方法被频繁调用, 因此提出了限流的概念。 例如, 在网页前端使用键入同时触发搜索的功能, 为了防止用户频繁触发搜索, 我们可以设置一个时间间隔, 在这个时间间隔内, 用户只能触发一次搜索。 同样在区块链中, 也有类似的场景: 例如, 在空投中, 我们不希望用户频繁领取空投, 因此, 我们可以设置一个时间间隔在这个时间间隔内, 用户只能领取一次空投。
通过 Aspect 的 mutableState
储存方法的调用信息,并在 PreContractCall
切点中检查方法调用的频率。如果超过限制,交易将被中止。
EVM生态中可以在合约中实现限流功能, 但如果合约尚未考虑限流逻辑的话, 升级合约相对麻烦, 通过 Aspect 可以在不修改合约的情况下, 为合约增加限流功能。
$ npm run account:create
address: 0x6B70B03B608a19Bf1817848A4C8FFF844f0Be0fB
脚本将在项目目录中创建一个名为 privateKey.txt
的私钥文件。您也可以输入自己的私钥。记录下您的地址,然后您可以去 Artela 的 Discord 水龙头中申请测试币。
$ npm run contract:build
$ npm run contract:deploy
> contract:deploy
> node scripts/contract-deploy.cjs --name Counter
from address: 0x6B70B03B608a19Bf1817848A4C8FFF844f0Be0fB
(node:87588) ExperimentalWarning: The Fetch API is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
deploy contract tx hash: 0x55d445796c0bc4435e827e88ee35104205369c685d9f06bbfc42d37bd4229769
{
blockHash: '0xb26ee4e2a2f24f1e10b13b9978ca16651f85ac862dd586770fa174dcdb325fd8',
blockNumber: 2175119,
contractAddress: '0x9CEAE67580eB1d82B9CeEe53e57f137f66D87d83',
cumulativeGasUsed: 3500000,
from: '0x6b70b03b608a19bf1817848a4c8fff844f0be0fb',
gasUsed: 7000001,
logs: [],
logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
status: true,
to: null,
transactionHash: '0x55d445796c0bc4435e827e88ee35104205369c685d9f06bbfc42d37bd4229769',
transactionIndex: 0,
type: '0x0'
}
contract address: 0x9CEAE67580eB1d82B9CeEe53e57f137f66D87d83
--contractAccount 0x6B70B03B608a19Bf1817848A4C8FFF844f0Be0fB --contractAddress 0x9CEAE67580eB1d82B9CeEe53e57f137f66D87d83
记住最后部署的合约地址,以便后续绑定。
$ npm run aspect:build
构建好之后,运行部署脚本。
$ node scripts/aspect-deploy.cjs --interval 5 --limit 2
from address: 0x6B70B03B608a19Bf1817848A4C8FFF844f0Be0fB
sending signed transaction...
{
blockHash: '0xdaaa2b913be2bb3007a6324b1ac81f5c824a9d61e52775f0cf300b8d66b87967',
blockNumber: 2177337,
contractAddress: null,
cumulativeGasUsed: 0,
from: '0x6b70b03b608a19bf1817848a4c8fff844f0be0fb',
gasUsed: 9000001,
logs: [],
logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
status: true,
to: '0x0000000000000000000000000000000000a27e14',
transactionHash: '0xf665be06dbd652060dda053beb599c0b9da710314d32a79b35189082f776ec58',
transactionIndex: 0,
type: '0x0',
aspectAddress: '0x9AE212EFbc8935D95DD266947cDb231571c1A09e'
}
ret: {
blockHash: '0xdaaa2b913be2bb3007a6324b1ac81f5c824a9d61e52775f0cf300b8d66b87967',
blockNumber: 2177337,
contractAddress: null,
cumulativeGasUsed: 0,
from: '0x6b70b03b608a19bf1817848a4c8fff844f0be0fb',
gasUsed: 9000001,
logs: [],
logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
status: true,
to: '0x0000000000000000000000000000000000a27e14',
transactionHash: '0xf665be06dbd652060dda053beb599c0b9da710314d32a79b35189082f776ec58',
transactionIndex: 0,
type: '0x0',
aspectAddress: '0x9AE212EFbc8935D95DD266947cDb231571c1A09e'
}
== deploy aspectID == 0x9AE212EFbc8935D95DD266947cDb231571c1A09e
参数为:
- interval:限流区块数量间隔
- limit:每个间隔最多能执行的次数
记住最后的 aspectID,以便后续绑定。
执行 bind.cjs
脚本,并代入之前部署的合约地址(或者自己的合约地址)和 aspectID。
$ node scripts/bind.cjs --contract <CONTRACT_ADDRESS> --aspectId <ASPECT_ID>
sending signed transaction...
{
blockHash: '0x619117094ee0083aafdb5400891c5e293976306d112e05a58d8836b24c808e68',
blockNumber: 2175732,
contractAddress: null,
cumulativeGasUsed: 0,
from: '0x6b70b03b608a19bf1817848a4c8fff844f0be0fb',
gasUsed: 9000001,
logs: [],
logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
status: true,
to: '0x0000000000000000000000000000000000a27e14',
transactionHash: '0x59529c6cbb2658e9f8f70ae3848fd52990c2e3d96e0cf58c26b4a83e14e76f13',
transactionIndex: 0,
type: '0x0'
}
== aspect bind success ==
然后我们可以执行查询合约绑定的 Aspect 的脚本,检查合约是否绑定成功。
$ node scripts/query.cjs --contract <CONTRACT_ADDRESS>
bound aspects : 0x9AE212EFbc8935D95DD266947cDb231571c1A09e,1,1
输出中显示了上述的 aspectID,表示已经成功绑定。
scripts/batch-test.cjs
会批量发送合约交易,用以测试限流功能。
$ node scripts/batch-test.cjs
#0
call contract tx hash: 0x8fbf4f2768e265045ee18a8d9c846a187656ff69b12c65776ac05958fe6ce6c9
#1
call contract tx hash: 0x7db621cbf06bed8d1569517292427ba06c65ab361ff8fc110ad2be3e6396b0c8
#2
call contract tx hash: 0x9d1c26f29a63f0b074a9743c0383a2e5d12eb3cd048627aa6cde98d9da6ad6be
#3
call contract tx hash: 0xbbc0452038fe672ad77b8acb16dc8ad7a4e12c00389aa24458762e30810b988c
#4
call contract tx hash: 0x2f678aebc6a635d490f1e306eab34a2fab9bdc3406598ed963378ccc97b40a28
#5
{
blockHash: '0xe73887b147b24d68077b54ef4dc6d20a3682b895780c129f1c73c993c3f9f06c',
blockNumber: 2177577,
contractAddress: null,
cumulativeGasUsed: 2000000,
from: '0x6b70b03b608a19bf1817848a4c8fff844f0be0fb',
gasUsed: 4000001,
logs: [],
logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
status: true,
to: '0x9ceae67580eb1d82b9ceee53e57f137f66d87d83',
transactionHash: '0x8fbf4f2768e265045ee18a8d9c846a187656ff69b12c65776ac05958fe6ce6c9',
transactionIndex: 0,
type: '0x0'
}
call contract tx hash: 0x7db43657de0bd98ede59ece6ec54c5db1397998320cc9d4375848a04a9e19419
#6
/Users/shiningray/projects/personal/throttle-aspect/node_modules/web3-core-helpers/lib/errors.js:90
var error = new Error(message);
^
Error: Transaction has been reverted by the EVM:
{
"blockHash": "0x81ce98a29a43349e5e23dee5e57ed6af94287f91c42eb018bdb6d92a357b7cd4",
"blockNumber": 2177579,
"contractAddress": null,
"cumulativeGasUsed": 4000000,
"from": "0x6b70b03b608a19bf1817848a4c8fff844f0be0fb",
"gasUsed": 4000001,
"logs": [],
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"status": false,
"to": "0x9ceae67580eb1d82b9ceee53e57f137f66d87d83",
"transactionHash": "0x9d1c26f29a63f0b074a9743c0383a2e5d12eb3cd048627aa6cde98d9da6ad6be",
"transactionIndex": 1,
"type": "0x0"
}
at Object.TransactionError (/Users/shiningray/projects/personal/throttle-aspect/node_modules/web3-core-helpers/lib/errors.js:90:21)
at Object.TransactionRevertedWithoutReasonError (/Users/shiningray/projects/personal/throttle-aspect/node_modules/web3-core-helpers/lib/errors.js:101:21)
at /Users/shiningray/projects/personal/throttle-aspect/node_modules/@artela/web3-core-method/lib/index.js:456:57
at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
receipt: {
blockHash: '0x81ce98a29a43349e5e23dee5e57ed6af94287f91c42eb018bdb6d92a357b7cd4',
blockNumber: 2177579,
contractAddress: null,
cumulativeGasUsed: 4000000,
from: '0x6b70b03b608a19bf1817848a4c8fff844f0be0fb',
gasUsed: 4000001,
logs: [],
logsBloom: '0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
status: false,
to: '0x9ceae67580eb1d82b9ceee53e57f137f66d87d83',
transactionHash: '0x9d1c26f29a63f0b074a9743c0383a2e5d12eb3cd048627aa6cde98d9da6ad6be',
transactionIndex: 1,
type: '0x0'
}
}
脚本返回报错,表示限流器成功阻止了交易的成功。