parent
e1d5133ae6
commit
ebadcefcd2
@ -1,198 +0,0 @@ |
||||
import { assert } from 'chai'; |
||||
import * as ethers from 'ethers'; |
||||
import { types, Validator } from '@abacus-network/utils'; |
||||
|
||||
import { |
||||
TestOutbox, |
||||
TestOutbox__factory, |
||||
ValidatorManager, |
||||
ValidatorManager__factory, |
||||
UpgradeBeaconController, |
||||
UpgradeBeaconController__factory, |
||||
XAppConnectionManager, |
||||
XAppConnectionManager__factory, |
||||
TestInbox, |
||||
TestInbox__factory, |
||||
} from '../../types'; |
||||
|
||||
export interface AbacusInstance { |
||||
domain: types.Domain; |
||||
validator: Validator; |
||||
validatorManager: ValidatorManager; |
||||
outbox: TestOutbox; |
||||
connectionManager: XAppConnectionManager; |
||||
ubc: UpgradeBeaconController; |
||||
inboxs: Record<number, TestInbox>; |
||||
} |
||||
|
||||
export class AbacusDeployment { |
||||
constructor( |
||||
public readonly domains: types.Domain[], |
||||
public readonly instances: Record<number, AbacusInstance>, |
||||
public readonly signer: ethers.Signer, |
||||
) {} |
||||
|
||||
static async fromDomains(domains: types.Domain[], signer: ethers.Signer) { |
||||
const instances: Record<number, AbacusInstance> = {}; |
||||
for (const local of domains) { |
||||
const instance = await AbacusDeployment.deployInstance( |
||||
local, |
||||
domains.filter((d) => d !== local), |
||||
signer, |
||||
); |
||||
instances[local] = instance; |
||||
} |
||||
return new AbacusDeployment(domains, instances, signer); |
||||
} |
||||
|
||||
static async deployInstance( |
||||
local: types.Domain, |
||||
remotes: types.Domain[], |
||||
signer: ethers.Signer, |
||||
): Promise<AbacusInstance> { |
||||
const validatorManagerFactory = new ValidatorManager__factory(signer); |
||||
const validatorManager = await validatorManagerFactory.deploy(); |
||||
await validatorManager.enrollValidator(local, await signer.getAddress()); |
||||
await Promise.all( |
||||
remotes.map(async (remoteDomain) => |
||||
validatorManager.enrollValidator( |
||||
remoteDomain, |
||||
await signer.getAddress(), |
||||
), |
||||
), |
||||
); |
||||
|
||||
const ubcFactory = new UpgradeBeaconController__factory(signer); |
||||
const ubc = await ubcFactory.deploy(); |
||||
|
||||
const outboxFactory = new TestOutbox__factory(signer); |
||||
const outbox = await outboxFactory.deploy(local); |
||||
await outbox.initialize(validatorManager.address); |
||||
|
||||
const connectionManagerFactory = new XAppConnectionManager__factory(signer); |
||||
const connectionManager = await connectionManagerFactory.deploy(); |
||||
await connectionManager.setOutbox(outbox.address); |
||||
|
||||
const inboxFactory = new TestInbox__factory(signer); |
||||
const inboxs: Record<number, TestInbox> = {}; |
||||
const deploys = remotes.map(async (remoteDomain) => { |
||||
const inbox = await inboxFactory.deploy(local); |
||||
await inbox.initialize( |
||||
remoteDomain, |
||||
validatorManager.address, |
||||
ethers.constants.HashZero, |
||||
0, |
||||
); |
||||
await connectionManager.enrollInbox(remoteDomain, inbox.address); |
||||
inboxs[remoteDomain] = inbox; |
||||
}); |
||||
await Promise.all(deploys); |
||||
return { |
||||
domain: local, |
||||
validator: await Validator.fromSigner(signer, local), |
||||
outbox, |
||||
connectionManager, |
||||
validatorManager, |
||||
inboxs, |
||||
ubc, |
||||
}; |
||||
} |
||||
|
||||
async transferOwnership(domain: types.Domain, address: types.Address) { |
||||
await this.outbox(domain).transferOwnership(address); |
||||
await this.ubc(domain).transferOwnership(address); |
||||
await this.connectionManager(domain).transferOwnership(address); |
||||
await this.validatorManager(domain).transferOwnership(address); |
||||
for (const remote of this.domains) { |
||||
if (remote !== domain) { |
||||
await this.inbox(domain, remote).transferOwnership(address); |
||||
} |
||||
} |
||||
} |
||||
|
||||
outbox(domain: types.Domain): TestOutbox { |
||||
return this.instances[domain].outbox; |
||||
} |
||||
|
||||
ubc(domain: types.Domain): UpgradeBeaconController { |
||||
return this.instances[domain].ubc; |
||||
} |
||||
|
||||
validator(domain: types.Domain): Validator { |
||||
return this.instances[domain].validator; |
||||
} |
||||
|
||||
inbox(local: types.Domain, remote: types.Domain): TestInbox { |
||||
return this.instances[local].inboxs[remote]; |
||||
} |
||||
|
||||
connectionManager(domain: types.Domain): XAppConnectionManager { |
||||
return this.instances[domain].connectionManager; |
||||
} |
||||
|
||||
validatorManager(domain: types.Domain): ValidatorManager { |
||||
return this.instances[domain].validatorManager; |
||||
} |
||||
|
||||
async processMessages() { |
||||
await Promise.all( |
||||
this.domains.map((d) => this.processMessagesFromDomain(d)), |
||||
); |
||||
} |
||||
|
||||
async processMessagesFromDomain(local: types.Domain) { |
||||
const outbox = this.outbox(local); |
||||
const [checkpointedRoot, checkpointedIndex] = |
||||
await outbox.latestCheckpoint(); |
||||
const latestIndex = await outbox.tree(); |
||||
if (latestIndex.eq(checkpointedIndex)) return; |
||||
|
||||
// Find the block number of the last checkpoint submitted on Outbox.
|
||||
const checkpointFilter = outbox.filters.Checkpoint(checkpointedRoot); |
||||
const checkpoints = await outbox.queryFilter(checkpointFilter); |
||||
assert(checkpoints.length === 0 || checkpoints.length === 1); |
||||
const fromBlock = checkpoints.length === 0 ? 0 : checkpoints[0].blockNumber; |
||||
|
||||
await outbox.checkpoint(); |
||||
const [root, index] = await outbox.latestCheckpoint(); |
||||
// If there have been no checkpoints since the last checkpoint, return.
|
||||
if ( |
||||
index.eq(0) || |
||||
(checkpoints.length == 1 && index.eq(checkpoints[0].args.index)) |
||||
) { |
||||
return; |
||||
} |
||||
// TODO come back to this!
|
||||
// Update the Outbox and Inboxs to the latest roots.
|
||||
// This is technically not necessary given that we are not proving against
|
||||
// a root in the TestInbox.
|
||||
// const validator = this.validator(local);
|
||||
// const { signature } = await validator.signCheckpoint(
|
||||
// root,
|
||||
// index.toNumber(),
|
||||
// );
|
||||
|
||||
for (const remote of this.domains) { |
||||
if (remote !== local) { |
||||
const inbox = this.inbox(remote, local); |
||||
await inbox.checkpoint(root, index); // TODO come back to this!
|
||||
} |
||||
} |
||||
|
||||
// Find all messages dispatched on the outbox since the previous checkpoint.
|
||||
const dispatchFilter = outbox.filters.Dispatch(); |
||||
const dispatches = await outbox.queryFilter(dispatchFilter, fromBlock); |
||||
for (const dispatch of dispatches) { |
||||
const destination = dispatch.args.destinationAndNonce.shr(32).toNumber(); |
||||
if (destination !== local) { |
||||
const inbox = this.inbox(destination, local); |
||||
await inbox.setMessageProven(dispatch.args.message); |
||||
await inbox.testProcess(dispatch.args.message); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
export const abacus: any = { |
||||
AbacusDeployment, |
||||
}; |
@ -1,150 +0,0 @@ |
||||
import { ethers } from 'hardhat'; |
||||
import { expect } from 'chai'; |
||||
import { types, utils, Validator } from '@abacus-network/utils'; |
||||
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; |
||||
|
||||
import { |
||||
Outbox__factory, |
||||
Outbox, |
||||
ValidatorManager__factory, |
||||
ValidatorManager, |
||||
} from '../types'; |
||||
|
||||
const domainHashCases = require('../../../vectors/domainHash.json'); |
||||
const localDomain = 1000; |
||||
|
||||
describe('ValidatorManager', async () => { |
||||
let signer: SignerWithAddress, |
||||
fakeSigner: SignerWithAddress, |
||||
validatorManager: ValidatorManager, |
||||
validator: Validator, |
||||
fakeValidator: Validator; |
||||
|
||||
before(async () => { |
||||
[signer, fakeSigner] = await ethers.getSigners(); |
||||
validator = await Validator.fromSigner(signer, localDomain); |
||||
fakeValidator = await Validator.fromSigner(fakeSigner, localDomain); |
||||
}); |
||||
|
||||
beforeEach(async () => { |
||||
const validatorManagerFactory = new ValidatorManager__factory(signer); |
||||
validatorManager = await validatorManagerFactory.deploy(); |
||||
await validatorManager.enrollValidator(localDomain, validator.address); |
||||
}); |
||||
|
||||
it('Accepts validator signature', async () => { |
||||
const root = ethers.utils.formatBytes32String('root'); |
||||
const index = 1; |
||||
|
||||
const { signature } = await validator.signCheckpoint(root, index); |
||||
const isValid = await validatorManager.isValidatorSignature( |
||||
localDomain, |
||||
root, |
||||
index, |
||||
signature, |
||||
); |
||||
expect(isValid).to.be.true; |
||||
}); |
||||
|
||||
it('Rejects non-validator signature', async () => { |
||||
const root = ethers.utils.formatBytes32String('root'); |
||||
const index = 1; |
||||
|
||||
const { signature } = await fakeValidator.signCheckpoint(root, index); |
||||
const isValid = await validatorManager.isValidatorSignature( |
||||
localDomain, |
||||
root, |
||||
index, |
||||
signature, |
||||
); |
||||
expect(isValid).to.be.false; |
||||
}); |
||||
|
||||
it('Calculated domain hash matches Rust-produced domain hash', async () => { |
||||
// Compare Rust output in json file to solidity output (json file matches
|
||||
// hash for local domain of 1000)
|
||||
for (let testCase of domainHashCases) { |
||||
const { expectedDomainHash } = testCase; |
||||
const domainHash = await validatorManager.domainHash( |
||||
testCase.outboxDomain, |
||||
); |
||||
expect(domainHash).to.equal(expectedDomainHash); |
||||
} |
||||
}); |
||||
|
||||
describe('improper checkpoints', async () => { |
||||
let outbox: Outbox; |
||||
beforeEach(async () => { |
||||
const outboxFactory = new Outbox__factory(signer); |
||||
outbox = await outboxFactory.deploy(localDomain); |
||||
await outbox.initialize(validatorManager.address); |
||||
}); |
||||
|
||||
it('Accepts improper checkpoint from validator', async () => { |
||||
const root = ethers.utils.formatBytes32String('root'); |
||||
const index = 1; |
||||
|
||||
const { signature } = await validator.signCheckpoint(root, index); |
||||
// Send message with signer address as msg.sender
|
||||
await expect( |
||||
validatorManager.improperCheckpoint( |
||||
outbox.address, |
||||
root, |
||||
index, |
||||
signature, |
||||
), |
||||
) |
||||
.to.emit(validatorManager, 'ImproperCheckpoint') |
||||
.withArgs( |
||||
outbox.address, |
||||
localDomain, |
||||
validator.address, |
||||
root, |
||||
index, |
||||
signature, |
||||
); |
||||
expect(await outbox.state()).to.equal(types.AbacusState.FAILED); |
||||
}); |
||||
|
||||
it('Rejects improper checkpoint from non-validator', async () => { |
||||
const root = ethers.utils.formatBytes32String('root'); |
||||
const index = 1; |
||||
|
||||
const { signature } = await fakeValidator.signCheckpoint(root, index); |
||||
// Send message with signer address as msg.sender
|
||||
await expect( |
||||
validatorManager.improperCheckpoint( |
||||
outbox.address, |
||||
root, |
||||
index, |
||||
signature, |
||||
), |
||||
).to.be.revertedWith('!validator sig'); |
||||
}); |
||||
|
||||
it('Rejects proper checkpoint from validator', async () => { |
||||
const message = `0x${Buffer.alloc(10).toString('hex')}`; |
||||
await outbox.dispatch( |
||||
localDomain, |
||||
utils.addressToBytes32(signer.address), |
||||
message, |
||||
); |
||||
await outbox.checkpoint(); |
||||
const [root, index] = await outbox.latestCheckpoint(); |
||||
|
||||
const { signature } = await validator.signCheckpoint( |
||||
root, |
||||
index.toNumber(), |
||||
); |
||||
// Send message with signer address as msg.sender
|
||||
await expect( |
||||
validatorManager.improperCheckpoint( |
||||
outbox.address, |
||||
root, |
||||
index, |
||||
signature, |
||||
), |
||||
).to.be.revertedWith('!improper'); |
||||
}); |
||||
}); |
||||
}); |
Loading…
Reference in new issue