-
Notifications
You must be signed in to change notification settings - Fork 0
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
test: e2e dispute #72
Merged
Merged
Changes from all commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
8cd31c2
docs: add error types for ProphetCodec functions
0xyaco 65ff4c3
chore: fix linters
0xyaco 30857c1
fix: handle chains with blocks with same timestamp
0xyaco 23ec54e
fix: add BigNumber to binary search
0xyaco e7d8b23
docs: add error types for ProphetCodec functions
0xyaco f34d525
fix: structs abi fields definition
0xyaco 8cbcbcc
fix: avoid duplicating events during getEvents
0xyaco 73c6351
fix: skip past events trying to be enqueued
0xyaco 8b74a67
fix: handle request already created error appropriately
0xyaco 1f47682
test: happy path working
0xyaco 7885d9d
chore: remove caret from package.json
0xyaco 0d0ba6b
Merge branch 'dev' into fix/blocknumber-binsearch
0xyaco b494c94
fix: candidate block number maths
0xyaco d1e2c55
refactor: normalize search methods naming
0xyaco 4e9c335
Merge branch 'fix/blocknumber-binsearch' into fix/e2e-scenario-1
0xyaco 635ac60
fix: fix blocks interval bounds during events sync
0xyaco 86fb47c
test: test E2E response disputal
0xyaco c8b7399
fix: fix logging and comment
0xyaco e711311
fix: wait for disputestatus event
0xyaco d3ba273
Merge remote-tracking branch 'origin/dev' into test/e2e-scenario-2
0xyaco 913649c
docs: finalize request docs
0xyaco a1b0fe6
fix: use Promise.all during pledging for dispute
0xyaco File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ import { | |
ProphetCodec, | ||
ProtocolProvider, | ||
} from "@ebo-agent/automated-dispute"; | ||
import { RequestId } from "@ebo-agent/automated-dispute/dist/types/prophet.js"; | ||
import { BlockNumberService } from "@ebo-agent/blocknumber"; | ||
import { Caip2ChainId, Logger } from "@ebo-agent/shared"; | ||
import { CreateServerReturnType } from "prool"; | ||
|
@@ -78,6 +79,10 @@ const FORK_L2_URL = "https://arbitrum-sepolia.gateway.tenderly.co"; | |
|
||
const PROTOCOL_L2_LOCAL_URL = `http://${PROTOCOL_L2_LOCAL_RPC_HOST}:${PROTOCOL_L2_LOCAL_RPC_PORT}/1`; | ||
|
||
const newEventAbi = parseAbiItem( | ||
"event NewEpoch(uint256 indexed _epoch, string indexed _chainId, uint256 _blockNumber)", | ||
); | ||
|
||
describe.sequential("single agent", () => { | ||
let l2ProtocolAnvil: CreateServerReturnType; | ||
|
||
|
@@ -258,7 +263,7 @@ describe.sequential("single agent", () => { | |
blockTimeout: initBlock + 1000n, | ||
}); | ||
|
||
expect(requestCreatedEvent).toBe(true); | ||
expect(requestCreatedEvent).toBeDefined(); | ||
|
||
const responseProposedAbi = getAbiItem({ abi: oracleAbi, name: "ResponseProposed" }); | ||
|
||
|
@@ -277,7 +282,7 @@ describe.sequential("single agent", () => { | |
blockTimeout: initBlock + 1000n, | ||
}); | ||
|
||
expect(responseProposedEvent).toBe(true); | ||
expect(responseProposedEvent).toBeDefined(); | ||
|
||
await anvilClient.increaseTime({ seconds: 60 * 60 * 24 * 7 * 4 }); | ||
|
||
|
@@ -306,9 +311,7 @@ describe.sequential("single agent", () => { | |
filter: { | ||
address: protocolContracts["EBOFinalityModule"], | ||
fromBlock: initBlock, | ||
event: parseAbiItem( | ||
"event NewEpoch(uint256 indexed _epoch, string indexed _chainId, uint256 _blockNumber)", | ||
), | ||
event: newEventAbi, | ||
strict: true, | ||
}, | ||
matcher: (log) => { | ||
|
@@ -325,4 +328,260 @@ describe.sequential("single agent", () => { | |
expect(oracleRequestFinalizedEvent).toBeDefined(); | ||
expect(newEpochEvent).toBeDefined(); | ||
}); | ||
|
||
/** | ||
* Given: | ||
* - A single agent A1 operating for chain CHAIN1 | ||
* - A request REQ1 for E1, a wrong response RESP1(REQ1) | ||
* - Within the RESP1 dispute window | ||
* | ||
* When: | ||
* - A1 detects RESP1 is wrong | ||
* | ||
* Then: | ||
* - A1 disputes RESP1 with DISP1 | ||
* - `ResponseDisputed(RESP1.id, DISP1.id, DISP1)` | ||
* - A1 proposes RESP2 | ||
* - `ResponseProposed(REQ1.id, RESP2.id, RESP2)` | ||
* - A1 settles DISP1 and it ends with status `Won` | ||
* - `DisputeStatusUpdated(DISP1.id, DISP1, "Won")` | ||
* - A1 finalizes REQ1 with RESP2 | ||
* - `EBOFinalityModule.newEpoch(E1, CHAIN1, RESP2.response)` | ||
* - `OracleRequestFinalized(REQ1.id, RESP2.id, A1.address)` | ||
*/ | ||
test.skip("dispute response and propose a new one", { timeout: E2E_TEST_TIMEOUT }, async () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will the test be enabled later? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, the idea is to enable every test to be run sequentially later |
||
const logger = Logger.getInstance(); | ||
|
||
const protocolProvider = new ProtocolProvider( | ||
{ | ||
l1: { | ||
chainId: PROTOCOL_L2_CHAIN_ID, | ||
// Using the same RPC due to Anvil's arbitrum block number bug | ||
urls: [PROTOCOL_L2_LOCAL_URL], | ||
transactionReceiptConfirmations: 1, | ||
timeout: 1_000, | ||
retryInterval: 500, | ||
}, | ||
l2: { | ||
chainId: PROTOCOL_L2_CHAIN_ID, | ||
urls: [PROTOCOL_L2_LOCAL_URL], | ||
transactionReceiptConfirmations: 1, | ||
timeout: 1_000, | ||
retryInterval: 500, | ||
}, | ||
}, | ||
{ | ||
bondEscalationModule: protocolContracts["BondEscalationModule"], | ||
eboRequestCreator: protocolContracts["EBORequestCreator"], | ||
epochManager: EPOCH_MANAGER_ADDRESS, | ||
oracle: protocolContracts["Oracle"], | ||
horizonAccountingExtension: protocolContracts["HorizonAccountingExtension"], | ||
}, | ||
accounts[0].privateKey, | ||
); | ||
|
||
vi.spyOn(protocolProvider, "getAccountingApprovedModules").mockResolvedValue([ | ||
protocolContracts["EBORequestModule"], | ||
protocolContracts["BondedResponseModule"], | ||
protocolContracts["BondEscalationModule"], | ||
]); | ||
|
||
const blockNumberService = new BlockNumberService( | ||
new Map<Caip2ChainId, string[]>([[PROTOCOL_L2_CHAIN_ID, [PROTOCOL_L2_LOCAL_URL]]]), | ||
{ | ||
baseUrl: new URL("http://not.needed/"), | ||
bearerToken: "not.needed", | ||
bearerTokenExpirationWindow: 1000, | ||
servicePaths: { | ||
block: "/block", | ||
blockByTime: "/blockByTime", | ||
}, | ||
}, | ||
logger, | ||
); | ||
|
||
const actorsManager = new EboActorsManager(); | ||
|
||
const processor = new EboProcessor( | ||
{ | ||
requestModule: protocolContracts["EBORequestModule"], | ||
responseModule: protocolContracts["BondedResponseModule"], | ||
escalationModule: protocolContracts["BondEscalationModule"], | ||
}, | ||
protocolProvider, | ||
blockNumberService, | ||
actorsManager, | ||
logger, | ||
{ | ||
notifyError: vi.fn(), | ||
} as unknown as NotificationService, | ||
); | ||
|
||
const anvilClient = createTestClient({ | ||
mode: "anvil", | ||
account: GRT_HOLDER, | ||
chain: PROTOCOL_L2_CHAIN, | ||
transport: http(PROTOCOL_L2_LOCAL_URL), | ||
}) | ||
.extend(publicActions) | ||
.extend(walletActions); | ||
|
||
// Set epoch length to a big enough epoch length as in sepolia is way too short at the moment | ||
await setEpochLength({ | ||
length: 100_000n, | ||
client: anvilClient, | ||
epochManagerAddress: EPOCH_MANAGER_ADDRESS, | ||
governorAddress: GOVERNOR_ADDRESS, | ||
}); | ||
|
||
const initBlock = await anvilClient.getBlockNumber(); | ||
const currentEpoch = await protocolProvider.getCurrentEpoch(); | ||
|
||
const correctResponse = await blockNumberService.getEpochBlockNumber( | ||
currentEpoch.startTimestamp, | ||
PROTOCOL_L2_CHAIN_ID, | ||
); | ||
|
||
await protocolProvider.createRequest(currentEpoch.number, PROTOCOL_L2_CHAIN_ID); | ||
|
||
const requestCreatedEvent = await waitForEvent({ | ||
client: anvilClient, | ||
filter: { | ||
address: protocolContracts["Oracle"], | ||
fromBlock: initBlock, | ||
event: getAbiItem({ abi: oracleAbi, name: "RequestCreated" }), | ||
strict: true, | ||
}, | ||
pollingIntervalMs: 100, | ||
blockTimeout: initBlock + 1000n, | ||
}); | ||
|
||
expect(requestCreatedEvent).toBeDefined(); | ||
|
||
await protocolProvider.proposeResponse(requestCreatedEvent.args._request, { | ||
proposer: accounts[0].account.address, | ||
requestId: requestCreatedEvent.args._requestId as RequestId, | ||
response: ProphetCodec.encodeResponse({ block: correctResponse - 1n }), | ||
}); | ||
|
||
const badResponseProposedEvent = await waitForEvent({ | ||
client: anvilClient, | ||
filter: { | ||
address: protocolContracts["Oracle"], | ||
fromBlock: initBlock, | ||
event: getAbiItem({ abi: oracleAbi, name: "ResponseProposed" }), | ||
strict: true, | ||
}, | ||
pollingIntervalMs: 100, | ||
blockTimeout: initBlock + 1000n, | ||
}); | ||
|
||
processor.start(3000); | ||
|
||
const badResponseDisputedEvent = await waitForEvent({ | ||
client: anvilClient, | ||
filter: { | ||
address: protocolContracts["Oracle"], | ||
fromBlock: initBlock, | ||
event: getAbiItem({ abi: oracleAbi, name: "ResponseDisputed" }), | ||
strict: true, | ||
}, | ||
matcher: (log) => { | ||
return log.args._responseId === badResponseProposedEvent.args._responseId; | ||
}, | ||
pollingIntervalMs: 100, | ||
blockTimeout: initBlock + 1000n, | ||
}); | ||
|
||
expect(badResponseDisputedEvent).toBeDefined(); | ||
|
||
const correctResponseProposedEvent = await waitForEvent({ | ||
client: anvilClient, | ||
filter: { | ||
address: protocolContracts["Oracle"], | ||
fromBlock: initBlock, | ||
event: getAbiItem({ abi: oracleAbi, name: "ResponseProposed" }), | ||
strict: true, | ||
}, | ||
matcher: (log) => { | ||
const responseBlock = ProphetCodec.decodeResponse( | ||
log.args._response.response, | ||
).block; | ||
|
||
return ( | ||
log.args._requestId === requestCreatedEvent.args._requestId && | ||
log.args._responseId !== badResponseProposedEvent.args._responseId && | ||
responseBlock === correctResponse | ||
); | ||
}, | ||
pollingIntervalMs: 100, | ||
blockTimeout: initBlock + 1000n, | ||
}); | ||
|
||
expect(correctResponseProposedEvent).toBeDefined(); | ||
|
||
await anvilClient.increaseTime({ seconds: 60 * 60 * 24 * 7 * 4 }); | ||
|
||
const disputeSettledEvent = await waitForEvent({ | ||
client: anvilClient, | ||
filter: { | ||
address: protocolContracts["Oracle"], | ||
fromBlock: initBlock, | ||
event: getAbiItem({ abi: oracleAbi, name: "DisputeStatusUpdated" }), | ||
strict: true, | ||
}, | ||
matcher: (log) => { | ||
const status = ProphetCodec.decodeDisputeStatus(log.args._status); | ||
|
||
return ( | ||
log.args._disputeId === badResponseDisputedEvent.args._disputeId && | ||
status === "Won" | ||
); | ||
}, | ||
pollingIntervalMs: 100, | ||
blockTimeout: initBlock + 1000n, | ||
}); | ||
|
||
expect(disputeSettledEvent).toBeDefined(); | ||
|
||
const [requestFinalizedEvent, newEpochEvent] = await Promise.all([ | ||
waitForEvent({ | ||
client: anvilClient, | ||
filter: { | ||
address: protocolContracts["Oracle"], | ||
fromBlock: initBlock, | ||
event: getAbiItem({ abi: oracleAbi, name: "OracleRequestFinalized" }), | ||
strict: true, | ||
}, | ||
matcher: (log) => { | ||
return ( | ||
log.args._requestId === requestCreatedEvent.args._requestId && | ||
log.args._responseId === correctResponseProposedEvent.args._responseId | ||
); | ||
}, | ||
pollingIntervalMs: 100, | ||
blockTimeout: initBlock + 1000n, | ||
}), | ||
waitForEvent({ | ||
client: anvilClient, | ||
filter: { | ||
address: protocolContracts["EBOFinalityModule"], | ||
fromBlock: initBlock, | ||
event: newEventAbi, | ||
strict: true, | ||
}, | ||
matcher: (log) => { | ||
return ( | ||
log.args._chainId === keccak256(toHex(ARBITRUM_SEPOLIA_ID)) && | ||
log.args._epoch === currentEpoch.number | ||
); | ||
}, | ||
pollingIntervalMs: 100, | ||
blockTimeout: initBlock + 1000n, | ||
}), | ||
]); | ||
|
||
expect(requestFinalizedEvent).toBeDefined(); | ||
expect(newEpochEvent).toBeDefined(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice explanation